commit
20efc76a8d
|
@ -57,6 +57,7 @@ export const Value = styled.div<{ width?: string }>`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const FlexWrapper = styled.div`
|
export const FlexWrapper = styled.div`
|
||||||
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
20
src/components/icons/SortIcon.tsx
Normal file
20
src/components/icons/SortIcon.tsx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function SortIcon(props) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
height={props.height}
|
||||||
|
viewBox={props.viewBox}
|
||||||
|
width={props.width}>
|
||||||
|
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||||
|
<path d="M3 18h6v-2H3v2zM3 6v2h18V6H3zm0 7h12v-2H3v2z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SortIcon.defaultProps = {
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
viewBox: '0 0 24 24',
|
||||||
|
};
|
20
src/components/icons/TickIcon.tsx
Normal file
20
src/components/icons/TickIcon.tsx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function TickIcon(props) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
height={props.height}
|
||||||
|
viewBox={props.viewBox}
|
||||||
|
width={props.width}
|
||||||
|
fill="currentColor">
|
||||||
|
<path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TickIcon.defaultProps = {
|
||||||
|
height: 28,
|
||||||
|
width: 20,
|
||||||
|
viewBox: '0 0 24 24',
|
||||||
|
};
|
|
@ -9,7 +9,7 @@ import {
|
||||||
import { getSelectedCollection } from 'utils/collection';
|
import { getSelectedCollection } from 'utils/collection';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import { SetCollectionNamerAttributes } from './CollectionNamer';
|
import { SetCollectionNamerAttributes } from './CollectionNamer';
|
||||||
import LinkButton from './LinkButton';
|
import LinkButton, { ButtonVariant, LinkButtonProps } from './LinkButton';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
syncWithRemote: () => Promise<void>;
|
syncWithRemote: () => Promise<void>;
|
||||||
|
@ -21,6 +21,28 @@ interface Props {
|
||||||
showCollectionShareModal: () => void;
|
showCollectionShareModal: () => void;
|
||||||
redirectToAll: () => void;
|
redirectToAll: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const MenuLink = ({ children, ...props }: LinkButtonProps) => (
|
||||||
|
<LinkButton
|
||||||
|
style={{ fontSize: '14px', fontWeight: 700, padding: '8px 1em' }}
|
||||||
|
{...props}>
|
||||||
|
{children}
|
||||||
|
</LinkButton>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const MenuItem = (props: { children: any }) => (
|
||||||
|
<ListGroup.Item
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
background: '#282828',
|
||||||
|
padding: 0,
|
||||||
|
}}>
|
||||||
|
{props.children}
|
||||||
|
</ListGroup.Item>
|
||||||
|
);
|
||||||
|
|
||||||
const CollectionOptions = (props: Props) => {
|
const CollectionOptions = (props: Props) => {
|
||||||
const collectionRename = async (
|
const collectionRename = async (
|
||||||
selectedCollection: Collection,
|
selectedCollection: Collection,
|
||||||
|
@ -75,23 +97,6 @@ const CollectionOptions = (props: Props) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const MenuLink = (props) => (
|
|
||||||
<LinkButton
|
|
||||||
style={{ fontSize: '14px', fontWeight: 700, padding: '8px 1em' }}
|
|
||||||
{...props}>
|
|
||||||
{props.children}
|
|
||||||
</LinkButton>
|
|
||||||
);
|
|
||||||
|
|
||||||
const MenuItem = (props) => (
|
|
||||||
<ListGroup.Item
|
|
||||||
style={{
|
|
||||||
background: '#282828',
|
|
||||||
padding: 0,
|
|
||||||
}}>
|
|
||||||
{props.children}
|
|
||||||
</ListGroup.Item>
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<Popover id="collection-options" style={{ borderRadius: '10px' }}>
|
<Popover id="collection-options" style={{ borderRadius: '10px' }}>
|
||||||
<Popover.Content style={{ padding: 0, border: 'none' }}>
|
<Popover.Content style={{ padding: 0, border: 'none' }}>
|
||||||
|
@ -108,7 +113,7 @@ const CollectionOptions = (props: Props) => {
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem>
|
<MenuItem>
|
||||||
<MenuLink
|
<MenuLink
|
||||||
variant="danger"
|
variant={ButtonVariant.danger}
|
||||||
onClick={confirmDeleteCollection}>
|
onClick={confirmDeleteCollection}>
|
||||||
{constants.DELETE}
|
{constants.DELETE}
|
||||||
</MenuLink>
|
</MenuLink>
|
||||||
|
|
|
@ -7,6 +7,8 @@ import {
|
||||||
} from 'services/collectionService';
|
} from 'services/collectionService';
|
||||||
import AddCollectionButton from './AddCollectionButton';
|
import AddCollectionButton from './AddCollectionButton';
|
||||||
import PreviewCard from './PreviewCard';
|
import PreviewCard from './PreviewCard';
|
||||||
|
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||||
|
import { User } from 'services/userService';
|
||||||
|
|
||||||
export const CollectionIcon = styled.div`
|
export const CollectionIcon = styled.div`
|
||||||
width: 200px;
|
width: 200px;
|
||||||
|
@ -53,14 +55,18 @@ function CollectionSelector({
|
||||||
if (!attributes) {
|
if (!attributes) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const collectionsOtherThanFrom = collectionsAndTheirLatestFile?.filter(
|
const user: User = getData(LS_KEYS.USER);
|
||||||
(item) => !(item.collection.id === attributes.fromCollection)
|
const personalCollectionsOtherThanFrom =
|
||||||
);
|
collectionsAndTheirLatestFile?.filter(
|
||||||
if (collectionsOtherThanFrom.length === 0) {
|
(item) =>
|
||||||
|
item.collection.id !== attributes.fromCollection &&
|
||||||
|
item.collection.owner.id === user?.id
|
||||||
|
);
|
||||||
|
if (personalCollectionsOtherThanFrom.length === 0) {
|
||||||
props.onHide();
|
props.onHide();
|
||||||
attributes.showNextModal();
|
attributes.showNextModal();
|
||||||
} else {
|
} else {
|
||||||
setCollectionToShow(collectionsOtherThanFrom);
|
setCollectionToShow(personalCollectionsOtherThanFrom);
|
||||||
}
|
}
|
||||||
}, [props.show]);
|
}, [props.show]);
|
||||||
|
|
||||||
|
|
31
src/components/pages/gallery/CollectionSort.tsx
Normal file
31
src/components/pages/gallery/CollectionSort.tsx
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { IconButton } from 'components/Container';
|
||||||
|
import SortIcon from 'components/icons/SortIcon';
|
||||||
|
import React from 'react';
|
||||||
|
import { OverlayTrigger } from 'react-bootstrap';
|
||||||
|
import { COLLECTION_SORT_BY } from 'services/collectionService';
|
||||||
|
import constants from 'utils/strings/constants';
|
||||||
|
import CollectionSortOptions from './CollectionSortOptions';
|
||||||
|
import { IconWithMessage } from './SelectedFileOptions';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
setCollectionSortBy: (sortBy: COLLECTION_SORT_BY) => void;
|
||||||
|
activeSortBy: COLLECTION_SORT_BY;
|
||||||
|
}
|
||||||
|
export default function CollectionSort(props: Props) {
|
||||||
|
const collectionSortOptions = CollectionSortOptions(props);
|
||||||
|
return (
|
||||||
|
<OverlayTrigger
|
||||||
|
rootClose
|
||||||
|
trigger="click"
|
||||||
|
placement="bottom"
|
||||||
|
overlay={collectionSortOptions}>
|
||||||
|
<div>
|
||||||
|
<IconWithMessage message={constants.SORT}>
|
||||||
|
<IconButton style={{ color: '#fff' }}>
|
||||||
|
<SortIcon />
|
||||||
|
</IconButton>
|
||||||
|
</IconWithMessage>
|
||||||
|
</div>
|
||||||
|
</OverlayTrigger>
|
||||||
|
);
|
||||||
|
}
|
69
src/components/pages/gallery/CollectionSortOptions.tsx
Normal file
69
src/components/pages/gallery/CollectionSortOptions.tsx
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import { Label, Value } from 'components/Container';
|
||||||
|
import TickIcon from 'components/icons/TickIcon';
|
||||||
|
import React from 'react';
|
||||||
|
import { ListGroup, Popover, Row } from 'react-bootstrap';
|
||||||
|
import { COLLECTION_SORT_BY } from 'services/collectionService';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import constants from 'utils/strings/constants';
|
||||||
|
import { MenuItem, MenuLink } from './CollectionOptions';
|
||||||
|
|
||||||
|
interface OptionProps {
|
||||||
|
activeSortBy: COLLECTION_SORT_BY;
|
||||||
|
setCollectionSortBy: (sortBy: COLLECTION_SORT_BY) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TickWrapper = styled.span`
|
||||||
|
color: #aaa;
|
||||||
|
margin-left: 5px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SortByOptionCreator =
|
||||||
|
({ setCollectionSortBy, activeSortBy }: OptionProps) =>
|
||||||
|
(props: { sortBy: COLLECTION_SORT_BY; children: any }) =>
|
||||||
|
(
|
||||||
|
<MenuItem>
|
||||||
|
<Row>
|
||||||
|
<Label width="20px">
|
||||||
|
{activeSortBy === props.sortBy && (
|
||||||
|
<TickWrapper>
|
||||||
|
<TickIcon />
|
||||||
|
</TickWrapper>
|
||||||
|
)}
|
||||||
|
</Label>
|
||||||
|
<Value width="165px">
|
||||||
|
<MenuLink
|
||||||
|
onClick={() => setCollectionSortBy(props.sortBy)}
|
||||||
|
variant={
|
||||||
|
activeSortBy === props.sortBy && 'success'
|
||||||
|
}>
|
||||||
|
{props.children}
|
||||||
|
</MenuLink>
|
||||||
|
</Value>
|
||||||
|
</Row>
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
|
||||||
|
const CollectionSortOptions = (props: OptionProps) => {
|
||||||
|
const SortByOption = SortByOptionCreator(props);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover id="collection-sort-options" style={{ borderRadius: '10px' }}>
|
||||||
|
<Popover.Content
|
||||||
|
style={{ padding: 0, border: 'none', width: '185px' }}>
|
||||||
|
<ListGroup style={{ borderRadius: '8px' }}>
|
||||||
|
<SortByOption sortBy={COLLECTION_SORT_BY.LATEST_FILE}>
|
||||||
|
{constants.SORT_BY_LATEST_PHOTO}
|
||||||
|
</SortByOption>
|
||||||
|
<SortByOption sortBy={COLLECTION_SORT_BY.MODIFICATION_TIME}>
|
||||||
|
{constants.SORT_BY_MODIFICATION_TIME}
|
||||||
|
</SortByOption>
|
||||||
|
<SortByOption sortBy={COLLECTION_SORT_BY.NAME}>
|
||||||
|
{constants.SORT_BY_COLLECTION_NAME}
|
||||||
|
</SortByOption>
|
||||||
|
</ListGroup>
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CollectionSortOptions;
|
|
@ -5,7 +5,13 @@ import NavigationButton, {
|
||||||
} from 'components/NavigationButton';
|
} from 'components/NavigationButton';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
|
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
|
||||||
import { Collection, CollectionType } from 'services/collectionService';
|
import {
|
||||||
|
Collection,
|
||||||
|
CollectionAndItsLatestFile,
|
||||||
|
CollectionType,
|
||||||
|
COLLECTION_SORT_BY,
|
||||||
|
sortCollections,
|
||||||
|
} from 'services/collectionService';
|
||||||
import { User } from 'services/userService';
|
import { User } from 'services/userService';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { IMAGE_CONTAINER_MAX_WIDTH } from 'types';
|
import { IMAGE_CONTAINER_MAX_WIDTH } from 'types';
|
||||||
|
@ -14,6 +20,7 @@ import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import { SetCollectionNamerAttributes } from './CollectionNamer';
|
import { SetCollectionNamerAttributes } from './CollectionNamer';
|
||||||
import CollectionOptions from './CollectionOptions';
|
import CollectionOptions from './CollectionOptions';
|
||||||
|
import CollectionSort from './CollectionSort';
|
||||||
import OptionIcon, { OptionIconWrapper } from './OptionIcon';
|
import OptionIcon, { OptionIconWrapper } from './OptionIcon';
|
||||||
|
|
||||||
export const ARCHIVE_SECTION = -1;
|
export const ARCHIVE_SECTION = -1;
|
||||||
|
@ -21,6 +28,7 @@ export const ALL_SECTION = 0;
|
||||||
|
|
||||||
interface CollectionProps {
|
interface CollectionProps {
|
||||||
collections: Collection[];
|
collections: Collection[];
|
||||||
|
collectionAndTheirLatestFile: CollectionAndItsLatestFile[];
|
||||||
activeCollection?: number;
|
activeCollection?: number;
|
||||||
setActiveCollection: (id?: number) => void;
|
setActiveCollection: (id?: number) => void;
|
||||||
setDialogMessage: SetDialogMessage;
|
setDialogMessage: SetDialogMessage;
|
||||||
|
@ -31,12 +39,11 @@ interface CollectionProps {
|
||||||
collectionFilesCount: Map<number, number>;
|
collectionFilesCount: Map<number, number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Container = styled.div`
|
const CollectionContainer = styled.div`
|
||||||
margin: 10px auto;
|
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: calc(100% - 80px);
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
|
|
||||||
|
@ -54,6 +61,14 @@ const Wrapper = styled.div`
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const CollectionBar = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
margin: 10px auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
`;
|
||||||
|
|
||||||
const Chip = styled.button<{ active: boolean }>`
|
const Chip = styled.button<{ active: boolean }>`
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
@ -84,6 +99,8 @@ export default function Collections(props: CollectionProps) {
|
||||||
scrollWidth?: number;
|
scrollWidth?: number;
|
||||||
clientWidth?: number;
|
clientWidth?: number;
|
||||||
}>({});
|
}>({});
|
||||||
|
const [collectionSortBy, setCollectionSortBy] =
|
||||||
|
useState<COLLECTION_SORT_BY>(COLLECTION_SORT_BY.MODIFICATION_TIME);
|
||||||
|
|
||||||
const updateScrollObj = () => {
|
const updateScrollObj = () => {
|
||||||
if (collectionRef.current) {
|
if (collectionRef.current) {
|
||||||
|
@ -165,81 +182,96 @@ export default function Collections(props: CollectionProps) {
|
||||||
)}
|
)}
|
||||||
syncWithRemote={props.syncWithRemote}
|
syncWithRemote={props.syncWithRemote}
|
||||||
/>
|
/>
|
||||||
<Container>
|
<CollectionBar>
|
||||||
{scrollObj.scrollLeft > 0 && (
|
<CollectionContainer>
|
||||||
<NavigationButton
|
{scrollObj.scrollLeft > 0 && (
|
||||||
scrollDirection={SCROLL_DIRECTION.LEFT}
|
<NavigationButton
|
||||||
onClick={scrollCollection(SCROLL_DIRECTION.LEFT)}
|
scrollDirection={SCROLL_DIRECTION.LEFT}
|
||||||
/>
|
onClick={scrollCollection(
|
||||||
)}
|
SCROLL_DIRECTION.LEFT
|
||||||
<Wrapper ref={collectionRef} onScroll={updateScrollObj}>
|
)}
|
||||||
<Chip
|
|
||||||
active={activeCollection === ALL_SECTION}
|
|
||||||
onClick={clickHandler(ALL_SECTION)}>
|
|
||||||
{constants.ALL}
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'inline-block',
|
|
||||||
width: '24px',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</Chip>
|
)}
|
||||||
{collections?.map((item) => (
|
<Wrapper ref={collectionRef} onScroll={updateScrollObj}>
|
||||||
<OverlayTrigger
|
<Chip
|
||||||
key={item.id}
|
active={activeCollection === ALL_SECTION}
|
||||||
placement="top"
|
onClick={clickHandler(ALL_SECTION)}>
|
||||||
delay={{ show: 250, hide: 400 }}
|
{constants.ALL}
|
||||||
overlay={renderTooltip(item.id)}>
|
<div
|
||||||
<Chip
|
style={{
|
||||||
active={activeCollection === item.id}
|
display: 'inline-block',
|
||||||
onClick={clickHandler(item.id)}>
|
width: '24px',
|
||||||
{item.name}
|
}}
|
||||||
{item.type !== CollectionType.favorites &&
|
/>
|
||||||
item.owner.id === user?.id ? (
|
</Chip>
|
||||||
<OverlayTrigger
|
{sortCollections(
|
||||||
rootClose
|
collections,
|
||||||
trigger="click"
|
props.collectionAndTheirLatestFile,
|
||||||
placement="bottom"
|
collectionSortBy
|
||||||
overlay={collectionOptions}>
|
).map((item) => (
|
||||||
<OptionIcon
|
<OverlayTrigger
|
||||||
onClick={() =>
|
key={item.id}
|
||||||
setSelectedCollectionID(
|
placement="top"
|
||||||
item.id
|
delay={{ show: 250, hide: 400 }}
|
||||||
)
|
overlay={renderTooltip(item.id)}>
|
||||||
}
|
<Chip
|
||||||
|
active={activeCollection === item.id}
|
||||||
|
onClick={clickHandler(item.id)}>
|
||||||
|
{item.name}
|
||||||
|
{item.type !==
|
||||||
|
CollectionType.favorites &&
|
||||||
|
item.owner.id === user?.id ? (
|
||||||
|
<OverlayTrigger
|
||||||
|
rootClose
|
||||||
|
trigger="click"
|
||||||
|
placement="bottom"
|
||||||
|
overlay={collectionOptions}>
|
||||||
|
<OptionIcon
|
||||||
|
onClick={() =>
|
||||||
|
setSelectedCollectionID(
|
||||||
|
item.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</OverlayTrigger>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'inline-block',
|
||||||
|
width: '24px',
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</OverlayTrigger>
|
)}
|
||||||
) : (
|
</Chip>
|
||||||
<div
|
</OverlayTrigger>
|
||||||
style={{
|
))}
|
||||||
display: 'inline-block',
|
<Chip
|
||||||
width: '24px',
|
active={activeCollection === ARCHIVE_SECTION}
|
||||||
}}
|
onClick={clickHandler(ARCHIVE_SECTION)}>
|
||||||
/>
|
{constants.ARCHIVE}
|
||||||
)}
|
<div
|
||||||
</Chip>
|
style={{
|
||||||
</OverlayTrigger>
|
display: 'inline-block',
|
||||||
))}
|
width: '24px',
|
||||||
<Chip
|
}}
|
||||||
active={activeCollection === ARCHIVE_SECTION}
|
/>
|
||||||
onClick={clickHandler(ARCHIVE_SECTION)}>
|
</Chip>
|
||||||
{constants.ARCHIVE}
|
</Wrapper>
|
||||||
<div
|
{scrollObj.scrollLeft <
|
||||||
style={{
|
scrollObj.scrollWidth - scrollObj.clientWidth && (
|
||||||
display: 'inline-block',
|
<NavigationButton
|
||||||
width: '24px',
|
scrollDirection={SCROLL_DIRECTION.RIGHT}
|
||||||
}}
|
onClick={scrollCollection(
|
||||||
|
SCROLL_DIRECTION.RIGHT
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</Chip>
|
)}
|
||||||
</Wrapper>
|
</CollectionContainer>
|
||||||
{scrollObj.scrollLeft <
|
<CollectionSort
|
||||||
scrollObj.scrollWidth - scrollObj.clientWidth && (
|
setCollectionSortBy={setCollectionSortBy}
|
||||||
<NavigationButton
|
activeSortBy={collectionSortBy}
|
||||||
scrollDirection={SCROLL_DIRECTION.RIGHT}
|
/>
|
||||||
onClick={scrollCollection(SCROLL_DIRECTION.RIGHT)}
|
</CollectionBar>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Container>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,10 +6,10 @@ export enum ButtonVariant {
|
||||||
secondary = 'secondary',
|
secondary = 'secondary',
|
||||||
warning = 'warning',
|
warning = 'warning',
|
||||||
}
|
}
|
||||||
type Props = React.PropsWithChildren<{
|
export type LinkButtonProps = React.PropsWithChildren<{
|
||||||
onClick: any;
|
onClick: () => void;
|
||||||
variant?: string;
|
variant?: string;
|
||||||
style?: any;
|
style?: React.CSSProperties;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export function getVariantColor(variant: string) {
|
export function getVariantColor(variant: string) {
|
||||||
|
@ -26,7 +26,7 @@ export function getVariantColor(variant: string) {
|
||||||
return '#d1d1d1';
|
return '#d1d1d1';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default function LinkButton(props: Props) {
|
export default function LinkButton(props: LinkButtonProps) {
|
||||||
return (
|
return (
|
||||||
<h5
|
<h5
|
||||||
style={{
|
style={{
|
||||||
|
|
|
@ -51,6 +51,14 @@ const SelectionContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const IconWithMessage = (props) => (
|
||||||
|
<OverlayTrigger
|
||||||
|
placement="bottom"
|
||||||
|
overlay={<p style={{ zIndex: 1002 }}>{props.message}</p>}>
|
||||||
|
{props.children}
|
||||||
|
</OverlayTrigger>
|
||||||
|
);
|
||||||
|
|
||||||
const SelectedFileOptions = ({
|
const SelectedFileOptions = ({
|
||||||
addToCollectionHelper,
|
addToCollectionHelper,
|
||||||
moveToCollectionHelper,
|
moveToCollectionHelper,
|
||||||
|
@ -94,14 +102,6 @@ const SelectedFileOptions = ({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const IconWithMessage = (props) => (
|
|
||||||
<OverlayTrigger
|
|
||||||
placement="bottom"
|
|
||||||
overlay={<p style={{ zIndex: 1002 }}>{props.message}</p>}>
|
|
||||||
{props.children}
|
|
||||||
</OverlayTrigger>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectionBar>
|
<SelectionBar>
|
||||||
<SelectionContainer>
|
<SelectionContainer>
|
||||||
|
|
|
@ -526,6 +526,7 @@ export default function Gallery() {
|
||||||
/>
|
/>
|
||||||
<Collections
|
<Collections
|
||||||
collections={collections}
|
collections={collections}
|
||||||
|
collectionAndTheirLatestFile={collectionsAndTheirLatestFile}
|
||||||
searchMode={searchMode}
|
searchMode={searchMode}
|
||||||
activeCollection={activeCollection}
|
activeCollection={activeCollection}
|
||||||
setActiveCollection={setActiveCollection}
|
setActiveCollection={setActiveCollection}
|
||||||
|
|
|
@ -69,6 +69,12 @@ export interface CollectionAndItsLatestFile {
|
||||||
file: File;
|
file: File;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum COLLECTION_SORT_BY {
|
||||||
|
LATEST_FILE,
|
||||||
|
MODIFICATION_TIME,
|
||||||
|
NAME,
|
||||||
|
}
|
||||||
|
|
||||||
const getCollectionWithSecrets = async (
|
const getCollectionWithSecrets = async (
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
masterKey: string
|
masterKey: string
|
||||||
|
@ -219,13 +225,9 @@ export const getCollectionsAndTheirLatestFile = (
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const collectionsAndTheirLatestFile: CollectionAndItsLatestFile[] = [];
|
const collectionsAndTheirLatestFile: CollectionAndItsLatestFile[] = [];
|
||||||
const userID = getData(LS_KEYS.USER)?.id;
|
|
||||||
|
|
||||||
for (const collection of collections) {
|
for (const collection of collections) {
|
||||||
if (
|
if (collection.type === CollectionType.favorites) {
|
||||||
collection.owner.id !== userID ||
|
|
||||||
collection.type === CollectionType.favorites
|
|
||||||
) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
collectionsAndTheirLatestFile.push({
|
collectionsAndTheirLatestFile.push({
|
||||||
|
@ -591,3 +593,62 @@ export const getNonEmptyCollections = (
|
||||||
nonEmptyCollectionsIds.has(collection.id)
|
nonEmptyCollectionsIds.has(collection.id)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function sortCollections(
|
||||||
|
collections: Collection[],
|
||||||
|
collectionAndTheirLatestFile: CollectionAndItsLatestFile[],
|
||||||
|
sortBy: COLLECTION_SORT_BY
|
||||||
|
) {
|
||||||
|
return collections.sort((collectionA, collectionB) => {
|
||||||
|
switch (sortBy) {
|
||||||
|
case COLLECTION_SORT_BY.LATEST_FILE:
|
||||||
|
return compareCollectionsLatestFile(
|
||||||
|
collectionAndTheirLatestFile,
|
||||||
|
collectionA,
|
||||||
|
collectionB
|
||||||
|
);
|
||||||
|
case COLLECTION_SORT_BY.MODIFICATION_TIME:
|
||||||
|
return collectionB.updationTime - collectionA.updationTime;
|
||||||
|
case COLLECTION_SORT_BY.NAME:
|
||||||
|
return collectionA.name.localeCompare(collectionB.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareCollectionsLatestFile(
|
||||||
|
collectionAndTheirLatestFile: CollectionAndItsLatestFile[],
|
||||||
|
collectionA: Collection,
|
||||||
|
collectionB: Collection
|
||||||
|
) {
|
||||||
|
const CollectionALatestFile = getCollectionLatestFile(
|
||||||
|
collectionAndTheirLatestFile,
|
||||||
|
collectionA
|
||||||
|
);
|
||||||
|
const CollectionBLatestFile = getCollectionLatestFile(
|
||||||
|
collectionAndTheirLatestFile,
|
||||||
|
collectionB
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
CollectionBLatestFile.updationTime - CollectionALatestFile.updationTime
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCollectionLatestFile(
|
||||||
|
collectionAndTheirLatestFile: CollectionAndItsLatestFile[],
|
||||||
|
collection: Collection
|
||||||
|
) {
|
||||||
|
const collectionAndItsLatestFile = collectionAndTheirLatestFile.filter(
|
||||||
|
(collectionAndItsLatestFile) =>
|
||||||
|
collectionAndItsLatestFile.collection.id === collection.id
|
||||||
|
);
|
||||||
|
if (collectionAndItsLatestFile.length === 1) {
|
||||||
|
return collectionAndItsLatestFile[0].file;
|
||||||
|
} else {
|
||||||
|
logError(
|
||||||
|
Error('collection missing from collectionLatestFile list'),
|
||||||
|
''
|
||||||
|
);
|
||||||
|
return { updationTime: 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -548,6 +548,10 @@ const englishConstants = {
|
||||||
UNARCHIVE: 'un-archive',
|
UNARCHIVE: 'un-archive',
|
||||||
MOVE: 'move',
|
MOVE: 'move',
|
||||||
ADD: 'add',
|
ADD: 'add',
|
||||||
|
SORT: 'sort',
|
||||||
|
SORT_BY_LATEST_PHOTO: 'most recent photo',
|
||||||
|
SORT_BY_MODIFICATION_TIME: 'last modified',
|
||||||
|
SORT_BY_COLLECTION_NAME: 'album title',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default englishConstants;
|
export default englishConstants;
|
||||||
|
|
Loading…
Reference in a new issue