Merge pull request #577 from ente-io/collections-redesign-v2
Collections redesign v2
This commit is contained in:
commit
9fe12d1510
|
@ -19,9 +19,7 @@ export default function AllCollectionCard({
|
|||
<CollectionCard
|
||||
collectionTile={collectionTile}
|
||||
latestFile={collectionSummary.latestFile}
|
||||
onClick={() =>
|
||||
onCollectionClick(collectionSummary.collectionAttributes.id)
|
||||
}>
|
||||
onClick={() => onCollectionClick(collectionSummary.id)}>
|
||||
<div>
|
||||
<Typography
|
||||
css={`
|
||||
|
@ -29,7 +27,7 @@ export default function AllCollectionCard({
|
|||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
`}>
|
||||
{collectionSummary.collectionAttributes.name}
|
||||
{collectionSummary.name}
|
||||
</Typography>
|
||||
<Typography
|
||||
css={`
|
||||
|
|
|
@ -25,7 +25,7 @@ export default function AllCollectionContent({
|
|||
collectionTile={collectionTile}
|
||||
onCollectionClick={onCollectionClick}
|
||||
collectionSummary={collectionSummary}
|
||||
key={collectionSummary.collectionAttributes.id}
|
||||
key={collectionSummary.id}
|
||||
/>
|
||||
))}
|
||||
</FlexWrapper>
|
||||
|
|
|
@ -35,7 +35,7 @@ export default function AllCollections(props: Iprops) {
|
|||
() =>
|
||||
sortCollectionSummaries(
|
||||
[...collectionSummaries.values()].filter(
|
||||
(x) => x.collectionAttributes.type !== CollectionType.system
|
||||
(x) => x.type !== CollectionType.system
|
||||
),
|
||||
collectionSortBy
|
||||
),
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
import React from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import NavigateNextIcon from '@mui/icons-material/NavigateNext';
|
||||
|
||||
export enum SCROLL_DIRECTION {
|
||||
LEFT = -1,
|
||||
RIGHT = +1,
|
||||
}
|
||||
import { SCROLL_DIRECTION } from 'hooks/useComponentScroll';
|
||||
|
||||
const Wrapper = styled.button<{ direction: SCROLL_DIRECTION }>`
|
||||
position: absolute;
|
||||
|
@ -43,9 +39,9 @@ const Wrapper = styled.button<{ direction: SCROLL_DIRECTION }>`
|
|||
}
|
||||
`;
|
||||
|
||||
const NavigationButton = ({ scrollDirection, ...rest }) => (
|
||||
const ScrollButton = ({ scrollDirection, ...rest }) => (
|
||||
<Wrapper direction={scrollDirection} {...rest}>
|
||||
<NavigateNextIcon />
|
||||
</Wrapper>
|
||||
);
|
||||
export default NavigationButton;
|
||||
export default ScrollButton;
|
|
@ -1,24 +1,23 @@
|
|||
import NavigationButton, {
|
||||
SCROLL_DIRECTION,
|
||||
} from 'components/Collections/CollectionBar/NavigationButton';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Collection, CollectionSummaries } from 'types/collection';
|
||||
import ScrollButton from 'components/Collections/CollectionBar/ScrollButton';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import { CollectionSummaries } from 'types/collection';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { ALL_SECTION } from 'constants/collection';
|
||||
import { ALL_SECTION, COLLECTION_SORT_BY } from 'constants/collection';
|
||||
import { Typography } from '@mui/material';
|
||||
import {
|
||||
Hider,
|
||||
CollectionBarWrapper,
|
||||
ScrollContainer,
|
||||
PaddedSpaceBetweenFlex,
|
||||
CollectionSectionWrapper,
|
||||
} from 'components/Collections/styledComponents';
|
||||
import CollectionCardWithActiveIndicator from 'components/Collections/CollectionBar/CollectionCardWithActiveIndicator';
|
||||
import useComponentScroll from 'hooks/useComponentScroll';
|
||||
import useComponentScroll, { SCROLL_DIRECTION } from 'hooks/useComponentScroll';
|
||||
import useWindowSize from 'hooks/useWindowSize';
|
||||
import LinkButton from 'components/pages/gallery/LinkButton';
|
||||
import { SpaceBetweenFlex } from 'components/Container';
|
||||
import { sortCollectionSummaries } from 'services/collectionService';
|
||||
|
||||
interface IProps {
|
||||
collections: Collection[];
|
||||
activeCollection?: number;
|
||||
setActiveCollection: (id?: number) => void;
|
||||
isInSearchMode: boolean;
|
||||
|
@ -29,13 +28,23 @@ interface IProps {
|
|||
export default function CollectionBar(props: IProps) {
|
||||
const {
|
||||
activeCollection,
|
||||
collections,
|
||||
|
||||
setActiveCollection,
|
||||
collectionSummaries,
|
||||
showAllCollections,
|
||||
} = props;
|
||||
|
||||
const sortedCollectionSummary = useMemo(
|
||||
() =>
|
||||
sortCollectionSummaries(
|
||||
[...collectionSummaries.values()],
|
||||
COLLECTION_SORT_BY.UPDATION_TIME_DESCENDING
|
||||
),
|
||||
[collectionSummaries]
|
||||
);
|
||||
|
||||
const windowSize = useWindowSize();
|
||||
|
||||
const {
|
||||
componentRef,
|
||||
scrollComponent,
|
||||
|
@ -43,12 +52,12 @@ export default function CollectionBar(props: IProps) {
|
|||
onFarLeft,
|
||||
onFarRight,
|
||||
} = useComponentScroll({
|
||||
dependencies: [windowSize, collections],
|
||||
dependencies: [windowSize, collectionSummaries],
|
||||
});
|
||||
|
||||
const collectionChipsRef = props.collections.reduce(
|
||||
(refMap, collection) => {
|
||||
refMap[collection.id] = React.createRef();
|
||||
const collectionChipsRef = sortedCollectionSummary.reduce(
|
||||
(refMap, collectionSummary) => {
|
||||
refMap[collectionSummary.id] = React.createRef();
|
||||
return refMap;
|
||||
},
|
||||
{}
|
||||
|
@ -66,17 +75,20 @@ export default function CollectionBar(props: IProps) {
|
|||
|
||||
return (
|
||||
<Hider hide={props.isInSearchMode}>
|
||||
<PaddedSpaceBetweenFlex>
|
||||
<Typography>{constants.ALBUMS}</Typography>
|
||||
{hasScrollBar && (
|
||||
<LinkButton onClick={showAllCollections}>
|
||||
{constants.VIEW_ALL_ALBUMS}
|
||||
</LinkButton>
|
||||
)}
|
||||
</PaddedSpaceBetweenFlex>
|
||||
<CollectionSectionWrapper>
|
||||
<SpaceBetweenFlex>
|
||||
<Typography>{constants.ALBUMS}</Typography>
|
||||
{hasScrollBar && (
|
||||
<LinkButton onClick={showAllCollections}>
|
||||
{constants.VIEW_ALL_ALBUMS}
|
||||
</LinkButton>
|
||||
)}
|
||||
</SpaceBetweenFlex>
|
||||
</CollectionSectionWrapper>
|
||||
|
||||
<CollectionBarWrapper>
|
||||
{!onFarLeft && (
|
||||
<NavigationButton
|
||||
<ScrollButton
|
||||
scrollDirection={SCROLL_DIRECTION.LEFT}
|
||||
onClick={scrollComponent(SCROLL_DIRECTION.LEFT)}
|
||||
/>
|
||||
|
@ -88,12 +100,10 @@ export default function CollectionBar(props: IProps) {
|
|||
onClick={clickHandler(ALL_SECTION)}>
|
||||
{constants.ALL_SECTION_NAME}
|
||||
</CollectionCardWithActiveIndicator>
|
||||
{collections.map((item) => (
|
||||
{sortedCollectionSummary.map((item) => (
|
||||
<CollectionCardWithActiveIndicator
|
||||
key={item.id}
|
||||
latestFile={
|
||||
collectionSummaries.get(item.id)?.latestFile
|
||||
}
|
||||
latestFile={item.latestFile}
|
||||
ref={collectionChipsRef[item.id]}
|
||||
active={activeCollection === item.id}
|
||||
onClick={clickHandler(item.id)}>
|
||||
|
@ -102,7 +112,7 @@ export default function CollectionBar(props: IProps) {
|
|||
))}
|
||||
</ScrollContainer>
|
||||
{!onFarRight && (
|
||||
<NavigationButton
|
||||
<ScrollButton
|
||||
scrollDirection={SCROLL_DIRECTION.RIGHT}
|
||||
onClick={scrollComponent(SCROLL_DIRECTION.RIGHT)}
|
||||
/>
|
||||
|
|
|
@ -1,50 +1,24 @@
|
|||
import React from 'react';
|
||||
import { Typography } from '@mui/material';
|
||||
import React from 'react';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { Collection, CollectionSummary } from 'types/collection';
|
||||
import { PaddedSpaceBetweenFlex } from 'components/Collections/styledComponents';
|
||||
import CollectionOptions from 'components/Collections/CollectionOptions';
|
||||
import { SetCollectionNamerAttributes } from 'components/Collections/CollectionNamer';
|
||||
import { ARCHIVE_SECTION, TRASH_SECTION } from 'constants/collection';
|
||||
|
||||
interface Iprops {
|
||||
activeCollection: Collection;
|
||||
collectionSummary: CollectionSummary;
|
||||
setCollectionNamerAttributes: SetCollectionNamerAttributes;
|
||||
showCollectionShareModal: () => void;
|
||||
redirectToAll: () => void;
|
||||
}
|
||||
export default function collectionInfo(props: Iprops) {
|
||||
if (!props.collectionSummary) {
|
||||
return <></>;
|
||||
}
|
||||
const {
|
||||
collectionSummary: { collectionAttributes, fileCount },
|
||||
} = props;
|
||||
|
||||
export function CollectionInfo({ name, fileCount }) {
|
||||
return (
|
||||
<PaddedSpaceBetweenFlex>
|
||||
<div>
|
||||
<Typography
|
||||
css={`
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
`}>
|
||||
{collectionAttributes.name}
|
||||
</Typography>
|
||||
<Typography
|
||||
css={`
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
`}>
|
||||
{constants.PHOTO_COUNT(fileCount)}
|
||||
</Typography>
|
||||
</div>
|
||||
{collectionAttributes.id !== ARCHIVE_SECTION &&
|
||||
collectionAttributes.id !== TRASH_SECTION && (
|
||||
<CollectionOptions {...props} />
|
||||
)}
|
||||
</PaddedSpaceBetweenFlex>
|
||||
<div>
|
||||
<Typography
|
||||
css={`
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
`}>
|
||||
{name}
|
||||
</Typography>
|
||||
<Typography
|
||||
css={`
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
`}>
|
||||
{constants.PHOTO_COUNT(fileCount)}
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
38
src/components/Collections/CollectionInfoWithOptions.tsx
Normal file
38
src/components/Collections/CollectionInfoWithOptions.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { CollectionInfo } from './CollectionInfo';
|
||||
import React from 'react';
|
||||
import { Collection, CollectionSummary } from 'types/collection';
|
||||
import { CollectionSectionWrapper } from 'components/Collections/styledComponents';
|
||||
import CollectionOptions from 'components/Collections/CollectionOptions';
|
||||
import { SetCollectionNamerAttributes } from 'components/Collections/CollectionNamer';
|
||||
import { CollectionType } from 'constants/collection';
|
||||
import { SpaceBetweenFlex } from 'components/Container';
|
||||
|
||||
interface Iprops {
|
||||
activeCollection: Collection;
|
||||
collectionSummary: CollectionSummary;
|
||||
setCollectionNamerAttributes: SetCollectionNamerAttributes;
|
||||
showCollectionShareModal: () => void;
|
||||
redirectToAll: () => void;
|
||||
}
|
||||
export default function collectionInfoWithOptions({
|
||||
collectionSummary,
|
||||
...props
|
||||
}: Iprops) {
|
||||
if (!collectionSummary) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const { name, type, fileCount } = collectionSummary;
|
||||
|
||||
return (
|
||||
<CollectionSectionWrapper>
|
||||
<SpaceBetweenFlex>
|
||||
<CollectionInfo name={name} fileCount={fileCount} />
|
||||
{type !== CollectionType.system &&
|
||||
type !== CollectionType.favorites && (
|
||||
<CollectionOptions {...props} />
|
||||
)}
|
||||
</SpaceBetweenFlex>
|
||||
</CollectionSectionWrapper>
|
||||
);
|
||||
}
|
|
@ -2,7 +2,7 @@ import { Collection, CollectionSummaries } from 'types/collection';
|
|||
import CollectionBar from 'components/Collections/CollectionBar';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import AllCollections from 'components/Collections/AllCollections';
|
||||
import CollectionInfo from 'components/Collections/CollectionInfo';
|
||||
import CollectionInfoWithOptions from 'components/Collections/CollectionInfoWithOptions';
|
||||
import { ALL_SECTION } from 'constants/collection';
|
||||
import CollectionShare from 'components/Collections/CollectionShare';
|
||||
import { SetCollectionNamerAttributes } from 'components/Collections/CollectionNamer';
|
||||
|
@ -45,7 +45,6 @@ export default function Collections(props: Iprops) {
|
|||
return (
|
||||
<>
|
||||
<CollectionBar
|
||||
collections={collections}
|
||||
isInSearchMode={isInSearchMode}
|
||||
activeCollection={activeCollectionID}
|
||||
setActiveCollection={setActiveCollectionID}
|
||||
|
@ -60,7 +59,7 @@ export default function Collections(props: Iprops) {
|
|||
setActiveCollection={setActiveCollectionID}
|
||||
/>
|
||||
|
||||
<CollectionInfo
|
||||
<CollectionInfoWithOptions
|
||||
collectionSummary={collectionSummaries.get(activeCollectionID)}
|
||||
activeCollection={activeCollection.current}
|
||||
setCollectionNamerAttributes={setCollectionNamerAttributes}
|
||||
|
|
|
@ -1,28 +1,19 @@
|
|||
import { SpaceBetweenFlex } from 'components/Container';
|
||||
import { IMAGE_CONTAINER_MAX_WIDTH } from 'constants/gallery';
|
||||
import { PaddedContainer } from 'components/Container';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const CollectionBarWrapper = styled.div`
|
||||
export const CollectionBarWrapper = styled(PaddedContainer)`
|
||||
display: flex;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 86px;
|
||||
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.A200};
|
||||
`;
|
||||
|
||||
export const PaddedSpaceBetweenFlex = styled(SpaceBetweenFlex)`
|
||||
export const CollectionSectionWrapper = styled(PaddedContainer)`
|
||||
margin-bottom: 8px;
|
||||
margin-top: 16px;
|
||||
padding: 0 24px;
|
||||
@media (max-width: ${IMAGE_CONTAINER_MAX_WIDTH * 4}px) {
|
||||
padding: 0 4px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const ScrollContainer = styled.div`
|
||||
|
@ -66,8 +57,7 @@ export const ActiveIndicator = styled.div`
|
|||
`;
|
||||
|
||||
export const Hider = styled.div<{ hide: boolean }>`
|
||||
opacity: ${(props) => (props.hide ? '0' : '100')};
|
||||
height: ${(props) => (props.hide ? '0' : 'auto')};
|
||||
display: ${(props) => (props.hide ? 'none' : 'block')};
|
||||
`;
|
||||
|
||||
export const AllCollectionTile = styled(CollectionTile)`
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Box } from '@mui/material';
|
||||
import styled from 'styled-components';
|
||||
import { default as MuiStyled } from '@mui/styled-engine';
|
||||
import { IMAGE_CONTAINER_MAX_WIDTH } from 'constants/gallery';
|
||||
|
||||
const VerticallyCentered = MuiStyled(Box)`
|
||||
flex: 1;
|
||||
|
@ -90,3 +91,10 @@ export const InvertedIconButton = styled(IconButton)`
|
|||
background-color: ${({ theme }) => theme.palette.primary.main};
|
||||
}
|
||||
`;
|
||||
|
||||
export const PaddedContainer = styled(Box)`
|
||||
padding: 0 24px;
|
||||
@media (max-width: ${IMAGE_CONTAINER_MAX_WIDTH * 4}px) {
|
||||
padding: 0 4px;
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -17,6 +17,7 @@ import { ENTE_WEBSITE_LINK } from 'constants/urls';
|
|||
import { getVariantColor, ButtonVariant } from './pages/gallery/LinkButton';
|
||||
import { convertBytesToHumanReadable } from 'utils/billing';
|
||||
import { DeduplicateContext } from 'pages/deduplicate';
|
||||
import { PaddedContainer } from './Container';
|
||||
|
||||
const A_DAY = 24 * 60 * 60 * 1000;
|
||||
const NO_OF_PAGES = 2;
|
||||
|
@ -65,19 +66,17 @@ const getTemplateColumns = (columns: number, groups?: number[]): string => {
|
|||
}
|
||||
};
|
||||
|
||||
const ListContainer = styled.div<{ columns: number; groups?: number[] }>`
|
||||
const ListContainer = styled(PaddedContainer)<{
|
||||
columns: number;
|
||||
groups?: number[];
|
||||
}>`
|
||||
user-select: none;
|
||||
display: grid;
|
||||
grid-template-columns: ${({ columns, groups }) =>
|
||||
getTemplateColumns(columns, groups)};
|
||||
grid-column-gap: ${GAP_BTW_TILES}px;
|
||||
padding: 0 24px;
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
|
||||
@media (max-width: ${IMAGE_CONTAINER_MAX_WIDTH * 4}px) {
|
||||
padding: 0 4px;
|
||||
}
|
||||
`;
|
||||
|
||||
const DateContainer = styled.div<{ span: number }>`
|
||||
|
|
|
@ -49,11 +49,11 @@ function CollectionSelector({
|
|||
const personalCollectionsOtherThanFrom = [
|
||||
...collectionSummaries.values(),
|
||||
]?.filter(
|
||||
({ collectionAttributes }) =>
|
||||
collectionAttributes.id !== attributes.fromCollection &&
|
||||
({ type, id, attributes: collectionAttributes }) =>
|
||||
id !== attributes.fromCollection &&
|
||||
collectionAttributes.ownerID === user?.id &&
|
||||
collectionAttributes.type !== CollectionType.favorites &&
|
||||
collectionAttributes.type !== CollectionType.system
|
||||
type !== CollectionType.favorites &&
|
||||
type !== CollectionType.system
|
||||
);
|
||||
if (personalCollectionsOtherThanFrom.length === 0) {
|
||||
props.onClose();
|
||||
|
@ -96,7 +96,7 @@ function CollectionSelector({
|
|||
collectionTile={CollectionSelectorTile}
|
||||
onCollectionClick={handleCollectionClick}
|
||||
collectionSummary={collectionSummary}
|
||||
key={collectionSummary.collectionAttributes.id}
|
||||
key={collectionSummary.id}
|
||||
/>
|
||||
))}
|
||||
</FlexWrapper>
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { SCROLL_DIRECTION } from 'components/Collections/CollectionBar/NavigationButton';
|
||||
import { useRef, useState, useEffect } from 'react';
|
||||
|
||||
export enum SCROLL_DIRECTION {
|
||||
LEFT = -1,
|
||||
RIGHT = +1,
|
||||
}
|
||||
|
||||
export default function useComponentScroll({
|
||||
dependencies,
|
||||
}: {
|
||||
|
|
|
@ -17,7 +17,6 @@ import {
|
|||
import styled from 'styled-components';
|
||||
import {
|
||||
syncCollections,
|
||||
getCollectionsAndTheirLatestFile,
|
||||
getFavItemIds,
|
||||
getLocalCollections,
|
||||
getNonEmptyCollections,
|
||||
|
@ -91,11 +90,7 @@ import DeleteBtn from 'components/DeleteBtn';
|
|||
import FixCreationTime, {
|
||||
FixCreationTimeAttributes,
|
||||
} from 'components/FixCreationTime';
|
||||
import {
|
||||
Collection,
|
||||
CollectionAndItsLatestFile,
|
||||
CollectionSummaries,
|
||||
} from 'types/collection';
|
||||
import { Collection, CollectionSummaries } from 'types/collection';
|
||||
import { EnteFile } from 'types/file';
|
||||
import {
|
||||
GalleryContextType,
|
||||
|
@ -145,8 +140,7 @@ export const GalleryContext = createContext<GalleryContextType>(
|
|||
export default function Gallery() {
|
||||
const router = useRouter();
|
||||
const [collections, setCollections] = useState<Collection[]>([]);
|
||||
const [collectionsAndTheirLatestFile, setCollectionsAndTheirLatestFile] =
|
||||
useState<CollectionAndItsLatestFile[]>([]);
|
||||
|
||||
const [files, setFiles] = useState<EnteFile[]>(null);
|
||||
const [favItemIds, setFavItemIds] = useState<Set<number>>();
|
||||
const [bannerMessage, setBannerMessage] = useState<JSX.Element | string>(
|
||||
|
@ -349,11 +343,7 @@ export default function Gallery() {
|
|||
const nonEmptyCollections = getNonEmptyCollections(collections, files);
|
||||
|
||||
setCollections(nonEmptyCollections);
|
||||
const collectionsAndTheirLatestFile = getCollectionsAndTheirLatestFile(
|
||||
nonEmptyCollections,
|
||||
files
|
||||
);
|
||||
setCollectionsAndTheirLatestFile(collectionsAndTheirLatestFile);
|
||||
|
||||
const collectionSummaries = getCollectionSummaries(
|
||||
nonEmptyCollections,
|
||||
files
|
||||
|
@ -672,7 +662,7 @@ export default function Gallery() {
|
|||
setUploadInProgress={setUploadInProgress}
|
||||
fileRejections={fileRejections}
|
||||
setFiles={setFiles}
|
||||
isFirstUpload={collectionsAndTheirLatestFile?.length === 0}
|
||||
isFirstUpload={collectionSummaries.size === 0}
|
||||
electronFiles={electronFiles}
|
||||
setElectronFiles={setElectronFiles}
|
||||
uploadTypeSelectorView={uploadTypeSelectorView}
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
syncPublicFiles,
|
||||
verifyPublicCollectionPassword,
|
||||
} from 'services/publicCollectionService';
|
||||
import { Collection, CollectionSummaries } from 'types/collection';
|
||||
import { Collection } from 'types/collection';
|
||||
import { EnteFile } from 'types/file';
|
||||
import { mergeMetadata, sortFiles } from 'utils/file';
|
||||
import { AppContext } from 'pages/_app';
|
||||
|
@ -33,6 +33,8 @@ import SingleInputForm from 'components/SingleInputForm';
|
|||
import { Card } from 'react-bootstrap';
|
||||
import { logError } from 'utils/sentry';
|
||||
import SharedAlbumNavbar from 'components/pages/sharedAlbum/Navbar';
|
||||
import { CollectionInfo } from 'components/Collections/CollectionInfo';
|
||||
import { CollectionSectionWrapper } from 'components/Collections/styledComponents';
|
||||
|
||||
const Loader = () => (
|
||||
<VerticallyCentered>
|
||||
|
@ -59,8 +61,6 @@ export default function PublicCollectionGallery() {
|
|||
const router = useRouter();
|
||||
const [isPasswordProtected, setIsPasswordProtected] =
|
||||
useState<boolean>(false);
|
||||
const [collectionSummaries, setCollectionSummaries] =
|
||||
useState<CollectionSummaries>(new Map());
|
||||
|
||||
useEffect(() => {
|
||||
const currentURL = new URL(window.location.href);
|
||||
|
@ -133,7 +133,7 @@ export default function PublicCollectionGallery() {
|
|||
collection?.publicURLs?.[0]?.passwordEnabled;
|
||||
setIsPasswordProtected(isPasswordProtected);
|
||||
setErrorMessage(null);
|
||||
let files = [];
|
||||
|
||||
// remove outdated password, sharer has disabled the password
|
||||
if (!isPasswordProtected && passwordJWTToken.current) {
|
||||
passwordJWTToken.current = null;
|
||||
|
@ -144,7 +144,7 @@ export default function PublicCollectionGallery() {
|
|||
(isPasswordProtected && passwordJWTToken.current)
|
||||
) {
|
||||
try {
|
||||
files = await syncPublicFiles(
|
||||
await syncPublicFiles(
|
||||
token.current,
|
||||
passwordJWTToken.current,
|
||||
collection,
|
||||
|
@ -162,18 +162,6 @@ export default function PublicCollectionGallery() {
|
|||
if (isPasswordProtected && !passwordJWTToken.current) {
|
||||
await removePublicFiles(collectionUID);
|
||||
}
|
||||
collectionSummaries.set(collection.id, {
|
||||
collectionAttributes: {
|
||||
name: collection.name,
|
||||
type: collection.type,
|
||||
id: collection.id,
|
||||
updationTime: collection.updationTime,
|
||||
ownerID: collection.owner.id,
|
||||
},
|
||||
latestFile: files[0],
|
||||
fileCount: files.length,
|
||||
});
|
||||
setCollectionSummaries(collectionSummaries);
|
||||
} catch (e) {
|
||||
const parsedError = parseSharingErrorCodes(e);
|
||||
if (
|
||||
|
@ -293,6 +281,12 @@ export default function PublicCollectionGallery() {
|
|||
openReportForm,
|
||||
}}>
|
||||
<SharedAlbumNavbar />
|
||||
<CollectionSectionWrapper>
|
||||
<CollectionInfo
|
||||
name={publicCollection.name}
|
||||
fileCount={publicFiles.length}
|
||||
/>
|
||||
</CollectionSectionWrapper>
|
||||
<PhotoFrame
|
||||
files={publicFiles}
|
||||
setFiles={setPublicFiles}
|
||||
|
@ -304,7 +298,6 @@ export default function PublicCollectionGallery() {
|
|||
openUploader={() => null}
|
||||
isInSearchMode={false}
|
||||
search={{}}
|
||||
setSearchStats={() => null}
|
||||
deleted={[]}
|
||||
activeCollection={ALL_SECTION}
|
||||
isSharedCollection
|
||||
|
|
|
@ -13,7 +13,7 @@ import { CustomError } from 'utils/error';
|
|||
import { sortFiles, sortFilesIntoCollections } from 'utils/file';
|
||||
import {
|
||||
Collection,
|
||||
CollectionAndItsLatestFile,
|
||||
CollectionLatestFiles,
|
||||
AddToCollectionRequest,
|
||||
MoveToCollectionRequest,
|
||||
EncryptedFileKey,
|
||||
|
@ -34,6 +34,7 @@ import {
|
|||
import { UpdateMagicMetadataRequest } from 'types/magicMetadata';
|
||||
import { EncryptionResult } from 'types/upload';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { IsArchived } from 'utils/magicMetadata';
|
||||
|
||||
const ENDPOINT = getEndpoint();
|
||||
const COLLECTION_TABLE = 'collections';
|
||||
|
@ -204,26 +205,23 @@ export const getCollection = async (
|
|||
}
|
||||
};
|
||||
|
||||
export const getCollectionsAndTheirLatestFile = (
|
||||
collections: Collection[],
|
||||
export const getCollectionLatestFiles = (
|
||||
files: EnteFile[]
|
||||
): CollectionAndItsLatestFile[] => {
|
||||
const latestFile = new Map<number, EnteFile>();
|
||||
): CollectionLatestFiles => {
|
||||
const latestFiles = new Map<number, EnteFile>();
|
||||
|
||||
files.forEach((file) => {
|
||||
if (!latestFile.has(file.collectionID)) {
|
||||
latestFile.set(file.collectionID, file);
|
||||
if (!latestFiles.has(file.collectionID)) {
|
||||
latestFiles.set(file.collectionID, file);
|
||||
}
|
||||
if (!latestFiles.has(ARCHIVE_SECTION) && IsArchived(file)) {
|
||||
latestFiles.set(ARCHIVE_SECTION, file);
|
||||
}
|
||||
if (!latestFiles.has(TRASH_SECTION) && file.isTrashed) {
|
||||
latestFiles.set(TRASH_SECTION, file);
|
||||
}
|
||||
});
|
||||
const collectionsAndTheirLatestFile: CollectionAndItsLatestFile[] = [];
|
||||
|
||||
for (const collection of collections) {
|
||||
collectionsAndTheirLatestFile.push({
|
||||
collection,
|
||||
file: latestFile.get(collection.id),
|
||||
});
|
||||
}
|
||||
return collectionsAndTheirLatestFile;
|
||||
return latestFiles;
|
||||
};
|
||||
|
||||
export const getFavItemIds = async (
|
||||
|
@ -730,13 +728,10 @@ export function sortCollectionSummaries(
|
|||
);
|
||||
case COLLECTION_SORT_BY.UPDATION_TIME_DESCENDING:
|
||||
return (
|
||||
b.collectionAttributes.updationTime -
|
||||
a.collectionAttributes.updationTime
|
||||
b.attributes.updationTime - a.attributes.updationTime
|
||||
);
|
||||
case COLLECTION_SORT_BY.NAME:
|
||||
return a.collectionAttributes.name.localeCompare(
|
||||
b.collectionAttributes.name
|
||||
);
|
||||
return a.name.localeCompare(b.name);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
@ -753,9 +748,9 @@ function compareCollectionsLatestFile(first: EnteFile, second: EnteFile) {
|
|||
|
||||
function moveFavCollectionToFront(collectionSummaries: CollectionSummary[]) {
|
||||
return collectionSummaries.sort((a, b) =>
|
||||
a.collectionAttributes.type === CollectionType.favorites
|
||||
a.type === CollectionType.favorites
|
||||
? -1
|
||||
: b.collectionAttributes.type === CollectionType.favorites
|
||||
: b.type === CollectionType.favorites
|
||||
? 1
|
||||
: 0
|
||||
);
|
||||
|
@ -766,39 +761,35 @@ export function getCollectionSummaries(
|
|||
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 collectionLatestFiles = getCollectionLatestFiles(files);
|
||||
const collectionFilesCount = getCollectionsFileCount(files);
|
||||
|
||||
for (const collection of collections) {
|
||||
collectionSummaries.set(collection.id, {
|
||||
collectionAttributes: {
|
||||
id: collection.id,
|
||||
name: collection.name,
|
||||
type: collection.type,
|
||||
id: collection.id,
|
||||
name: collection.name,
|
||||
type: collection.type,
|
||||
latestFile: collectionLatestFiles.get(collection.id),
|
||||
fileCount: collectionFilesCount.get(collection.id) ?? 0,
|
||||
attributes: {
|
||||
updationTime: collection.updationTime,
|
||||
ownerID: collection.owner.id,
|
||||
},
|
||||
latestFile: collectionAndTheirLatestFileMap.get(collection.id),
|
||||
fileCount: collectionFilesCount.get(collection.id) ?? 0,
|
||||
});
|
||||
}
|
||||
collectionSummaries.set(
|
||||
ARCHIVE_SECTION,
|
||||
getArchivedCollectionSummaries(collectionFilesCount)
|
||||
getArchivedCollectionSummaries(
|
||||
collectionFilesCount,
|
||||
collectionLatestFiles
|
||||
)
|
||||
);
|
||||
collectionSummaries.set(
|
||||
TRASH_SECTION,
|
||||
getTrashedCollectionSummaries(collectionFilesCount)
|
||||
getTrashedCollectionSummaries(
|
||||
collectionFilesCount,
|
||||
collectionLatestFiles
|
||||
)
|
||||
);
|
||||
return collectionSummaries;
|
||||
}
|
||||
|
@ -813,33 +804,27 @@ function getCollectionsFileCount(files: EnteFile[]): CollectionFilesCount {
|
|||
}
|
||||
|
||||
function getArchivedCollectionSummaries(
|
||||
collectionFilesCount: CollectionFilesCount
|
||||
collectionFilesCount: CollectionFilesCount,
|
||||
collectionsLatestFile: CollectionLatestFiles
|
||||
) {
|
||||
return {
|
||||
collectionAttributes: {
|
||||
id: ARCHIVE_SECTION,
|
||||
name: constants.ARCHIVE,
|
||||
type: CollectionType.system,
|
||||
updationTime: 0,
|
||||
ownerID: 0,
|
||||
},
|
||||
latestFile: null,
|
||||
id: TRASH_SECTION,
|
||||
name: constants.ARCHIVE,
|
||||
type: CollectionType.system,
|
||||
latestFile: collectionsLatestFile.get(ARCHIVE_SECTION),
|
||||
fileCount: collectionFilesCount.get(ARCHIVE_SECTION) ?? 0,
|
||||
} as CollectionSummary;
|
||||
}
|
||||
|
||||
function getTrashedCollectionSummaries(
|
||||
collectionFilesCount: CollectionFilesCount
|
||||
) {
|
||||
collectionFilesCount: CollectionFilesCount,
|
||||
collectionsLatestFile: CollectionLatestFiles
|
||||
): CollectionSummary {
|
||||
return {
|
||||
collectionAttributes: {
|
||||
id: TRASH_SECTION,
|
||||
name: constants.TRASH,
|
||||
type: CollectionType.system,
|
||||
updationTime: 0,
|
||||
ownerID: 0,
|
||||
},
|
||||
latestFile: null,
|
||||
id: TRASH_SECTION,
|
||||
name: constants.TRASH,
|
||||
type: CollectionType.system,
|
||||
latestFile: collectionsLatestFile.get(TRASH_SECTION),
|
||||
fileCount: collectionFilesCount.get(TRASH_SECTION) ?? 0,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -73,10 +73,7 @@ export interface collectionAttributes {
|
|||
pathDecryptionNonce?: string;
|
||||
}
|
||||
|
||||
export interface CollectionAndItsLatestFile {
|
||||
collection: Collection;
|
||||
file: EnteFile;
|
||||
}
|
||||
export type CollectionLatestFiles = Map<number, EnteFile>;
|
||||
|
||||
export interface RemoveFromCollectionRequest {
|
||||
collectionID: number;
|
||||
|
@ -92,16 +89,23 @@ export interface CollectionMagicMetadata
|
|||
data: CollectionMagicMetadataProps;
|
||||
}
|
||||
export interface CollectionSummary {
|
||||
collectionAttributes: {
|
||||
id: number;
|
||||
name: string;
|
||||
type: CollectionType;
|
||||
ownerID: number;
|
||||
updationTime: number;
|
||||
};
|
||||
latestFile: EnteFile;
|
||||
id: number;
|
||||
name: string;
|
||||
type: CollectionType;
|
||||
latestFile?: EnteFile;
|
||||
fileCount: number;
|
||||
attributes?: {
|
||||
ownerID?: number;
|
||||
updationTime?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export type CollectionSummaries = Map<number, CollectionSummary>;
|
||||
export type CollectionFilesCount = Map<number, number>;
|
||||
|
||||
export interface CollectionInfo {
|
||||
id: number;
|
||||
name: string;
|
||||
fileCount: number;
|
||||
type: CollectionType;
|
||||
}
|
||||
|
|
|
@ -140,21 +140,22 @@ export function isFileHEIC(mimeType: string) {
|
|||
}
|
||||
|
||||
export function sortFilesIntoCollections(files: EnteFile[]) {
|
||||
const collectionWiseFiles = new Map<number, EnteFile[]>();
|
||||
const collectionWiseFiles = new Map<number, EnteFile[]>([
|
||||
[ARCHIVE_SECTION, []],
|
||||
[TRASH_SECTION, []],
|
||||
]);
|
||||
for (const file of files) {
|
||||
let collectionID: number;
|
||||
if (file.isTrashed) {
|
||||
collectionID = TRASH_SECTION;
|
||||
} else if (IsArchived(file)) {
|
||||
collectionID = ARCHIVE_SECTION;
|
||||
} else {
|
||||
collectionID = file.collectionID;
|
||||
}
|
||||
if (!collectionWiseFiles.has(collectionID)) {
|
||||
collectionWiseFiles.set(collectionID, []);
|
||||
if (!collectionWiseFiles.has(file.collectionID)) {
|
||||
collectionWiseFiles.set(file.collectionID, []);
|
||||
}
|
||||
|
||||
collectionWiseFiles.get(collectionID).push(file);
|
||||
collectionWiseFiles.get(file.collectionID).push(file);
|
||||
if (IsArchived(file)) {
|
||||
collectionWiseFiles.get(ARCHIVE_SECTION).push(file);
|
||||
}
|
||||
if (file.isTrashed) {
|
||||
collectionWiseFiles.get(TRASH_SECTION).push(file);
|
||||
}
|
||||
}
|
||||
return collectionWiseFiles;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue