add cover image logic
This commit is contained in:
parent
a09025ce63
commit
a61cebbde9
|
@ -1,127 +1,87 @@
|
|||
import { SetDialogMessage } from 'components/MessageDialog';
|
||||
import NavigationButton, {
|
||||
SCROLL_DIRECTION,
|
||||
} from 'components/NavigationButton';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { IMAGE_CONTAINER_MAX_WIDTH } from 'constants/gallery';
|
||||
import { Collection, CollectionAndItsLatestFile } from 'types/collection';
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import { Collection, CollectionSummaries } from 'types/collection';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { SetCollectionNamerAttributes } from './CollectionNamer';
|
||||
import { ALL_SECTION } from 'constants/collection';
|
||||
import { Link } from '@mui/material';
|
||||
import { Link, Typography } from '@mui/material';
|
||||
import {
|
||||
CollectionTileWrapper,
|
||||
CollectionTile,
|
||||
ActiveIndicator,
|
||||
Hider,
|
||||
CollectionBarWrapper,
|
||||
Header,
|
||||
CollectionWithNavigationContainer,
|
||||
ScrollContainer,
|
||||
EmptyCollectionTile,
|
||||
} from 'components/collection';
|
||||
import { GalleryContext } from 'pages/gallery';
|
||||
import downloadManager from 'services/downloadManager';
|
||||
import { EnteFile } from 'types/file';
|
||||
|
||||
interface CollectionProps {
|
||||
collections: Collection[];
|
||||
collectionAndTheirLatestFile: CollectionAndItsLatestFile[];
|
||||
activeCollection?: number;
|
||||
setActiveCollection: (id?: number) => void;
|
||||
setDialogMessage: SetDialogMessage;
|
||||
syncWithRemote: () => Promise<void>;
|
||||
setCollectionNamerAttributes: SetCollectionNamerAttributes;
|
||||
startLoading: () => void;
|
||||
finishLoading: () => void;
|
||||
isInSearchMode: boolean;
|
||||
collectionFilesCount: Map<number, number>;
|
||||
collectionSummaries: CollectionSummaries;
|
||||
}
|
||||
|
||||
const SAMPLE_URL =
|
||||
'https://images.unsplash.com/photo-1615789591457-74a63395c990?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8YmFieSUyMGNhdHxlbnwwfHwwfHw%3D&w=1000&q=80';
|
||||
|
||||
const CollectionContainer = styled.div`
|
||||
overflow: hidden;
|
||||
height: 86px;
|
||||
display: flex;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const ScrollWrapper = styled.div`
|
||||
width: calc(100%- 80px);
|
||||
height: 100px;
|
||||
overflow: auto;
|
||||
max-width: 100%;
|
||||
scroll-behavior: smooth;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const CollectionBar = styled.div`
|
||||
width: 100%;
|
||||
margin: 10px auto;
|
||||
padding: 0 24px;
|
||||
@media (max-width: ${IMAGE_CONTAINER_MAX_WIDTH * 4}px) {
|
||||
padding: 0 4px;
|
||||
}
|
||||
border-bottom: 1px solid ${({ theme }) => theme.palette.grey.A400};
|
||||
`;
|
||||
|
||||
const EmptyCollectionTile = styled.div`
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
width: 80px;
|
||||
height: 64px;
|
||||
border-radius: 4px;
|
||||
padding: 4px 6px;
|
||||
align-items: flex-end;
|
||||
border: 1px dashed ${({ theme }) => theme.palette.grey.A200};
|
||||
justify-content: space-between;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const CollectionTile = styled(EmptyCollectionTile)<{ coverImgURL: string }>`
|
||||
background-image: url(${({ coverImgURL }) => coverImgURL});
|
||||
background-size: cover;
|
||||
border: none;
|
||||
`;
|
||||
|
||||
const CollectionTileWrapper = styled.div`
|
||||
margin-right: 6px;
|
||||
`;
|
||||
|
||||
const ActiveIndicator = styled.div`
|
||||
height: 3px;
|
||||
background-color: ${({ theme }) => theme.palette.text.primary};
|
||||
margin-top: 18px;
|
||||
border-radius: 2px;
|
||||
`;
|
||||
|
||||
const Hider = styled.div<{ hide: boolean }>`
|
||||
opacity: ${(props) => (props.hide ? '0' : '100')};
|
||||
height: ${(props) => (props.hide ? '0' : 'auto')};
|
||||
`;
|
||||
|
||||
const Header = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
`;
|
||||
const CollectionTileWithActiveIndicator = React.forwardRef(
|
||||
(
|
||||
props: {
|
||||
children;
|
||||
|
||||
active: boolean;
|
||||
coverImgURL: string;
|
||||
latestFile: EnteFile;
|
||||
onClick: () => void;
|
||||
},
|
||||
ref: any
|
||||
) => {
|
||||
const [coverImageURL, setCoverImageURL] = useState(null);
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
const { latestFile: file, onClick, active, children } = props;
|
||||
useEffect(() => {
|
||||
const main = async () => {
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
if (!galleryContext.thumbs.has(file.id)) {
|
||||
const url = await downloadManager.getThumbnail(file);
|
||||
galleryContext.thumbs.set(file.id, url);
|
||||
}
|
||||
setCoverImageURL(galleryContext.thumbs.get(file.id));
|
||||
};
|
||||
main();
|
||||
}, [file]);
|
||||
return (
|
||||
<CollectionTileWrapper ref={ref}>
|
||||
<CollectionTile
|
||||
coverImgURL={props.coverImgURL}
|
||||
onClick={props.onClick}>
|
||||
{props.children}
|
||||
<CollectionTile coverImgURL={coverImageURL} onClick={onClick}>
|
||||
{children}
|
||||
</CollectionTile>
|
||||
{props.active && <ActiveIndicator />}
|
||||
{active && <ActiveIndicator />}
|
||||
</CollectionTileWrapper>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default function Collections(props: CollectionProps) {
|
||||
const { activeCollection, collections, setActiveCollection } = props;
|
||||
const CreateNewCollectionHookTile = () => {
|
||||
return (
|
||||
<EmptyCollectionTile>
|
||||
<div>{constants.NEW} </div>
|
||||
<div>{'+'}</div>
|
||||
</EmptyCollectionTile>
|
||||
);
|
||||
};
|
||||
|
||||
export default function CollectionBar(props: CollectionProps) {
|
||||
const {
|
||||
activeCollection,
|
||||
collections,
|
||||
setActiveCollection,
|
||||
collectionSummaries,
|
||||
} = props;
|
||||
const collectionWrapperRef = useRef<HTMLDivElement>(null);
|
||||
const collectionChipsRef = props.collections.reduce(
|
||||
(refMap, collection) => {
|
||||
|
@ -172,25 +132,25 @@ export default function Collections(props: CollectionProps) {
|
|||
|
||||
return (
|
||||
<Hider hide={props.isInSearchMode}>
|
||||
<CollectionBar>
|
||||
<CollectionBarWrapper>
|
||||
<Header>
|
||||
{constants.ALBUMS}
|
||||
<Typography>{constants.ALBUMS}</Typography>
|
||||
{scrollObj.scrollWidth > scrollObj.clientWidth && (
|
||||
<Link component="button">{constants.ALL_ALBUMS}</Link>
|
||||
)}
|
||||
</Header>
|
||||
<CollectionContainer>
|
||||
{/* {scrollObj.scrollLeft > 0 && ( */}
|
||||
<NavigationButton
|
||||
scrollDirection={SCROLL_DIRECTION.LEFT}
|
||||
onClick={scrollCollection(SCROLL_DIRECTION.LEFT)}
|
||||
/>
|
||||
{/* )} */}
|
||||
<ScrollWrapper
|
||||
<CollectionWithNavigationContainer>
|
||||
{scrollObj.scrollLeft > 0 && (
|
||||
<NavigationButton
|
||||
scrollDirection={SCROLL_DIRECTION.LEFT}
|
||||
onClick={scrollCollection(SCROLL_DIRECTION.LEFT)}
|
||||
/>
|
||||
)}
|
||||
<ScrollContainer
|
||||
ref={collectionWrapperRef}
|
||||
onScroll={updateScrollObj}>
|
||||
<CollectionTileWithActiveIndicator
|
||||
coverImgURL={SAMPLE_URL}
|
||||
latestFile={null}
|
||||
active={activeCollection === ALL_SECTION}
|
||||
onClick={clickHandler(ALL_SECTION)}>
|
||||
{constants.ALL_SECTION_NAME}
|
||||
|
@ -211,27 +171,27 @@ export default function Collections(props: CollectionProps) {
|
|||
].map((item) => (
|
||||
<CollectionTileWithActiveIndicator
|
||||
key={item.id}
|
||||
latestFile={
|
||||
collectionSummaries?.get(item.id)
|
||||
?.latestFile
|
||||
}
|
||||
ref={collectionChipsRef[item.id]}
|
||||
active={activeCollection === item.id}
|
||||
onClick={clickHandler(item.id)}
|
||||
coverImgURL={SAMPLE_URL}>
|
||||
onClick={clickHandler(item.id)}>
|
||||
{item.name}
|
||||
</CollectionTileWithActiveIndicator>
|
||||
))}
|
||||
<EmptyCollectionTile>
|
||||
<div>{constants.NEW} </div>
|
||||
<div>{'+'}</div>
|
||||
</EmptyCollectionTile>
|
||||
</ScrollWrapper>
|
||||
{/* {scrollObj.scrollLeft < */}
|
||||
{/* scrollObj.scrollWidth - scrollObj.clientWidth && ( */}
|
||||
<NavigationButton
|
||||
scrollDirection={SCROLL_DIRECTION.RIGHT}
|
||||
onClick={scrollCollection(SCROLL_DIRECTION.RIGHT)}
|
||||
/>
|
||||
{/* )} */}
|
||||
</CollectionContainer>
|
||||
</CollectionBar>
|
||||
<CreateNewCollectionHookTile />
|
||||
</ScrollContainer>
|
||||
{scrollObj.scrollLeft <
|
||||
scrollObj.scrollWidth - scrollObj.clientWidth && (
|
||||
<NavigationButton
|
||||
scrollDirection={SCROLL_DIRECTION.RIGHT}
|
||||
onClick={scrollCollection(SCROLL_DIRECTION.RIGHT)}
|
||||
/>
|
||||
)}
|
||||
</CollectionWithNavigationContainer>
|
||||
</CollectionBarWrapper>
|
||||
</Hider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
getLocalCollections,
|
||||
getNonEmptyCollections,
|
||||
createCollection,
|
||||
getCollectionSummaries,
|
||||
} from 'services/collectionService';
|
||||
import constants from 'utils/strings/constants';
|
||||
import billingService from 'services/billingService';
|
||||
|
@ -48,7 +49,6 @@ import {
|
|||
getSelectedFiles,
|
||||
mergeMetadata,
|
||||
sortFiles,
|
||||
sortFilesIntoCollections,
|
||||
} from 'utils/file';
|
||||
import SearchBar from 'components/Search';
|
||||
import SelectedFileOptions from 'components/pages/gallery/SelectedFileOptions/GalleryOptions';
|
||||
|
@ -93,7 +93,11 @@ import DeleteBtn from 'components/DeleteBtn';
|
|||
import FixCreationTime, {
|
||||
FixCreationTimeAttributes,
|
||||
} from 'components/FixCreationTime';
|
||||
import { Collection, CollectionAndItsLatestFile } from 'types/collection';
|
||||
import {
|
||||
Collection,
|
||||
CollectionAndItsLatestFile,
|
||||
CollectionSummaries,
|
||||
} from 'types/collection';
|
||||
import { EnteFile } from 'types/file';
|
||||
import {
|
||||
GalleryContextType,
|
||||
|
@ -101,7 +105,7 @@ import {
|
|||
Search,
|
||||
NotificationAttributes,
|
||||
} from 'types/gallery';
|
||||
import Collections from 'components/pages/gallery/Collections';
|
||||
import CollectionBar from 'components/pages/gallery/Collections';
|
||||
import { VISIBILITY_STATE } from 'types/magicMetadata';
|
||||
import ToastNotification from 'components/ToastNotification';
|
||||
import { ElectronFile } from 'types/upload';
|
||||
|
@ -185,8 +189,8 @@ export default function Gallery() {
|
|||
const [deleted, setDeleted] = useState<number[]>([]);
|
||||
const { startLoading, finishLoading, setDialogMessage, ...appContext } =
|
||||
useContext(AppContext);
|
||||
const [collectionFilesCount, setCollectionFilesCount] =
|
||||
useState<Map<number, number>>();
|
||||
const [collectionSummaries, setCollectionSummaries] =
|
||||
useState<CollectionSummaries>();
|
||||
const [activeCollection, setActiveCollection] = useState<number>(undefined);
|
||||
const [trash, setTrash] = useState<Trash>([]);
|
||||
const [fixCreationTimeView, setFixCreationTimeView] = useState(false);
|
||||
|
@ -346,12 +350,9 @@ export default function Gallery() {
|
|||
files
|
||||
);
|
||||
setCollectionsAndTheirLatestFile(collectionsAndTheirLatestFile);
|
||||
const collectionWiseFiles = sortFilesIntoCollections(files);
|
||||
const collectionFilesCount = new Map<number, number>();
|
||||
for (const [id, files] of collectionWiseFiles) {
|
||||
collectionFilesCount.set(id, files.length);
|
||||
}
|
||||
setCollectionFilesCount(collectionFilesCount);
|
||||
const collectionSummaries = getCollectionSummaries(collections, files);
|
||||
|
||||
setCollectionSummaries(collectionSummaries);
|
||||
|
||||
const archivedCollections = getArchivedCollections(collections);
|
||||
setArchivedCollections(new Set(archivedCollections));
|
||||
|
@ -605,18 +606,12 @@ export default function Gallery() {
|
|||
setSearch={updateSearch}
|
||||
searchStats={searchStats}
|
||||
/>
|
||||
<Collections
|
||||
<CollectionBar
|
||||
collections={collections}
|
||||
collectionAndTheirLatestFile={collectionsAndTheirLatestFile}
|
||||
isInSearchMode={isInSearchMode}
|
||||
activeCollection={activeCollection}
|
||||
setActiveCollection={setActiveCollection}
|
||||
syncWithRemote={syncWithRemote}
|
||||
setDialogMessage={setDialogMessage}
|
||||
setCollectionNamerAttributes={setCollectionNamerAttributes}
|
||||
startLoading={startLoading}
|
||||
finishLoading={finishLoading}
|
||||
collectionFilesCount={collectionFilesCount}
|
||||
collectionSummaries={collectionSummaries}
|
||||
/>
|
||||
<CollectionNamer
|
||||
show={collectionNamerView}
|
||||
|
|
|
@ -12,7 +12,7 @@ import HTTPService from './HTTPService';
|
|||
import { EnteFile } from 'types/file';
|
||||
import { logError } from 'utils/sentry';
|
||||
import { CustomError } from 'utils/error';
|
||||
import { sortFiles } from 'utils/file';
|
||||
import { sortFiles, sortFilesIntoCollections } from 'utils/file';
|
||||
import {
|
||||
Collection,
|
||||
CollectionAndItsLatestFile,
|
||||
|
@ -23,6 +23,7 @@ import {
|
|||
CreatePublicAccessTokenRequest,
|
||||
PublicURL,
|
||||
UpdatePublicURL,
|
||||
CollectionSummaries,
|
||||
} from 'types/collection';
|
||||
import { COLLECTION_SORT_BY, CollectionType } from 'constants/collection';
|
||||
import { UpdateMagicMetadataRequest } from 'types/magicMetadata';
|
||||
|
@ -795,3 +796,41 @@ function moveFavCollectionToFront(collections: Collection[]) {
|
|||
: 0
|
||||
);
|
||||
}
|
||||
|
||||
export function getCollectionSummaries(
|
||||
collections: Collection[],
|
||||
files: EnteFile[]
|
||||
): CollectionSummaries {
|
||||
const CollectionSummaries: CollectionSummaries = new Map();
|
||||
const collectionAndTheirLatestFile = getCollectionsAndTheirLatestFile(
|
||||
collections,
|
||||
files
|
||||
);
|
||||
const collectionAndTheirLatestFileMap = new Map();
|
||||
for (const collectionAndItsLatestFile of collectionAndTheirLatestFile) {
|
||||
collectionAndTheirLatestFileMap.set(
|
||||
collectionAndItsLatestFile.collection.id,
|
||||
collectionAndItsLatestFile.file
|
||||
);
|
||||
}
|
||||
const collectionFilesCount = getCollectionsFileCount(files);
|
||||
|
||||
for (const collection of collections) {
|
||||
CollectionSummaries.set(collection.id, {
|
||||
collectionName: collection.name,
|
||||
latestFile: collectionAndTheirLatestFileMap.get(collection.id),
|
||||
fileCount: collectionFilesCount.get(collection.id),
|
||||
});
|
||||
}
|
||||
|
||||
return CollectionSummaries;
|
||||
}
|
||||
|
||||
function getCollectionsFileCount(files: EnteFile[]) {
|
||||
const collectionWiseFiles = sortFilesIntoCollections(files);
|
||||
const collectionFilesCount = new Map<number, number>();
|
||||
for (const [id, files] of collectionWiseFiles) {
|
||||
collectionFilesCount.set(id, files.length);
|
||||
}
|
||||
return collectionFilesCount;
|
||||
}
|
||||
|
|
|
@ -91,3 +91,10 @@ export interface CollectionMagicMetadata
|
|||
extends Omit<MagicMetadataCore, 'data'> {
|
||||
data: CollectionMagicMetadataProps;
|
||||
}
|
||||
export interface CollectionSummary {
|
||||
collectionName: string;
|
||||
latestFile: EnteFile;
|
||||
fileCount: number;
|
||||
}
|
||||
|
||||
export type CollectionSummaries = Map<number, CollectionSummary>;
|
||||
|
|
Loading…
Reference in a new issue