Initial gallary chagnes.
This commit is contained in:
parent
8e76f6b5bd
commit
f9ee680c8f
10 changed files with 158 additions and 30 deletions
|
@ -18,6 +18,7 @@
|
|||
"react": "16.13.1",
|
||||
"react-bootstrap": "^1.3.0",
|
||||
"react-dom": "16.13.1",
|
||||
"react-photoswipe": "^1.3.0",
|
||||
"scrypt-js": "^3.0.1",
|
||||
"styled-components": "^5.2.0",
|
||||
"yup": "^0.29.3"
|
||||
|
|
1
public/image.svg
Normal file
1
public/image.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="48px" height="48px"><path d="M0 0h24v24H0z" fill="none"/><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>
|
After Width: | Height: | Size: 275 B |
|
@ -2,7 +2,6 @@ import React, { useEffect, useState } from 'react';
|
|||
import styled, {createGlobalStyle } from 'styled-components';
|
||||
import Navbar from 'components/Navbar';
|
||||
import constants from 'utils/strings/constants';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import Spinner from 'react-bootstrap/Spinner';
|
||||
import { clearKeys } from 'utils/storage/sessionStorage';
|
||||
|
@ -11,6 +10,8 @@ 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 'react-photoswipe/lib/photoswipe.css';
|
||||
|
||||
const GlobalStyles = createGlobalStyle`
|
||||
html, body {
|
||||
|
|
|
@ -1,18 +1,46 @@
|
|||
import React from 'react';
|
||||
import Card from 'react-bootstrap/Card';
|
||||
import { fileData } from 'services/fileService';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { fileData, getPreview } from 'services/fileService';
|
||||
import { getActualKey } from 'utils/common/key';
|
||||
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||
import styled from 'styled-components';
|
||||
|
||||
interface IProps {
|
||||
data: fileData,
|
||||
}
|
||||
|
||||
const Cont = styled.div`
|
||||
background: #555 url(/image.svg) no-repeat center;
|
||||
margin: 0 4px;
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
overflow: hidden;
|
||||
|
||||
& > img {
|
||||
object-fit: cover;
|
||||
max-width: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function PreviewCard(props: IProps) {
|
||||
const [imgSrc, setImgSrc] = useState<string>();
|
||||
const { data } = props;
|
||||
|
||||
return (<Card>
|
||||
<Card.Body>
|
||||
<div>ID: {data?.id}</div>
|
||||
<div>MetaData: {JSON.stringify(data?.metadata)}</div>
|
||||
</Card.Body>
|
||||
</Card>);
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
const main = async () => {
|
||||
const token = getData(LS_KEYS.USER).token;
|
||||
const key = await getActualKey();
|
||||
const url = await getPreview(token, data, key);
|
||||
setImgSrc(url);
|
||||
data.src = url;
|
||||
}
|
||||
main();
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
return <Cont>
|
||||
<img src={imgSrc}/>
|
||||
</Cont>;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useLayoutEffect, useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import Spinner from 'react-bootstrap/Spinner';
|
||||
import { getKey, SESSION_KEYS } from 'utils/storage/sessionStorage';
|
||||
|
@ -6,13 +6,27 @@ import { fileData, getFiles } from 'services/fileService';
|
|||
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||
import PreviewCard from './components/PreviewCard';
|
||||
import { getActualKey } from 'utils/common/key';
|
||||
import styled from 'styled-components';
|
||||
import { PhotoSwipeGallery } from 'react-photoswipe';
|
||||
|
||||
const Container = styled.div`
|
||||
max-width: 1260px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 auto;
|
||||
|
||||
.pswp-thumbnail {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Gallery() {
|
||||
const router = useRouter();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState<fileData[]>();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
const key = getKey(SESSION_KEYS.ENCRYPTION_KEY);
|
||||
const token = getData(LS_KEYS.USER).token;
|
||||
if (!key) {
|
||||
|
@ -21,7 +35,7 @@ export default function Gallery() {
|
|||
const main = async () => {
|
||||
setLoading(true);
|
||||
const encryptionKey = await getActualKey();
|
||||
const resp = await getFiles("0", token, "100", encryptionKey);
|
||||
const resp = await getFiles("0", token, "24", encryptionKey);
|
||||
setLoading(false);
|
||||
setData(resp);
|
||||
};
|
||||
|
@ -29,8 +43,24 @@ export default function Gallery() {
|
|||
}, []);
|
||||
|
||||
if (!data || loading) {
|
||||
return <Spinner animation="border" variant="primary"/>;
|
||||
return <div className="text-center">
|
||||
<Spinner animation="border" variant="primary"/>;
|
||||
</div>
|
||||
}
|
||||
|
||||
return (data || []).map(item => <PreviewCard key={item.id} data={item} />);
|
||||
const getThumbnail = (item) => (
|
||||
<PreviewCard data={item}/>
|
||||
)
|
||||
|
||||
return (<Container>
|
||||
<PhotoSwipeGallery
|
||||
items={data.map(item => ({
|
||||
...item,
|
||||
src: '/image.svg',
|
||||
w: 512,
|
||||
h: 512,
|
||||
}))}
|
||||
thumbnailContent={getThumbnail}
|
||||
/>
|
||||
</Container>);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import aescrypt from "utils/aescrypt";
|
||||
import { getEndpoint } from "utils/common/apiUtil";
|
||||
import { decrypt } from "utils/crypto/aes";
|
||||
import { strToUint8 } from "utils/crypto/common";
|
||||
import HTTPService from "./HTTPService";
|
||||
|
||||
const ENDPOINT = getEndpoint();
|
||||
|
@ -6,28 +9,56 @@ const ENDPOINT = getEndpoint();
|
|||
export interface fileData {
|
||||
id: number;
|
||||
metadata: {
|
||||
currentTimestamp: number,
|
||||
},
|
||||
currentTime: number;
|
||||
modificationTime: number;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
title: string;
|
||||
deviceFolder: string;
|
||||
};
|
||||
encryptedPassword: string;
|
||||
encryptedPasswordIV: string;
|
||||
file?: string;
|
||||
};
|
||||
|
||||
const getFileDataUsingWorker = (data: any, key: string) => {
|
||||
return new Promise((resolve) => {
|
||||
const worker = new Worker('worker/decrypt.worker.js', { type: 'module' });
|
||||
const worker = new Worker('worker/decryptMetadata.worker.js', { type: 'module' });
|
||||
const onWorkerMessage = (event) => resolve(event.data);
|
||||
worker.addEventListener('message', onWorkerMessage);
|
||||
worker.postMessage({ data, key });
|
||||
});
|
||||
}
|
||||
|
||||
export const getFiles = async (sinceTimestamp: string, token: string, limit: string, key: string) => {
|
||||
const getFileUsingWorker = (data: any, key: string) => {
|
||||
return new Promise((resolve) => {
|
||||
const worker = new Worker('worker/decryptFile.worker.js', { type: 'module' });
|
||||
const onWorkerMessage = (event) => resolve(event.data);
|
||||
worker.addEventListener('message', onWorkerMessage);
|
||||
worker.postMessage({ data, key });
|
||||
});
|
||||
}
|
||||
|
||||
export const getFiles = async (sinceTime: string, token: string, limit: string, key: string) => {
|
||||
const resp = await HTTPService.get(`${ENDPOINT}/encrypted-files/diff`, {
|
||||
sinceTimestamp, token, limit,
|
||||
sinceTime, token, limit,
|
||||
});
|
||||
|
||||
const promises: Promise<fileData>[] = resp.data.diff.map((data) => getFileDataUsingWorker(data, key));
|
||||
console.time('Metadata Parsing');
|
||||
const decrypted = await Promise.all(promises);
|
||||
console.timeEnd('Metadata Parsing');
|
||||
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
export const getPreview = async (token: string, data: fileData, key: string) => {
|
||||
const resp = await HTTPService.get(
|
||||
`${ENDPOINT}/encrypted-files/preview/${data.id}`,
|
||||
{ token }, null, { responseType: 'arraybuffer' },
|
||||
);
|
||||
const decrypted = await getFileUsingWorker({
|
||||
...data,
|
||||
file: resp.data,
|
||||
}, key);
|
||||
const url = URL.createObjectURL(new Blob([decrypted.file]));
|
||||
return url;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { resolve } from 'path';
|
||||
import { aescrypt } from './aescrypt';
|
||||
|
||||
const decrypt = (file: Uint8Array, password: String, binaryResponse: Boolean = false) => {
|
||||
|
@ -6,6 +5,7 @@ const decrypt = (file: Uint8Array, password: String, binaryResponse: Boolean = f
|
|||
try {
|
||||
aescrypt.decrypt(file, password, !binaryResponse, ({ data, error}) => {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
reject(error);
|
||||
}
|
||||
resolve(data);
|
||||
|
|
19
src/worker/decryptFile.worker.js
Normal file
19
src/worker/decryptFile.worker.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { decrypt } from "utils/crypto/aes";
|
||||
import { strToUint8 } from "utils/crypto/common";
|
||||
import aescrypt from 'utils/aescrypt';
|
||||
|
||||
function decryptFile(event) {
|
||||
const main = async () => {
|
||||
const data = event.data.data;
|
||||
const key = event.data.key;
|
||||
const password = await decrypt(data.encryptedPassword, key, data.encryptedPasswordIV);
|
||||
const file = await aescrypt.decrypt(data.file, atob(password), true);
|
||||
self.postMessage({
|
||||
id: data.id,
|
||||
file: file,
|
||||
});
|
||||
}
|
||||
main();
|
||||
}
|
||||
|
||||
self.addEventListener('message', decryptFile);
|
|
@ -9,11 +9,8 @@ function decryptFile(event) {
|
|||
const password = await decrypt(data.encryptedPassword, key, data.encryptedPasswordIV);
|
||||
const metadata = await aescrypt.decrypt(base64ToUint8(data.encryptedMetadata), atob(password));
|
||||
self.postMessage({
|
||||
id: data.id,
|
||||
ownerId: data.ownerId,
|
||||
updationTime: data.updationTime,
|
||||
password,
|
||||
metadata: JSON.parse(metadata),
|
||||
...data,
|
||||
metadata: JSON.parse(metadata)
|
||||
});
|
||||
}
|
||||
main();
|
24
yarn.lock
24
yarn.lock
|
@ -2020,7 +2020,7 @@ class-utils@^0.3.5:
|
|||
isobject "^3.0.0"
|
||||
static-extend "^0.1.1"
|
||||
|
||||
classnames@2.2.6, classnames@^2.2.6:
|
||||
classnames@2.2.6, classnames@^2.2.3, classnames@^2.2.6:
|
||||
version "2.2.6"
|
||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
|
||||
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
|
||||
|
@ -3502,6 +3502,11 @@ lodash-es@^4.17.11, lodash-es@^4.17.14, lodash-es@^4.17.15:
|
|||
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
|
||||
integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==
|
||||
|
||||
lodash.pick@^4.2.1:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
|
||||
integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=
|
||||
|
||||
lodash.sortby@^4.7.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||
|
@ -4232,6 +4237,11 @@ pbkdf2@^3.0.3:
|
|||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
photoswipe@^4.1.0:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/photoswipe/-/photoswipe-4.1.3.tgz#59f49494eeb9ddab5888d03392926a19bc197550"
|
||||
integrity sha512-89Z43IRUyw7ycTolo+AaiDn3W1EEIfox54hERmm9bI12IB9cvRfHSHez3XhAyU8XW2EAFrC+2sKMhh7SJwn0bA==
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
||||
|
@ -4368,7 +4378,7 @@ prop-types-extra@^1.1.0:
|
|||
react-is "^16.3.2"
|
||||
warning "^4.0.0"
|
||||
|
||||
prop-types@15.7.2, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
prop-types@15.7.2, prop-types@^15.5.10, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
version "15.7.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
|
@ -4537,6 +4547,16 @@ react-overlays@^4.1.0:
|
|||
uncontrollable "^7.0.0"
|
||||
warning "^4.0.3"
|
||||
|
||||
react-photoswipe@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-photoswipe/-/react-photoswipe-1.3.0.tgz#016dd978450a8406776db97511eaf96f2ffb9cfb"
|
||||
integrity sha512-1ok6vXFAj/rd60KIzF0YwCdq1Tcl+8yKqWJHbPo43lJBuwUi+LBosmBdJmswpiOzMn2496ekU0k/r6aHWQk7PQ==
|
||||
dependencies:
|
||||
classnames "^2.2.3"
|
||||
lodash.pick "^4.2.1"
|
||||
photoswipe "^4.1.0"
|
||||
prop-types "^15.5.10"
|
||||
|
||||
react-refresh@0.8.3:
|
||||
version "0.8.3"
|
||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
|
||||
|
|
Loading…
Add table
Reference in a new issue