[Abhinav] | Integrated FileUpload with gallery component
This commit is contained in:
parent
82729532fe
commit
d71d89bada
1 changed files with 322 additions and 274 deletions
|
@ -2,7 +2,14 @@ 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';
|
||||
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 PreviewCard from './components/PreviewCard';
|
||||
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 Collections from './components/Collections';
|
||||
import SadFace from 'components/SadFace';
|
||||
import FileUpload from './components/DragAndDropUpload';
|
||||
|
||||
enum ITEM_TYPE {
|
||||
TIME='TIME',
|
||||
TILE='TILE'
|
||||
TIME = 'TIME',
|
||||
TILE = 'TILE',
|
||||
}
|
||||
|
||||
interface TimeStampListItem {
|
||||
itemType: ITEM_TYPE,
|
||||
items?: file[],
|
||||
itemStartIndex?: number,
|
||||
date?: string,
|
||||
itemType: ITEM_TYPE;
|
||||
items?: file[];
|
||||
itemStartIndex?: number;
|
||||
date?: string;
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
display: block;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 auto;
|
||||
|
||||
.pswp-thumbnail {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pswp-thumbnail {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
const ListItem = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const DeadCenter = styled.div`
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const ListContainer = styled.div`
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
color: #fff;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
width: 1000px;
|
||||
}
|
||||
@media (min-width: 1000px) {
|
||||
width: 1000px;
|
||||
}
|
||||
|
||||
@media (min-width: 450px) and (max-width: 1000px) {
|
||||
width: 600px;
|
||||
}
|
||||
@media (min-width: 450px) and (max-width: 1000px) {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
@media (max-width: 450px) {
|
||||
width: 100%;
|
||||
}
|
||||
@media (max-width: 450px) {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const DateContainer = styled.div`
|
||||
padding: 0 4px;
|
||||
padding: 0 4px;
|
||||
`;
|
||||
|
||||
const PAGE_SIZE = 12;
|
||||
const COLUMNS = 3;
|
||||
|
||||
export default function Gallery() {
|
||||
const router = useRouter();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [collections, setCollections] = useState<collection[]>([])
|
||||
const [data, setData] = useState<file[]>();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [options, setOptions] = useState<Options>({
|
||||
history: false,
|
||||
maxSpreadZoom: 5,
|
||||
});
|
||||
const fetching: { [k: number]: boolean } = {};
|
||||
const router = useRouter();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [collections, setCollections] = useState<collection[]>([]);
|
||||
const [data, setData] = useState<file[]>();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [options, setOptions] = useState<Options>({
|
||||
history: false,
|
||||
maxSpreadZoom: 5,
|
||||
});
|
||||
const fetching: { [k: number]: boolean } = {};
|
||||
|
||||
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 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>
|
||||
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 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) => {
|
||||
data[index] = {
|
||||
...data[index],
|
||||
msrc: url,
|
||||
w: window.innerWidth,
|
||||
h: window.innerHeight,
|
||||
}
|
||||
if (data[index].metadata.fileType === 1 && !data[index].html) {
|
||||
data[index].html = `
|
||||
if (!data || loading) {
|
||||
return (
|
||||
<div className='text-center'>
|
||||
<Spinner animation='border' variant='primary' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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">
|
||||
<img src="${url}" />
|
||||
<div class="spinner-border text-light" role="status">
|
||||
|
@ -135,220 +153,250 @@ export default function Gallery() {
|
|||
</div>
|
||||
</div>
|
||||
`;
|
||||
delete data[index].src;
|
||||
}
|
||||
if (data[index].metadata.fileType === 0 && !data[index].src) {
|
||||
data[index].src = url;
|
||||
}
|
||||
setData(data);
|
||||
delete data[index].src;
|
||||
}
|
||||
if (data[index].metadata.fileType === 0 && !data[index].src) {
|
||||
data[index].src = url;
|
||||
}
|
||||
setData(data);
|
||||
};
|
||||
|
||||
const updateSrcUrl = (index: number, url: string) => {
|
||||
data[index] = {
|
||||
...data[index],
|
||||
src: url,
|
||||
w: window.innerWidth,
|
||||
h: window.innerHeight,
|
||||
}
|
||||
if (data[index].metadata.fileType === 1) {
|
||||
data[index].html = `
|
||||
const updateSrcUrl = (index: number, url: string) => {
|
||||
data[index] = {
|
||||
...data[index],
|
||||
src: url,
|
||||
w: window.innerWidth,
|
||||
h: window.innerHeight,
|
||||
};
|
||||
if (data[index].metadata.fileType === 1) {
|
||||
data[index].html = `
|
||||
<video controls>
|
||||
<source src="${url}" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
`;
|
||||
delete data[index].src;
|
||||
}
|
||||
setData(data);
|
||||
delete data[index].src;
|
||||
}
|
||||
setData(data);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
const handleClose = () => {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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]) {
|
||||
fetching[item.dataIndex] = true;
|
||||
const url = await getFile(token, item);
|
||||
updateSrcUrl(item.dataIndex, url);
|
||||
if (item.metadata.fileType === 1) {
|
||||
item.html = `
|
||||
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>
|
||||
<source src="${url}" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
`;
|
||||
delete item.src;
|
||||
item.w = window.innerWidth;
|
||||
} else {
|
||||
item.src = url;
|
||||
}
|
||||
item.h = window.innerHeight;
|
||||
try {
|
||||
instance.invalidateCurrItems();
|
||||
instance.updateSize(true);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
delete item.src;
|
||||
item.w = window.innerWidth;
|
||||
} else {
|
||||
item.src = url;
|
||||
}
|
||||
item.h = window.innerHeight;
|
||||
try {
|
||||
instance.invalidateCurrItems();
|
||||
instance.updateSize(true);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const selectCollection = (id?: string) => {
|
||||
const href = `/gallery?collection=${id || ''}`;
|
||||
router.push(href, undefined, { shallow: true });
|
||||
}
|
||||
const selectCollection = (id?: string) => {
|
||||
const href = `/gallery?collection=${id || ''}`;
|
||||
router.push(href, undefined, { shallow: true });
|
||||
};
|
||||
|
||||
const idSet = new Set();
|
||||
const filteredData = data.map((item, index) => ({
|
||||
...item,
|
||||
dataIndex: index,
|
||||
})).filter(item => {
|
||||
if (!idSet.has(item.id)) {
|
||||
if (!router.query.collection || router.query.collection === item.collectionID.toString()) {
|
||||
idSet.add(item.id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
const idSet = new Set();
|
||||
const filteredData = data
|
||||
.map((item, index) => ({
|
||||
...item,
|
||||
dataIndex: index,
|
||||
}))
|
||||
.filter((item) => {
|
||||
if (!idSet.has(item.id)) {
|
||||
if (
|
||||
!router.query.collection ||
|
||||
router.query.collection === item.collectionID.toString()
|
||||
) {
|
||||
idSet.add(item.id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const isSameDay = (first, second) => {
|
||||
return first.getFullYear() === second.getFullYear() &&
|
||||
first.getMonth() === second.getMonth() &&
|
||||
first.getDate() === second.getDate();
|
||||
}
|
||||
const isSameDay = (first, second) => {
|
||||
return (
|
||||
first.getFullYear() === second.getFullYear() &&
|
||||
first.getMonth() === second.getMonth() &&
|
||||
first.getDate() === second.getDate()
|
||||
);
|
||||
};
|
||||
|
||||
return (<>
|
||||
<Collections
|
||||
collections={collections}
|
||||
selected={router.query.collection?.toString()}
|
||||
selectCollection={selectCollection}
|
||||
/>
|
||||
{
|
||||
filteredData.length
|
||||
? <Container>
|
||||
<AutoSizer>
|
||||
{({ height, width }) => {
|
||||
let columns;
|
||||
if (width >= 1000) {
|
||||
columns = 5;
|
||||
} else if (width < 1000 && width >= 450) {
|
||||
columns = 3;
|
||||
} else if (width < 450 && width >= 300) {
|
||||
columns = 2;
|
||||
} else {
|
||||
columns = 1;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Collections
|
||||
collections={collections}
|
||||
selected={router.query.collection?.toString()}
|
||||
selectCollection={selectCollection}
|
||||
/>
|
||||
<FileUpload>
|
||||
{filteredData.length ? (
|
||||
<Container>
|
||||
<AutoSizer>
|
||||
{({ height, width }) => {
|
||||
let columns;
|
||||
if (width >= 1000) {
|
||||
columns = 5;
|
||||
} else if (width < 1000 && width >= 450) {
|
||||
columns = 3;
|
||||
} else if (width < 450 && width >= 300) {
|
||||
columns = 2;
|
||||
} else {
|
||||
columns = 1;
|
||||
}
|
||||
|
||||
const timeStampList: TimeStampListItem[] = [];
|
||||
let listItemIndex = 0;
|
||||
let currentDate = -1;
|
||||
filteredData.forEach((item, index) => {
|
||||
if (!isSameDay(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'
|
||||
});
|
||||
timeStampList.push({
|
||||
itemType: ITEM_TYPE.TIME,
|
||||
date: dateTimeFormat.format(currentDate),
|
||||
});
|
||||
timeStampList.push({
|
||||
itemType: ITEM_TYPE.TILE,
|
||||
items: [item],
|
||||
itemStartIndex: index,
|
||||
});
|
||||
listItemIndex = 1;
|
||||
} else {
|
||||
if (listItemIndex < columns) {
|
||||
timeStampList[timeStampList.length - 1].items.push(item);
|
||||
listItemIndex++;
|
||||
} else {
|
||||
listItemIndex = 1;
|
||||
timeStampList.push({
|
||||
itemType: ITEM_TYPE.TILE,
|
||||
items: [item],
|
||||
itemStartIndex: index,
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
const timeStampList: TimeStampListItem[] = [];
|
||||
let listItemIndex = 0;
|
||||
let currentDate = -1;
|
||||
filteredData.forEach((item, index) => {
|
||||
if (
|
||||
!isSameDay(
|
||||
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',
|
||||
});
|
||||
timeStampList.push({
|
||||
itemType: ITEM_TYPE.TIME,
|
||||
date: dateTimeFormat.format(currentDate),
|
||||
});
|
||||
timeStampList.push({
|
||||
itemType: ITEM_TYPE.TILE,
|
||||
items: [item],
|
||||
itemStartIndex: index,
|
||||
});
|
||||
listItemIndex = 1;
|
||||
} else {
|
||||
if (listItemIndex < columns) {
|
||||
timeStampList[timeStampList.length - 1].items.push(item);
|
||||
listItemIndex++;
|
||||
} else {
|
||||
listItemIndex = 1;
|
||||
timeStampList.push({
|
||||
itemType: ITEM_TYPE.TILE,
|
||||
items: [item],
|
||||
itemStartIndex: index,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<List
|
||||
itemSize={(index) => timeStampList[index].itemType === ITEM_TYPE.TIME ? 30 : 200}
|
||||
height={height}
|
||||
width={width}
|
||||
itemCount={timeStampList.length}
|
||||
key={`${router.query.collection}-${columns}`}
|
||||
>
|
||||
{({ index, style }) => {
|
||||
return (<ListItem style={style}>
|
||||
<ListContainer>
|
||||
{
|
||||
timeStampList[index].itemType === ITEM_TYPE.TIME
|
||||
? <DateContainer>{timeStampList[index].date}</DateContainer>
|
||||
: timeStampList[index].items.map((item, idx) =>{
|
||||
return getThumbnail(filteredData, timeStampList[index].itemStartIndex + idx);
|
||||
})
|
||||
}
|
||||
</ListContainer>
|
||||
</ListItem>);
|
||||
}}
|
||||
</List>
|
||||
)
|
||||
}}
|
||||
</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>
|
||||
}
|
||||
</>);
|
||||
return (
|
||||
<List
|
||||
itemSize={(index) =>
|
||||
timeStampList[index].itemType === ITEM_TYPE.TIME
|
||||
? 30
|
||||
: 200
|
||||
}
|
||||
height={height}
|
||||
width={width}
|
||||
itemCount={timeStampList.length}
|
||||
key={`${router.query.collection}-${columns}`}
|
||||
>
|
||||
{({ index, style }) => {
|
||||
return (
|
||||
<ListItem style={style}>
|
||||
<ListContainer>
|
||||
{timeStampList[index].itemType ===
|
||||
ITEM_TYPE.TIME ? (
|
||||
<DateContainer>
|
||||
{timeStampList[index].date}
|
||||
</DateContainer>
|
||||
) : (
|
||||
timeStampList[index].items.map((item, idx) => {
|
||||
return getThumbnail(
|
||||
filteredData,
|
||||
timeStampList[index].itemStartIndex + idx
|
||||
);
|
||||
})
|
||||
)}
|
||||
</ListContainer>
|
||||
</ListItem>
|
||||
);
|
||||
}}
|
||||
</List>
|
||||
);
|
||||
}}
|
||||
</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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue