Browse Source

Merge pull request #11 from ente-io/collection-delete

handling collection deleted from remote
Abhinav-grd 4 years ago
parent
commit
5b17b2a322

+ 0 - 3
src/components/PhotoSwipe/PhotoSwipe.tsx

@@ -51,7 +51,6 @@ function PhotoSwipe(props: Iprops) {
     }, [isOpen]);
 
     function updateFavButton() {
-        console.log(this.currItem.id, props.favItemIds)
         setIsFav(isInFav(this?.currItem));
     }
 
@@ -113,14 +112,12 @@ function PhotoSwipe(props: Iprops) {
         if (!isInFav(file)) {
             favItemIds.add(file.id);
             await addToFavorites(file);
-            console.log("added to Favorites");
             setIsFav(true);
             setFavItemIds(favItemIds);
         }
         else {
             favItemIds.delete(file.id);
             await removeFromFavorites(file)
-            console.log("removed from Favorites");
             setIsFav(false);
             setFavItemIds(favItemIds);
 

+ 2 - 2
src/pages/gallery/components/CollectionDropZone.tsx

@@ -9,7 +9,7 @@ function CollectionDropZone({
     closeModal,
     showModal,
     refetchData,
-    collectionLatestFile,
+    collectionAndItsLatestFile,
     setProgressView,
     progressBarProps
 
@@ -21,7 +21,7 @@ function CollectionDropZone({
         progressBarProps.setPercentComplete(0);
         setProgressView(true);
 
-        await UploadService.uploadFiles(acceptedFiles, collectionLatestFile, token, progressBarProps);
+        await UploadService.uploadFiles(acceptedFiles, collectionAndItsLatestFile, token, progressBarProps);
         refetchData();
         setProgressView(false);
     }

+ 3 - 4
src/pages/gallery/components/CollectionSelector.tsx

@@ -10,17 +10,16 @@ function CollectionSelector(props) {
         uploadModalView,
         closeUploadModal,
         showUploadModal,
-        collectionLatestFile,
+        collectionAndItsLatestFile,
         ...rest
     } = props;
 
-
-    const CollectionIcons = collectionLatestFile?.map((item) => (
+    const CollectionIcons = collectionAndItsLatestFile?.map((item) => (
         <CollectionDropZone key={item.collection.id}
             {...rest}
             closeModal={closeUploadModal}
             showModal={showUploadModal}
-            collectionLatestFile={item}
+            collectionAndItsLatestFile={item}
         >
             <Card>
                 <PreviewCard data={item.file} updateUrl={() => { }} forcedEnable />

+ 2 - 2
src/pages/gallery/components/Collections.tsx

@@ -5,7 +5,7 @@ import styled from 'styled-components';
 interface CollectionProps {
     collections: collection[];
     selected?: string;
-    selectCollection: (id?: string) => void;
+    selectCollection: (id?: number) => void;
 }
 
 const Container = styled.div`
@@ -51,7 +51,7 @@ const Chip = styled.button<{ active: boolean }>`
 
 export default function Collections(props: CollectionProps) {
     const { selected, collections, selectCollection } = props;
-    const clickHandler = (id?: string) => () => selectCollection(id);
+    const clickHandler = (id?: number) => () => selectCollection(id);
 
     return <Container>
         <Wrapper>

+ 3 - 3
src/pages/gallery/components/CreateCollection.tsx

@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
 import { Button, Form, Modal } from 'react-bootstrap';
 import { createAlbum } from 'services/collectionService';
 import UploadService from 'services/uploadService';
-import { collectionLatestFile } from 'services/collectionService'
+import { CollectionAndItsLatestFile } from 'services/collectionService'
 import { getToken } from 'utils/common/key';
 
 export default function CreateCollection(props) {
@@ -35,12 +35,12 @@ export default function CreateCollection(props) {
 
         const collection = await createAlbum(albumName);
 
-        const collectionLatestFile: collectionLatestFile = { collection, file: null }
+        const collectionAndItsLatestFile: CollectionAndItsLatestFile = { collection, file: null }
 
         progressBarProps.setPercentComplete(0);
         setProgressView(true);
 
-        await UploadService.uploadFiles(acceptedFiles, collectionLatestFile, token, progressBarProps);
+        await UploadService.uploadFiles(acceptedFiles, collectionAndItsLatestFile, token, progressBarProps);
         refetchData();
         setProgressView(false);
     }

+ 24 - 19
src/pages/gallery/index.tsx

@@ -6,7 +6,7 @@ import {
     file,
     getFile,
     getPreview,
-    fetchData,
+    syncData,
     localFiles,
 } from 'services/fileService';
 import { getData, LS_KEYS } from 'utils/storage/localStorage';
@@ -22,9 +22,9 @@ import Collections from './components/Collections';
 import Upload from './components/Upload';
 import {
     collection,
-    fetchUpdatedCollections,
-    collectionLatestFile,
-    getCollectionLatestFile,
+    syncCollections,
+    CollectionAndItsLatestFile,
+    getCollectionAndItsLatestFile,
     getFavItemIds,
     getLocalCollections,
 } from 'services/collectionService';
@@ -108,9 +108,10 @@ export default function Gallery(props) {
     const router = useRouter();
     const [loading, setLoading] = useState(false);
     const [collections, setCollections] = useState<collection[]>([]);
-    const [collectionLatestFile, setCollectionLatestFile] = useState<
-        collectionLatestFile[]
-    >([]);
+    const [
+        collectionAndItsLatestFile,
+        setCollectionAndItsLatestFile,
+    ] = useState<CollectionAndItsLatestFile[]>([]);
     const [data, setData] = useState<file[]>();
     const [favItemIds, setFavItemIds] = useState<Set<number>>();
     const [open, setOpen] = useState(false);
@@ -132,8 +133,16 @@ export default function Gallery(props) {
             setLoading(true);
             const data = await localFiles();
             const collections = await getLocalCollections();
+            const collectionAndItsLatestFile = await getCollectionAndItsLatestFile(
+                collections,
+                data
+            );
             setData(data);
             setCollections(collections);
+            setCollectionAndItsLatestFile(collectionAndItsLatestFile);
+            const favItemIds = await getFavItemIds(data);
+            setFavItemIds(favItemIds);
+
             setLoading(false);
             setProgress(80);
             await syncWithRemote();
@@ -146,22 +155,18 @@ export default function Gallery(props) {
     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(
+        const collections = await syncCollections(token, encryptionKey);
+        const { data, isUpdated } = await syncData(token, collections);
+        const collectionAndItsLatestFile = await getCollectionAndItsLatestFile(
             collections,
             data
         );
         const favItemIds = await getFavItemIds(data);
-        if (updatedCollections.length > 0) {
-            setCollections(collections);
+        setCollections(collections);
+        if (isUpdated) {
             setData(data);
         }
-        setCollectionLatestFile(collectionLatestFile);
+        setCollectionAndItsLatestFile(collectionAndItsLatestFile);
         setFavItemIds(favItemIds);
         setSinceTime(new Date().getTime());
         props.setUploadButtonView(true);
@@ -295,7 +300,7 @@ export default function Gallery(props) {
         );
     }
 
-    const selectCollection = (id?: string) => {
+    const selectCollection = (id?: number) => {
         const href = `/gallery?collection=${id || ''}`;
         router.push(href, undefined, { shallow: true });
     };
@@ -344,7 +349,7 @@ export default function Gallery(props) {
                 uploadModalView={props.uploadModalView}
                 closeUploadModal={props.closeUploadModal}
                 showUploadModal={props.showUploadModal}
-                collectionLatestFile={collectionLatestFile}
+                collectionAndItsLatestFile={collectionAndItsLatestFile}
                 refetchData={syncWithRemote}
             />
             {filteredData.length ? (

+ 211 - 109
src/services/collectionService.ts

@@ -1,35 +1,37 @@
-import { getEndpoint } from "utils/common/apiUtil";
-import { getData, LS_KEYS } from "utils/storage/localStorage";
-import { file, user, getFiles } from "./fileService";
+import { getEndpoint } from 'utils/common/apiUtil';
+import { getData, LS_KEYS } from 'utils/storage/localStorage';
+import { file, user, getFiles } from './fileService';
 import localForage from 'localforage';
 
-import HTTPService from "./HTTPService";
+import HTTPService from './HTTPService';
 import * as Comlink from 'comlink';
-import { keyEncryptionResult } from "./uploadService";
-import { getActualKey, getToken } from "utils/common/key";
-
+import { keyEncryptionResult } from './uploadService';
+import { getActualKey, getToken } from 'utils/common/key';
 
 const CryptoWorker: any =
     typeof window !== 'undefined' &&
     Comlink.wrap(new Worker('worker/crypto.worker.js', { type: 'module' }));
 const ENDPOINT = getEndpoint();
 
-
 enum CollectionType {
-    folder = "folder",
-    favorites = "favorites",
-    album = "album",
+    folder = 'folder',
+    favorites = 'favorites',
+    album = 'album',
 }
 
+const COLLECTION_UPDATION_TIME = 'collection-updation-time';
+const FAV_COLLECTION = 'fav-collection';
+const COLLECTIONS = 'collections';
+
 export interface collection {
-    id: string;
+    id: number;
     owner: user;
     key?: string;
     name?: string;
     encryptedName?: string;
     nameDecryptionNonce?: string;
     type: string;
-    attributes: collectionAttributes
+    attributes: collectionAttributes;
     sharees: user[];
     updationTime: number;
     encryptedKey: string;
@@ -39,16 +41,18 @@ export interface collection {
 
 interface collectionAttributes {
     encryptedPath?: string;
-    pathDecryptionNonce?: string
-};
+    pathDecryptionNonce?: string;
+}
 
-export interface collectionLatestFile {
-    collection: collection
+export interface CollectionAndItsLatestFile {
+    collection: collection;
     file: file;
 }
 
-
-const getCollectionSecrets = async (collection: collection, masterKey: string) => {
+const getCollectionSecrets = async (
+    collection: collection,
+    masterKey: string
+) => {
     const worker = await new CryptoWorker();
     const userID = getData(LS_KEYS.USER).id;
     let decryptedKey: string;
@@ -58,7 +62,6 @@ const getCollectionSecrets = async (collection: collection, masterKey: string) =
             collection.keyDecryptionNonce,
             masterKey
         );
-
     } else {
         const keyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES);
         const secretKey = await worker.decryptB64(
@@ -72,10 +75,13 @@ const getCollectionSecrets = async (collection: collection, masterKey: string) =
             secretKey
         );
     }
-    collection.name = collection.name || await worker.decryptString(
-        collection.encryptedName,
-        collection.nameDecryptionNonce,
-        decryptedKey);
+    collection.name =
+        collection.name ||
+        (await worker.decryptString(
+            collection.encryptedName,
+            collection.nameDecryptionNonce,
+            decryptedKey
+        ));
     return {
         ...collection,
         key: decryptedKey,
@@ -88,85 +94,131 @@ const getCollections = async (
     key: string
 ): Promise<collection[]> => {
     try {
-        const resp = await HTTPService.get(`${ENDPOINT}/collections`, {
-            sinceTime: sinceTime,
-        }, { 'X-Auth-Token': token, });
+        const resp = await HTTPService.get(
+            `${ENDPOINT}/collections`,
+            {
+                sinceTime: sinceTime,
+            },
+            { 'X-Auth-Token': token }
+        );
         const promises: Promise<collection>[] = resp.data.collections.map(
             (collection: collection) => getCollectionSecrets(collection, key)
         );
         return await Promise.all(promises);
-    }
-    catch (e) {
-        console.log("getCollections failed- " + e);
+    } catch (e) {
+        console.log('getCollections failed- ' + e);
     }
 };
 
 export const getLocalCollections = async (): Promise<collection[]> => {
-    const collections = await localForage.getItem('collections') as collection[] ?? [];
+    const collections: collection[] =
+        (await localForage.getItem(COLLECTIONS)) ?? [];
     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);
+};
+
+export const syncCollections = async (token: string, key: string) => {
     const localCollections = await getLocalCollections();
-    const allCollectionsInstances = [...localCollections, ...updatedCollections];
-    var latestCollectionsInstances = new Map<string, collection>();
+    const lastCollectionUpdationTime =
+        (await localForage.getItem<string>(COLLECTION_UPDATION_TIME)) ?? '0';
+    const updatedCollections =
+        (await getCollections(token, lastCollectionUpdationTime, key)) || [];
+
+    if (updatedCollections.length == 0) {
+        return localCollections;
+    }
+    setLocalFavoriteCollection(updatedCollections);
+    const allCollectionsInstances = [
+        ...localCollections,
+        ...updatedCollections,
+    ];
+    var latestCollectionsInstances = new Map<number, collection>();
     allCollectionsInstances.forEach((collection) => {
-        if (!latestCollectionsInstances.has(collection.id) || latestCollectionsInstances.get(collection.id).updationTime < collection.updationTime) {
+        if (
+            !latestCollectionsInstances.has(collection.id) ||
+            latestCollectionsInstances.get(collection.id).updationTime <
+                collection.updationTime
+        ) {
             latestCollectionsInstances.set(collection.id, collection);
         }
     });
-    let collections = [];
+
+    let collections = [],
+        updationTime = await localForage.getItem<number>(
+            COLLECTION_UPDATION_TIME
+        );
     for (const [_, collection] of latestCollectionsInstances) {
-        collections.push(collection);
+        if (!collection.isDeleted) {
+            collections.push(collection);
+            updationTime = Math.max(updationTime, collection.updationTime);
+        }
     }
-    await localForage.setItem('fav-collection', favCollection);
-    await localForage.setItem('collections', collections);
-    return updatedCollections;
+    await localForage.setItem(COLLECTION_UPDATION_TIME, updationTime);
+    await localForage.setItem(COLLECTIONS, collections);
+    return collections;
 };
 
-export const getCollectionLatestFile = (
+export const getCollectionAndItsLatestFile = (
     collections: collection[],
     files: file[]
-): collectionLatestFile[] => {
+): CollectionAndItsLatestFile[] => {
     const latestFile = new Map<number, file>();
     const collectionMap = new Map<number, collection>();
 
-    collections.forEach(collection => collectionMap.set(Number(collection.id), collection));
-    files.forEach(file => {
+    collections.forEach((collection) =>
+        collectionMap.set(collection.id, collection)
+    );
+    files.forEach((file) => {
         if (!latestFile.has(file.collectionID)) {
-            latestFile.set(file.collectionID, file)
+            latestFile.set(file.collectionID, file);
         }
     });
-    let allCollectionLatestFile: collectionLatestFile[] = [];
+    let allCollectionAndItsLatestFile: CollectionAndItsLatestFile[] = [];
     for (const [collectionID, file] of latestFile) {
-        allCollectionLatestFile.push({ collection: collectionMap.get(collectionID), file });
+        allCollectionAndItsLatestFile.push({
+            collection: collectionMap.get(collectionID),
+            file,
+        });
     }
-    return allCollectionLatestFile;
-}
+    return allCollectionAndItsLatestFile;
+};
 
 export const getFavItemIds = async (files: file[]): Promise<Set<number>> => {
+    let favCollection = await localForage.getItem<collection>(FAV_COLLECTION);
+    if (!favCollection) return new Set();
 
-    let favCollection: collection = (await localForage.getItem<collection>('fav-collection'))[0];
-    if (!favCollection)
-        return new Set();
-
-    return new Set(files.filter(file => file.collectionID === Number(favCollection.id)).map((file): number => file.id));
-}
+    return new Set(
+        files
+            .filter((file) => file.collectionID === favCollection.id)
+            .map((file): number => file.id)
+    );
+};
 
 export const createAlbum = async (albumName: string) => {
     return AddCollection(albumName, CollectionType.album);
-}
-
+};
 
-export const AddCollection = async (collectionName: string, type: CollectionType) => {
+export const AddCollection = async (
+    collectionName: string,
+    type: CollectionType
+) => {
     const worker = await new CryptoWorker();
     const encryptionKey = await getActualKey();
     const token = getToken();
     const collectionKey: string = await worker.generateMasterKey();
-    const { encryptedData: encryptedKey, nonce: keyDecryptionNonce }: keyEncryptionResult = await worker.encryptToB64(collectionKey, encryptionKey);
-    const { encryptedData: encryptedName, nonce: nameDecryptionNonce }: keyEncryptionResult = await worker.encryptToB64(collectionName, collectionKey);
+    const {
+        encryptedData: encryptedKey,
+        nonce: keyDecryptionNonce,
+    }: keyEncryptionResult = await worker.encryptToB64(
+        collectionKey,
+        encryptionKey
+    );
+    const {
+        encryptedData: encryptedName,
+        nonce: nameDecryptionNonce,
+    }: keyEncryptionResult = await worker.encryptToB64(
+        collectionName,
+        collectionKey
+    );
     const newCollection: collection = {
         id: null,
         owner: null,
@@ -178,76 +230,126 @@ export const AddCollection = async (collectionName: string, type: CollectionType
         attributes: {},
         sharees: null,
         updationTime: null,
-        isDeleted: false
+        isDeleted: false,
     };
-    let createdCollection: collection = await createCollection(newCollection, token);
-    createdCollection = await getCollectionSecrets(createdCollection, encryptionKey);
+    let createdCollection: collection = await createCollection(
+        newCollection,
+        token
+    );
+    createdCollection = await getCollectionSecrets(
+        createdCollection,
+        encryptionKey
+    );
     return createdCollection;
-}
+};
 
-const createCollection = async (collectionData: collection, token: string): Promise<collection> => {
+const createCollection = async (
+    collectionData: collection,
+    token: string
+): Promise<collection> => {
     try {
-        const response = await HTTPService.post(`${ENDPOINT}/collections`, collectionData, null, { 'X-Auth-Token': token });
+        const response = await HTTPService.post(
+            `${ENDPOINT}/collections`,
+            collectionData,
+            null,
+            { 'X-Auth-Token': token }
+        );
         return response.data.collection;
     } catch (e) {
-        console.log("create Collection failed " + e);
+        console.log('create Collection failed ' + e);
     }
-}
+};
 
 export const addToFavorites = async (file: file) => {
-    let favCollection: collection = (await localForage.getItem<collection>('fav-collection'))[0];
+    let favCollection: collection = await localForage.getItem<collection>(
+        FAV_COLLECTION
+    );
     if (!favCollection) {
-        favCollection = await AddCollection("Favorites", CollectionType.favorites);
-        await localForage.setItem('fav-collection', favCollection);
+        favCollection = await AddCollection(
+            'Favorites',
+            CollectionType.favorites
+        );
+        await localForage.setItem(FAV_COLLECTION, favCollection);
     }
-    await addtoCollection(favCollection, [file])
-}
+    await addToCollection(favCollection, [file]);
+};
 
 export const removeFromFavorites = async (file: file) => {
-    let favCollection: collection = (await localForage.getItem<collection>('fav-collection'))[0];
-    await removeFromCollection(favCollection, [file])
-}
+    let favCollection: collection = await localForage.getItem<collection>(
+        FAV_COLLECTION
+    );
+    await removeFromCollection(favCollection, [file]);
+};
 
-const addtoCollection = async (collection: collection, files: file[]) => {
+const addToCollection = async (collection: collection, files: file[]) => {
     try {
         const params = new Object();
         const worker = await new CryptoWorker();
         const token = getToken();
-        params["collectionID"] = collection.id;
-        await Promise.all(files.map(async file => {
-            file.collectionID = Number(collection.id);
-            const newEncryptedKey: keyEncryptionResult = await worker.encryptToB64(file.key, collection.key);
-            file.encryptedKey = newEncryptedKey.encryptedData;
-            file.keyDecryptionNonce = newEncryptedKey.nonce;
-            if (params["files"] == undefined) {
-                params["files"] = [];
-            }
-            params["files"].push({
-                id: file.id,
-                encryptedKey: file.encryptedKey,
-                keyDecryptionNonce: file.keyDecryptionNonce
+        params['collectionID'] = collection.id;
+        await Promise.all(
+            files.map(async (file) => {
+                file.collectionID = collection.id;
+                const newEncryptedKey: keyEncryptionResult = await worker.encryptToB64(
+                    file.key,
+                    collection.key
+                );
+                file.encryptedKey = newEncryptedKey.encryptedData;
+                file.keyDecryptionNonce = newEncryptedKey.nonce;
+                if (params['files'] == undefined) {
+                    params['files'] = [];
+                }
+                params['files'].push({
+                    id: file.id,
+                    encryptedKey: file.encryptedKey,
+                    keyDecryptionNonce: file.keyDecryptionNonce,
+                });
+                return file;
             })
-            return file;
-        }));
-        await HTTPService.post(`${ENDPOINT}/collections/add-files`, params, null, { 'X-Auth-Token': token });
+        );
+        await HTTPService.post(
+            `${ENDPOINT}/collections/add-files`,
+            params,
+            null,
+            { 'X-Auth-Token': token }
+        );
     } catch (e) {
-        console.log("Add to collection Failed " + e);
+        console.log('Add to collection Failed ' + e);
     }
-}
+};
 const removeFromCollection = async (collection: collection, files: file[]) => {
     try {
         const params = new Object();
         const token = getToken();
-        params["collectionID"] = collection.id;
-        await Promise.all(files.map(async file => {
-            if (params["fileIDs"] == undefined) {
-                params["fileIDs"] = [];
-            }
-            params["fileIDs"].push(file.id);
-        }));
-        await HTTPService.post(`${ENDPOINT}/collections/remove-files`, params, null, { 'X-Auth-Token': token });
+        params['collectionID'] = collection.id;
+        await Promise.all(
+            files.map(async (file) => {
+                if (params['fileIDs'] == undefined) {
+                    params['fileIDs'] = [];
+                }
+                params['fileIDs'].push(file.id);
+            })
+        );
+        await HTTPService.post(
+            `${ENDPOINT}/collections/remove-files`,
+            params,
+            null,
+            { 'X-Auth-Token': token }
+        );
     } catch (e) {
-        console.log("remove from collection failed " + e);
+        console.log('remove from collection failed ' + e);
     }
-}
+};
 
+const setLocalFavoriteCollection = async (collections: collection[]) => {
+    const localFavCollection = await localForage.getItem(FAV_COLLECTION);
+    if (localFavCollection) {
+        return;
+    }
+    const favCollection = collections.filter(
+        (collection) => collection.type == CollectionType.favorites
+    );
+    if (favCollection.length > 0) {
+        await localForage.setItem(FAV_COLLECTION, favCollection[0]);
+    }
+};

+ 90 - 73
src/services/fileService.ts

@@ -16,6 +16,8 @@ localForage.config({
     storeName: 'files',
 });
 
+const FILES = 'files';
+
 export interface fileAttribute {
     encryptedData: Uint8Array | string;
     decryptionHeader: string;
@@ -29,7 +31,6 @@ export interface user {
     email: string;
 }
 
-
 export interface file {
     id: number;
     collectionID: number;
@@ -49,92 +50,95 @@ export interface file {
     updationTime: number;
 }
 
+export const syncData = async (token, collections) => {
+    const { files: resp, isUpdated } = await syncFiles(token, collections);
 
-
-export const fetchData = async (token, collections) => {
-    const resp = await fetchFiles(
-        token,
-        collections
-    );
-
-    return (
-        resp.map((item) => ({
+    return {
+        data: resp.map((item) => ({
             ...item,
             w: window.innerWidth,
             h: window.innerHeight,
-        }))
-    );
-}
+        })),
+        isUpdated,
+    };
+};
 
 export const localFiles = async () => {
-    let files: Array<file> = (await localForage.getItem<file[]>('files')) || [];
+    let files: Array<file> = (await localForage.getItem<file[]>(FILES)) || [];
     return files;
-}
+};
 
-export const fetchFiles = async (
-    token: string,
-    collections: collection[]
-) => {
+export const syncFiles = async (token: string, collections: collection[]) => {
     let files = await localFiles();
-    const collectionUpdationTime = new Map<string, string>();
-    let fetchedFiles = [];
+    let isUpdated = false;
+    files = await removeDeletedCollectionFiles(collections, files);
     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) => {
-        let uid = `${file.collectionID}-${file.id}`;
-        if (!latestFiles.has(uid) || latestFiles.get(uid).updationTime < file.updationTime) {
-            latestFiles.set(uid, file);
+        const lastSyncTime =
+            (await localForage.getItem<number>(`${collection.id}-time`)) ?? 0;
+        if (collection.updationTime === lastSyncTime) {
+            continue;
         }
-    });
-    files = [];
-    for (const [_, file] of latestFiles) {
-        if (!file.isDeleted)
+        isUpdated = true;
+        let fetchedFiles =
+            (await getFiles(collection, lastSyncTime, 100, token)) ?? [];
+        files.push(...fetchedFiles);
+        var latestVersionFiles = new Map<number, file>();
+        files.forEach((file) => {
+            if (
+                !latestVersionFiles.has(file.id) ||
+                latestVersionFiles.get(file.id).updationTime < file.updationTime
+            ) {
+                latestVersionFiles.set(file.id, file);
+            }
+        });
+        files = [];
+        for (const [_, file] of latestVersionFiles) {
+            if (file.isDeleted) {
+                continue;
+            }
             files.push(file);
+        }
+        files = files.sort(
+            (a, b) => b.metadata.creationTime - a.metadata.creationTime
+        );
+        await localForage.setItem('files', files);
+        await localForage.setItem(
+            `${collection.id}-time`,
+            collection.updationTime
+        );
     }
-    files = files.sort(
-        (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;
+    return { files, isUpdated };
 };
 
-export const getFiles = async (collection: collection, sinceTime: string, limit: number, token: string): Promise<file[]> => {
+export const getFiles = async (
+    collection: collection,
+    sinceTime: number,
+    limit: number,
+    token: string
+): Promise<file[]> => {
     try {
         const worker = await new CryptoWorker();
         let promises: Promise<file>[] = [];
-        if (collection.isDeleted) {
-            // TODO: Remove files in this collection from localForage and cache
-            return;
-        }
         let time =
-            sinceTime || (await localForage.getItem<string>(`${collection.id}-time`)) || "0";
+            sinceTime ||
+            (await localForage.getItem<number>(`${collection.id}-time`)) ||
+            0;
         let resp;
         do {
-            resp = await HTTPService.get(`${ENDPOINT}/collections/diff`, {
-                collectionID: collection.id,
-                sinceTime: time,
-                limit: limit.toString(),
-            },
+            resp = await HTTPService.get(
+                `${ENDPOINT}/collections/diff`,
+                {
+                    collectionID: collection.id.toString(),
+                    sinceTime: time.toString(),
+                    limit: limit.toString(),
+                },
                 {
-                    'X-Auth-Token': token
-                });
-            promises.push(...resp.data.diff.map(
-                async (file: file) => {
+                    '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,
@@ -143,8 +147,8 @@ export const getFiles = async (collection: collection, sinceTime: string, limit:
                         file.metadata = await worker.decryptMetadata(file);
                     }
                     return file;
-                }
-            ));
+                })
+            );
 
             if (resp.data.diff.length) {
                 time = resp.data.diff.slice(-1)[0].updationTime.toString();
@@ -152,9 +156,9 @@ export const getFiles = async (collection: collection, sinceTime: string, limit:
         } while (resp.data.diff.length === limit);
         return await Promise.all(promises);
     } catch (e) {
-        console.log("Get files failed" + e);
+        console.log('Get files failed' + e);
     }
-}
+};
 export const getPreview = async (token: string, file: file) => {
     try {
         const cache = await caches.open('thumbs');
@@ -175,13 +179,16 @@ export const getPreview = async (token: string, file: file) => {
             file.key
         );
         try {
-            await cache.put(file.id.toString(), new Response(new Blob([decrypted])));
+            await cache.put(
+                file.id.toString(),
+                new Response(new Blob([decrypted]))
+            );
         } catch (e) {
             // TODO: handle storage full exception.
         }
         return URL.createObjectURL(new Blob([decrypted]));
     } catch (e) {
-        console.log("get preview Failed" + e);
+        console.log('get preview Failed' + e);
     }
 };
 
@@ -200,9 +207,19 @@ export const getFile = async (token: string, file: file) => {
             file.key
         );
         return URL.createObjectURL(new Blob([decrypted]));
-    }
-    catch (e) {
-        console.log("get file failed " + e);
+    } catch (e) {
+        console.log('get file failed ' + e);
     }
 };
 
+const removeDeletedCollectionFiles = async (
+    collections: collection[],
+    files: file[]
+) => {
+    const syncedCollectionIds = new Set<number>();
+    for (let collection of collections) {
+        syncedCollectionIds.add(collection.id);
+    }
+    files = files.filter((file) => syncedCollectionIds.has(file.collectionID));
+    return files;
+};

+ 4 - 5
src/services/uploadService.ts

@@ -3,7 +3,7 @@ import HTTPService from './HTTPService';
 import * as Comlink from 'comlink';
 import EXIF from "exif-js";
 import { fileAttribute } from './fileService';
-import { collection, collectionLatestFile } from "./collectionService"
+import { collection, CollectionAndItsLatestFile } from "./collectionService"
 import { FILE_TYPE } from 'pages/gallery';
 const CryptoWorker: any =
     typeof window !== 'undefined' &&
@@ -49,7 +49,7 @@ interface objectKeys {
 }
 
 interface uploadFile extends objectKeys {
-    collectionID: string,
+    collectionID: number,
     encryptedKey: string;
     keyDecryptionNonce: string;
     metadata?: {
@@ -80,7 +80,7 @@ class UploadService {
     private totalFilesCount: number
     private metadataMap: Map<string, Object>;
 
-    public async uploadFiles(recievedFiles: File[], collectionLatestFile: collectionLatestFile, token: string, progressBarProps) {
+    public async uploadFiles(recievedFiles: File[], collectionAndItsLatestFile: CollectionAndItsLatestFile, token: string, progressBarProps) {
         try {
             const worker = await new CryptoWorker();
             this.stepsCompleted = 0;
@@ -107,7 +107,7 @@ class UploadService {
             while (actualFiles.length > 0) {
                 var promises = [];
                 for (var i = 0; i < 5 && actualFiles.length > 0; i++)
-                    promises.push(this.uploadHelper(progressBarProps, actualFiles.pop(), collectionLatestFile.collection, token));
+                    promises.push(this.uploadHelper(progressBarProps, actualFiles.pop(), collectionAndItsLatestFile.collection, token));
                 uploadFilesWithoutMetaData.push(...await Promise.all(promises));
             }
 
@@ -364,7 +364,6 @@ class UploadService {
                     resolve(blob);
                 }), 'image/jpeg', 0.4
             });
-            console.log(URL.createObjectURL(thumbnailBlob));
             const thumbnail = this.getUint8ArrayView(thumbnailBlob);
             return thumbnail;
         } catch (e) {

+ 0 - 1
src/utils/common/apiUtil.ts

@@ -1,5 +1,4 @@
 export const getEndpoint = () => {
     const endPoint = process.env.NEXT_PUBLIC_ENTE_ENDPOINT ?? "https://api.ente.io";
-    console.log(endPoint);
     return endPoint;
 }