[Abhinav] | Integrated FileUpload with gallery component

This commit is contained in:
Abhinav-grd 2021-01-05 08:10:59 +05:30
parent 82729532fe
commit d71d89bada

View file

@ -2,7 +2,14 @@ import React, { useEffect, useState } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import Spinner from 'react-bootstrap/Spinner'; import Spinner from 'react-bootstrap/Spinner';
import { getKey, SESSION_KEYS } from 'utils/storage/sessionStorage'; import { getKey, SESSION_KEYS } from 'utils/storage/sessionStorage';
import { collection, fetchCollections, file, getFile, getFiles, getPreview } from 'services/fileService'; import {
collection,
fetchCollections,
file,
getFile,
getFiles,
getPreview,
} from 'services/fileService';
import { getData, LS_KEYS } from 'utils/storage/localStorage'; import { getData, LS_KEYS } from 'utils/storage/localStorage';
import PreviewCard from './components/PreviewCard'; import PreviewCard from './components/PreviewCard';
import { getActualKey } from 'utils/common/key'; import { getActualKey } from 'utils/common/key';
@ -13,121 +20,132 @@ import AutoSizer from 'react-virtualized-auto-sizer';
import { VariableSizeList as List } from 'react-window'; import { VariableSizeList as List } from 'react-window';
import Collections from './components/Collections'; import Collections from './components/Collections';
import SadFace from 'components/SadFace'; import SadFace from 'components/SadFace';
import FileUpload from './components/DragAndDropUpload';
enum ITEM_TYPE { enum ITEM_TYPE {
TIME='TIME', TIME = 'TIME',
TILE='TILE' TILE = 'TILE',
} }
interface TimeStampListItem { interface TimeStampListItem {
itemType: ITEM_TYPE, itemType: ITEM_TYPE;
items?: file[], items?: file[];
itemStartIndex?: number, itemStartIndex?: number;
date?: string, date?: string;
} }
const Container = styled.div` const Container = styled.div`
display: block; display: block;
flex: 1; flex: 1;
width: 100%; width: 100%;
flex-wrap: wrap; flex-wrap: wrap;
margin: 0 auto; margin: 0 auto;
.pswp-thumbnail { .pswp-thumbnail {
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
} }
`; `;
const ListItem = styled.div` const ListItem = styled.div`
display: flex; display: flex;
justify-content: center; justify-content: center;
`; `;
const DeadCenter = styled.div` const DeadCenter = styled.div`
flex: 1; flex: 1;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
color: #fff; color: #fff;
text-align: center; text-align: center;
flex-direction: column; flex-direction: column;
`; `;
const ListContainer = styled.div` const ListContainer = styled.div`
display: flex; display: flex;
max-width: 100%; max-width: 100%;
color: #fff; color: #fff;
@media (min-width: 1000px) { @media (min-width: 1000px) {
width: 1000px; width: 1000px;
} }
@media (min-width: 450px) and (max-width: 1000px) { @media (min-width: 450px) and (max-width: 1000px) {
width: 600px; width: 600px;
} }
@media (max-width: 450px) { @media (max-width: 450px) {
width: 100%; width: 100%;
} }
`; `;
const DateContainer = styled.div` const DateContainer = styled.div`
padding: 0 4px; padding: 0 4px;
`; `;
const PAGE_SIZE = 12; const PAGE_SIZE = 12;
const COLUMNS = 3; const COLUMNS = 3;
export default function Gallery() { export default function Gallery() {
const router = useRouter(); const router = useRouter();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [collections, setCollections] = useState<collection[]>([]) const [collections, setCollections] = useState<collection[]>([]);
const [data, setData] = useState<file[]>(); const [data, setData] = useState<file[]>();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [options, setOptions] = useState<Options>({ const [options, setOptions] = useState<Options>({
history: false, history: false,
maxSpreadZoom: 5, maxSpreadZoom: 5,
}); });
const fetching: { [k: number]: boolean } = {}; const fetching: { [k: number]: boolean } = {};
useEffect(() => { useEffect(() => {
const key = getKey(SESSION_KEYS.ENCRYPTION_KEY); const key = getKey(SESSION_KEYS.ENCRYPTION_KEY);
const token = getData(LS_KEYS.USER).token; const token = getData(LS_KEYS.USER).token;
if (!key) { if (!key) {
router.push("/"); router.push('/');
}
const main = async () => {
setLoading(true);
const encryptionKey = await getActualKey();
const collections = await fetchCollections(token, encryptionKey);
const resp = await getFiles("0", token, "100", encryptionKey, collections);
setLoading(false);
setCollections(collections);
setData(resp.map(item => ({
...item,
w: window.innerWidth,
h: window.innerHeight,
})));
};
main();
}, []);
if (!data || loading) {
return <div className="text-center">
<Spinner animation="border" variant="primary" />
</div>
} }
const main = async () => {
setLoading(true);
const encryptionKey = await getActualKey();
const collections = await fetchCollections(token, encryptionKey);
const resp = await getFiles(
'0',
token,
'100',
encryptionKey,
collections
);
setLoading(false);
setCollections(collections);
setData(
resp.map((item) => ({
...item,
w: window.innerWidth,
h: window.innerHeight,
}))
);
};
main();
}, []);
const updateUrl = (index: number) => (url: string) => { if (!data || loading) {
data[index] = { return (
...data[index], <div className='text-center'>
msrc: url, <Spinner animation='border' variant='primary' />
w: window.innerWidth, </div>
h: window.innerHeight, );
} }
if (data[index].metadata.fileType === 1 && !data[index].html) {
data[index].html = ` const updateUrl = (index: number) => (url: string) => {
data[index] = {
...data[index],
msrc: url,
w: window.innerWidth,
h: window.innerHeight,
};
if (data[index].metadata.fileType === 1 && !data[index].html) {
data[index].html = `
<div class="video-loading"> <div class="video-loading">
<img src="${url}" /> <img src="${url}" />
<div class="spinner-border text-light" role="status"> <div class="spinner-border text-light" role="status">
@ -135,220 +153,250 @@ export default function Gallery() {
</div> </div>
</div> </div>
`; `;
delete data[index].src; delete data[index].src;
}
if (data[index].metadata.fileType === 0 && !data[index].src) {
data[index].src = url;
}
setData(data);
} }
if (data[index].metadata.fileType === 0 && !data[index].src) {
data[index].src = url;
}
setData(data);
};
const updateSrcUrl = (index: number, url: string) => { const updateSrcUrl = (index: number, url: string) => {
data[index] = { data[index] = {
...data[index], ...data[index],
src: url, src: url,
w: window.innerWidth, w: window.innerWidth,
h: window.innerHeight, h: window.innerHeight,
} };
if (data[index].metadata.fileType === 1) { if (data[index].metadata.fileType === 1) {
data[index].html = ` data[index].html = `
<video controls> <video controls>
<source src="${url}" /> <source src="${url}" />
Your browser does not support the video tag. Your browser does not support the video tag.
</video> </video>
`; `;
delete data[index].src; delete data[index].src;
}
setData(data);
} }
setData(data);
};
const handleClose = () => { const handleClose = () => {
setOpen(false); setOpen(false);
};
const onThumbnailClick = (index: number) => () => {
setOptions({
...options,
index,
});
setOpen(true);
};
const getThumbnail = (file: file[], index: number) => {
return (
<PreviewCard
key={`tile-${file[index].id}`}
data={file[index]}
updateUrl={updateUrl(file[index].dataIndex)}
onClick={onThumbnailClick(index)}
/>
);
};
const getSlideData = async (instance: any, index: number, item: file) => {
const token = getData(LS_KEYS.USER).token;
if (!item.msrc) {
const url = await getPreview(token, item);
updateUrl(item.dataIndex)(url);
item.msrc = url;
if (!item.src) {
item.src = url;
}
item.w = window.innerWidth;
item.h = window.innerHeight;
try {
instance.invalidateCurrItems();
instance.updateSize(true);
} catch (e) {
// ignore
}
} }
if ((!item.src || item.src === item.msrc) && !fetching[item.dataIndex]) {
const onThumbnailClick = (index: number) => () => { fetching[item.dataIndex] = true;
setOptions({ const url = await getFile(token, item);
...options, updateSrcUrl(item.dataIndex, url);
index, if (item.metadata.fileType === 1) {
}); item.html = `
setOpen(true);
}
const getThumbnail = (file: file[], index: number) => {
return (<PreviewCard
key={`tile-${file[index].id}`}
data={file[index]}
updateUrl={updateUrl(file[index].dataIndex)}
onClick={onThumbnailClick(index)}
/>);
}
const getSlideData = async (instance: any, index: number, item: file) => {
const token = getData(LS_KEYS.USER).token;
if (!item.msrc) {
const url = await getPreview(token, item);
updateUrl(item.dataIndex)(url);
item.msrc = url;
if (!item.src) {
item.src = url;
}
item.w = window.innerWidth;
item.h = window.innerHeight;
try {
instance.invalidateCurrItems();
instance.updateSize(true);
} catch (e) {
// ignore
}
}
if ((!item.src || item.src === item.msrc) && !fetching[item.dataIndex]) {
fetching[item.dataIndex] = true;
const url = await getFile(token, item);
updateSrcUrl(item.dataIndex, url);
if (item.metadata.fileType === 1) {
item.html = `
<video width="320" height="240" controls> <video width="320" height="240" controls>
<source src="${url}" /> <source src="${url}" />
Your browser does not support the video tag. Your browser does not support the video tag.
</video> </video>
`; `;
delete item.src; delete item.src;
item.w = window.innerWidth; item.w = window.innerWidth;
} else { } else {
item.src = url; item.src = url;
} }
item.h = window.innerHeight; item.h = window.innerHeight;
try { try {
instance.invalidateCurrItems(); instance.invalidateCurrItems();
instance.updateSize(true); instance.updateSize(true);
} catch (e) { } catch (e) {
// ignore // ignore
} }
}
} }
};
const selectCollection = (id?: string) => { const selectCollection = (id?: string) => {
const href = `/gallery?collection=${id || ''}`; const href = `/gallery?collection=${id || ''}`;
router.push(href, undefined, { shallow: true }); router.push(href, undefined, { shallow: true });
} };
const idSet = new Set(); const idSet = new Set();
const filteredData = data.map((item, index) => ({ const filteredData = data
...item, .map((item, index) => ({
dataIndex: index, ...item,
})).filter(item => { dataIndex: index,
if (!idSet.has(item.id)) { }))
if (!router.query.collection || router.query.collection === item.collectionID.toString()) { .filter((item) => {
idSet.add(item.id); if (!idSet.has(item.id)) {
return true; if (
} !router.query.collection ||
return false; router.query.collection === item.collectionID.toString()
) {
idSet.add(item.id);
return true;
} }
return false; return false;
}
return false;
}); });
const isSameDay = (first, second) => { const isSameDay = (first, second) => {
return first.getFullYear() === second.getFullYear() && return (
first.getMonth() === second.getMonth() && first.getFullYear() === second.getFullYear() &&
first.getDate() === second.getDate(); first.getMonth() === second.getMonth() &&
} first.getDate() === second.getDate()
);
};
return (<> return (
<Collections <>
collections={collections} <Collections
selected={router.query.collection?.toString()} collections={collections}
selectCollection={selectCollection} selected={router.query.collection?.toString()}
/> selectCollection={selectCollection}
{ />
filteredData.length <FileUpload>
? <Container> {filteredData.length ? (
<AutoSizer> <Container>
{({ height, width }) => { <AutoSizer>
let columns; {({ height, width }) => {
if (width >= 1000) { let columns;
columns = 5; if (width >= 1000) {
} else if (width < 1000 && width >= 450) { columns = 5;
columns = 3; } else if (width < 1000 && width >= 450) {
} else if (width < 450 && width >= 300) { columns = 3;
columns = 2; } else if (width < 450 && width >= 300) {
} else { columns = 2;
columns = 1; } else {
} columns = 1;
}
const timeStampList: TimeStampListItem[] = []; const timeStampList: TimeStampListItem[] = [];
let listItemIndex = 0; let listItemIndex = 0;
let currentDate = -1; let currentDate = -1;
filteredData.forEach((item, index) => { filteredData.forEach((item, index) => {
if (!isSameDay(new Date(item.metadata.creationTime/1000), new Date(currentDate))) { if (
currentDate = item.metadata.creationTime/1000; !isSameDay(
const dateTimeFormat = new Intl.DateTimeFormat('en-IN', { new Date(item.metadata.creationTime / 1000),
weekday: 'short', new Date(currentDate)
year: 'numeric', )
month: 'long', ) {
day: 'numeric' currentDate = item.metadata.creationTime / 1000;
}); const dateTimeFormat = new Intl.DateTimeFormat('en-IN', {
timeStampList.push({ weekday: 'short',
itemType: ITEM_TYPE.TIME, year: 'numeric',
date: dateTimeFormat.format(currentDate), month: 'long',
}); day: 'numeric',
timeStampList.push({ });
itemType: ITEM_TYPE.TILE, timeStampList.push({
items: [item], itemType: ITEM_TYPE.TIME,
itemStartIndex: index, date: dateTimeFormat.format(currentDate),
}); });
listItemIndex = 1; timeStampList.push({
} else { itemType: ITEM_TYPE.TILE,
if (listItemIndex < columns) { items: [item],
timeStampList[timeStampList.length - 1].items.push(item); itemStartIndex: index,
listItemIndex++; });
} else { listItemIndex = 1;
listItemIndex = 1; } else {
timeStampList.push({ if (listItemIndex < columns) {
itemType: ITEM_TYPE.TILE, timeStampList[timeStampList.length - 1].items.push(item);
items: [item], listItemIndex++;
itemStartIndex: index, } else {
}) listItemIndex = 1;
} timeStampList.push({
} itemType: ITEM_TYPE.TILE,
}); items: [item],
itemStartIndex: index,
});
}
}
});
return ( return (
<List <List
itemSize={(index) => timeStampList[index].itemType === ITEM_TYPE.TIME ? 30 : 200} itemSize={(index) =>
height={height} timeStampList[index].itemType === ITEM_TYPE.TIME
width={width} ? 30
itemCount={timeStampList.length} : 200
key={`${router.query.collection}-${columns}`} }
> height={height}
{({ index, style }) => { width={width}
return (<ListItem style={style}> itemCount={timeStampList.length}
<ListContainer> key={`${router.query.collection}-${columns}`}
{ >
timeStampList[index].itemType === ITEM_TYPE.TIME {({ index, style }) => {
? <DateContainer>{timeStampList[index].date}</DateContainer> return (
: timeStampList[index].items.map((item, idx) =>{ <ListItem style={style}>
return getThumbnail(filteredData, timeStampList[index].itemStartIndex + idx); <ListContainer>
}) {timeStampList[index].itemType ===
} ITEM_TYPE.TIME ? (
</ListContainer> <DateContainer>
</ListItem>); {timeStampList[index].date}
}} </DateContainer>
</List> ) : (
) timeStampList[index].items.map((item, idx) => {
}} return getThumbnail(
</AutoSizer> filteredData,
<PhotoSwipe timeStampList[index].itemStartIndex + idx
isOpen={open} );
items={filteredData} })
options={options} )}
onClose={handleClose} </ListContainer>
gettingData={getSlideData} </ListItem>
/> );
</Container> }}
: <DeadCenter> </List>
<SadFace height={100} width={100} /> );
<div>No content found!</div> }}
</DeadCenter> </AutoSizer>
} <PhotoSwipe
</>); isOpen={open}
items={filteredData}
options={options}
onClose={handleClose}
gettingData={getSlideData}
/>
</Container>
) : (
<DeadCenter>
<SadFace height={100} width={100} />
<div>No content found!</div>
</DeadCenter>
)}
</FileUpload>
</>
);
} }