commit
2173484e1d
|
@ -10,9 +10,13 @@ interface Iprops {
|
|||
action: CollectionActions,
|
||||
loader?: boolean
|
||||
) => (...args: any[]) => Promise<void>;
|
||||
downloadOptionText?: string;
|
||||
}
|
||||
|
||||
export function FavoritiesCollectionOption({ handleCollectionAction }: Iprops) {
|
||||
export function OnlyDownloadCollectionOption({
|
||||
handleCollectionAction,
|
||||
downloadOptionText = constants.DOWNLOAD,
|
||||
}: Iprops) {
|
||||
return (
|
||||
<OverflowMenuOption
|
||||
startIcon={<FileDownloadOutlinedIcon />}
|
||||
|
@ -20,7 +24,7 @@ export function FavoritiesCollectionOption({ handleCollectionAction }: Iprops) {
|
|||
CollectionActions.CONFIRM_DOWNLOAD,
|
||||
false
|
||||
)}>
|
||||
{constants.DOWNLOAD_COLLECTION}
|
||||
{downloadOptionText}
|
||||
</OverflowMenuOption>
|
||||
);
|
||||
}
|
|
@ -25,6 +25,7 @@ export function QuickOptions({
|
|||
{!(
|
||||
collectionSummaryType === CollectionSummaryType.trash ||
|
||||
collectionSummaryType === CollectionSummaryType.favorites ||
|
||||
collectionSummaryType === CollectionSummaryType.uncategorized ||
|
||||
collectionSummaryType === CollectionSummaryType.incomingShare
|
||||
) && (
|
||||
<Tooltip
|
||||
|
@ -51,7 +52,10 @@ export function QuickOptions({
|
|||
title={
|
||||
collectionSummaryType ===
|
||||
CollectionSummaryType.favorites
|
||||
? constants.DOWNLOAD_FAVOURITES
|
||||
? constants.DOWNLOAD_FAVORITES
|
||||
: collectionSummaryType ===
|
||||
CollectionSummaryType.uncategorized
|
||||
? constants.DOWNLOAD_UNCATEGORIZED
|
||||
: constants.DOWNLOAD_COLLECTION
|
||||
}>
|
||||
<IconButton
|
||||
|
|
|
@ -18,7 +18,7 @@ import OverflowMenu from 'components/OverflowMenu/menu';
|
|||
import { CollectionSummaryType } from 'constants/collection';
|
||||
import { TrashCollectionOption } from './TrashCollectionOption';
|
||||
import { SharedCollectionOption } from './SharedCollectionOption';
|
||||
import { FavoritiesCollectionOption } from './FavoritiesCollectionOption';
|
||||
import { OnlyDownloadCollectionOption } from './OnlyDownloadCollectionOption';
|
||||
import { QuickOptions } from './QuickOptions';
|
||||
import MoreHoriz from '@mui/icons-material/MoreHoriz';
|
||||
import { HorizontalFlex } from 'components/Container';
|
||||
|
@ -39,7 +39,8 @@ export enum CollectionActions {
|
|||
ARCHIVE,
|
||||
UNARCHIVE,
|
||||
CONFIRM_DELETE,
|
||||
DELETE,
|
||||
DELETE_WITH_FILES,
|
||||
DELETE_BUT_KEEP_FILES,
|
||||
SHOW_SHARE_DIALOG,
|
||||
CONFIRM_EMPTY_TRASH,
|
||||
EMPTY_TRASH,
|
||||
|
@ -87,8 +88,11 @@ const CollectionOptions = (props: CollectionOptionsProps) => {
|
|||
case CollectionActions.CONFIRM_DELETE:
|
||||
callback = confirmDeleteCollection;
|
||||
break;
|
||||
case CollectionActions.DELETE:
|
||||
callback = deleteCollection;
|
||||
case CollectionActions.DELETE_WITH_FILES:
|
||||
callback = deleteCollectionAlongWithFiles;
|
||||
break;
|
||||
case CollectionActions.DELETE_BUT_KEEP_FILES:
|
||||
callback = deleteCollectionButKeepFiles;
|
||||
break;
|
||||
case CollectionActions.SHOW_SHARE_DIALOG:
|
||||
callback = showCollectionShareModal;
|
||||
|
@ -137,8 +141,13 @@ const CollectionOptions = (props: CollectionOptionsProps) => {
|
|||
}
|
||||
};
|
||||
|
||||
const deleteCollection = async () => {
|
||||
await CollectionAPI.deleteCollection(activeCollection.id);
|
||||
const deleteCollectionAlongWithFiles = async () => {
|
||||
await CollectionAPI.deleteCollection(activeCollection.id, false);
|
||||
redirectToAll();
|
||||
};
|
||||
|
||||
const deleteCollectionButKeepFiles = async () => {
|
||||
await CollectionAPI.deleteCollection(activeCollection.id, true);
|
||||
redirectToAll();
|
||||
};
|
||||
|
||||
|
@ -177,12 +186,21 @@ const CollectionOptions = (props: CollectionOptionsProps) => {
|
|||
const confirmDeleteCollection = () => {
|
||||
setDialogMessage({
|
||||
title: constants.DELETE_COLLECTION_TITLE,
|
||||
content: constants.DELETE_COLLECTION_MESSAGE,
|
||||
content: constants.DELETE_COLLECTION_MESSAGE(),
|
||||
proceed: {
|
||||
text: constants.DELETE_COLLECTION,
|
||||
action: handleCollectionAction(CollectionActions.DELETE),
|
||||
text: constants.DELETE_PHOTOS,
|
||||
action: handleCollectionAction(
|
||||
CollectionActions.DELETE_WITH_FILES
|
||||
),
|
||||
variant: 'danger',
|
||||
},
|
||||
secondary: {
|
||||
text: constants.KEEP_PHOTOS,
|
||||
action: handleCollectionAction(
|
||||
CollectionActions.DELETE_BUT_KEEP_FILES
|
||||
),
|
||||
variant: 'primary',
|
||||
},
|
||||
close: {
|
||||
text: constants.CANCEL,
|
||||
},
|
||||
|
@ -250,8 +268,15 @@ const CollectionOptions = (props: CollectionOptionsProps) => {
|
|||
/>
|
||||
) : collectionSummaryType ===
|
||||
CollectionSummaryType.favorites ? (
|
||||
<FavoritiesCollectionOption
|
||||
<OnlyDownloadCollectionOption
|
||||
handleCollectionAction={handleCollectionAction}
|
||||
downloadOptionText={constants.DOWNLOAD_FAVORITES}
|
||||
/>
|
||||
) : collectionSummaryType ===
|
||||
CollectionSummaryType.uncategorized ? (
|
||||
<OnlyDownloadCollectionOption
|
||||
handleCollectionAction={handleCollectionAction}
|
||||
downloadOptionText={constants.DOWNLOAD_UNCATEGORIZED}
|
||||
/>
|
||||
) : collectionSummaryType ===
|
||||
CollectionSummaryType.incomingShare ? (
|
||||
|
|
|
@ -96,6 +96,18 @@ export default function DialogBox({
|
|||
{attributes.proceed.text}
|
||||
</Button>
|
||||
)}
|
||||
{attributes.secondary && (
|
||||
<Button
|
||||
size="large"
|
||||
color={attributes.secondary?.variant}
|
||||
onClick={() => {
|
||||
attributes.secondary.action();
|
||||
onClose();
|
||||
}}
|
||||
disabled={attributes.secondary.disabled}>
|
||||
{attributes.secondary.text}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
</DialogActions>
|
||||
)}
|
||||
|
|
|
@ -31,7 +31,7 @@ export function OverflowMenuOption({
|
|||
<MenuItem
|
||||
onClick={handleClick}
|
||||
sx={{
|
||||
width: 220,
|
||||
minWidth: 220,
|
||||
color: (theme) => theme.palette[color].main,
|
||||
padding: 1.5,
|
||||
'& .MuiSvgIcon-root': {
|
||||
|
|
|
@ -66,7 +66,7 @@ interface Props {
|
|||
deletedFileIds?: Set<number>;
|
||||
setDeletedFileIds?: (value: Set<number>) => void;
|
||||
activeCollection: number;
|
||||
isSharedCollection?: boolean;
|
||||
isIncomingSharedCollection?: boolean;
|
||||
enableDownload?: boolean;
|
||||
isDeduplicating?: boolean;
|
||||
resetSearch?: () => void;
|
||||
|
@ -95,7 +95,7 @@ const PhotoFrame = ({
|
|||
deletedFileIds,
|
||||
setDeletedFileIds,
|
||||
activeCollection,
|
||||
isSharedCollection,
|
||||
isIncomingSharedCollection,
|
||||
enableDownload,
|
||||
isDeduplicating,
|
||||
}: Props) => {
|
||||
|
@ -179,7 +179,10 @@ const PhotoFrame = ({
|
|||
return false;
|
||||
}
|
||||
|
||||
if (isSharedFile(user, item) && !isSharedCollection) {
|
||||
if (
|
||||
isSharedFile(user, item) &&
|
||||
!isIncomingSharedCollection
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (activeCollection === TRASH_SECTION && !item.isTrashed) {
|
||||
|
@ -501,7 +504,7 @@ const PhotoFrame = ({
|
|||
file={files[index]}
|
||||
updateURL={updateURL(files[index].id)}
|
||||
onClick={onThumbnailClick(index)}
|
||||
selectable={!isSharedCollection}
|
||||
selectable={!isIncomingSharedCollection}
|
||||
onSelect={handleSelect(files[index].id, index)}
|
||||
selected={
|
||||
selected.collectionID === activeCollection &&
|
||||
|
@ -683,7 +686,10 @@ const PhotoFrame = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
{!isFirstLoad && files.length === 0 && !isInSearchMode ? (
|
||||
{!isFirstLoad &&
|
||||
files.length === 0 &&
|
||||
!isInSearchMode &&
|
||||
activeCollection === ALL_SECTION ? (
|
||||
<EmptyScreen openUploader={openUploader} />
|
||||
) : (
|
||||
<Container>
|
||||
|
@ -713,7 +719,7 @@ const PhotoFrame = ({
|
|||
favItemIds={favItemIds}
|
||||
deletedFileIds={deletedFileIds}
|
||||
setDeletedFileIds={setDeletedFileIds}
|
||||
isSharedCollection={isSharedCollection}
|
||||
isIncomingSharedCollection={isIncomingSharedCollection}
|
||||
isTrashCollection={activeCollection === TRASH_SECTION}
|
||||
enableDownload={enableDownload}
|
||||
isSourceLoaded={isSourceLoaded}
|
||||
|
|
|
@ -68,7 +68,7 @@ interface Iprops {
|
|||
favItemIds: Set<number>;
|
||||
deletedFileIds: Set<number>;
|
||||
setDeletedFileIds?: (value: Set<number>) => void;
|
||||
isSharedCollection: boolean;
|
||||
isIncomingSharedCollection: boolean;
|
||||
isTrashCollection: boolean;
|
||||
enableDownload: boolean;
|
||||
isSourceLoaded: boolean;
|
||||
|
@ -393,7 +393,11 @@ function PhotoViewer(props: Iprops) {
|
|||
};
|
||||
|
||||
const confirmTrashFile = (file: EnteFile) => {
|
||||
if (!file || props.isSharedCollection || props.isTrashCollection) {
|
||||
if (
|
||||
!file ||
|
||||
props.isIncomingSharedCollection ||
|
||||
props.isTrashCollection
|
||||
) {
|
||||
return;
|
||||
}
|
||||
appContext.setDialogMessage(getTrashFileMessage(() => trashFile(file)));
|
||||
|
@ -571,7 +575,7 @@ function PhotoViewer(props: Iprops) {
|
|||
<ContentCopy fontSize="small" />
|
||||
</button>
|
||||
)}
|
||||
{!props.isSharedCollection &&
|
||||
{!props.isIncomingSharedCollection &&
|
||||
!props.isTrashCollection && (
|
||||
<button
|
||||
className="pswp__button pswp__button--custom"
|
||||
|
@ -593,7 +597,7 @@ function PhotoViewer(props: Iprops) {
|
|||
title={constants.TOGGLE_FULLSCREEN}
|
||||
/>
|
||||
|
||||
{!props.isSharedCollection && (
|
||||
{!props.isIncomingSharedCollection && (
|
||||
<button
|
||||
className="pswp__button pswp__button--custom"
|
||||
title={constants.INFO_OPTION}
|
||||
|
@ -601,7 +605,7 @@ function PhotoViewer(props: Iprops) {
|
|||
<InfoIcon fontSize="small" />
|
||||
</button>
|
||||
)}
|
||||
{!props.isSharedCollection &&
|
||||
{!props.isIncomingSharedCollection &&
|
||||
!props.isTrashCollection && (
|
||||
<button
|
||||
title={
|
||||
|
@ -652,7 +656,7 @@ function PhotoViewer(props: Iprops) {
|
|||
</div>
|
||||
<FileInfo
|
||||
isTrashCollection={props.isTrashCollection}
|
||||
shouldDisableEdits={props.isSharedCollection}
|
||||
shouldDisableEdits={props.isIncomingSharedCollection}
|
||||
showInfo={showInfo}
|
||||
handleCloseInfo={handleCloseInfo}
|
||||
file={photoSwipe?.currItem as EnteFile}
|
||||
|
|
|
@ -20,12 +20,11 @@ const ShortcutButton: FC<ButtonProps<'button', Iprops>> = ({
|
|||
sx={{ fontWeight: 'normal' }}
|
||||
{...props}>
|
||||
{label}
|
||||
{count > 0 && (
|
||||
<Box sx={{ color: 'text.secondary' }}>
|
||||
<DotSeparator />
|
||||
{count}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Box sx={{ color: 'text.secondary' }}>
|
||||
<DotSeparator />
|
||||
{count}
|
||||
</Box>
|
||||
</SidebarButton>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
import React, { useContext } from 'react';
|
||||
import React, { useContext, useState, useEffect } from 'react';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { GalleryContext } from 'pages/gallery';
|
||||
import { ARCHIVE_SECTION, TRASH_SECTION } from 'constants/collection';
|
||||
import {
|
||||
ARCHIVE_SECTION,
|
||||
DUMMY_UNCATEGORIZED_SECTION,
|
||||
TRASH_SECTION,
|
||||
} from 'constants/collection';
|
||||
import { CollectionSummaries } from 'types/collection';
|
||||
import ShortcutButton from './ShortcutButton';
|
||||
import DeleteOutline from '@mui/icons-material/DeleteOutline';
|
||||
import ArchiveOutlined from '@mui/icons-material/ArchiveOutlined';
|
||||
import CategoryIcon from '@mui/icons-material/Category';
|
||||
import { getUncategorizedCollection } from 'services/collectionService';
|
||||
interface Iprops {
|
||||
closeSidebar: () => void;
|
||||
collectionSummaries: CollectionSummaries;
|
||||
|
@ -16,6 +22,24 @@ export default function ShortcutSection({
|
|||
collectionSummaries,
|
||||
}: Iprops) {
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
const [uncategorizedCollectionId, setUncategorizedCollectionID] =
|
||||
useState<number>();
|
||||
useEffect(() => {
|
||||
const main = async () => {
|
||||
const unCategorizedCollection = await getUncategorizedCollection();
|
||||
if (unCategorizedCollection) {
|
||||
setUncategorizedCollectionID(unCategorizedCollection.id);
|
||||
} else {
|
||||
setUncategorizedCollectionID(DUMMY_UNCATEGORIZED_SECTION);
|
||||
}
|
||||
};
|
||||
main();
|
||||
}, []);
|
||||
|
||||
const openUncategorizedSection = () => {
|
||||
galleryContext.setActiveCollection(uncategorizedCollectionId);
|
||||
closeSidebar();
|
||||
};
|
||||
|
||||
const openTrashSection = () => {
|
||||
galleryContext.setActiveCollection(TRASH_SECTION);
|
||||
|
@ -26,9 +50,17 @@ export default function ShortcutSection({
|
|||
galleryContext.setActiveCollection(ARCHIVE_SECTION);
|
||||
closeSidebar();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ShortcutButton
|
||||
startIcon={<CategoryIcon />}
|
||||
label={constants.UNCATEGORIZED}
|
||||
onClick={openUncategorizedSection}
|
||||
count={
|
||||
collectionSummaries.get(uncategorizedCollectionId)
|
||||
?.fileCount
|
||||
}
|
||||
/>
|
||||
<ShortcutButton
|
||||
startIcon={<DeleteOutline />}
|
||||
label={constants.TRASH}
|
||||
|
|
|
@ -40,6 +40,7 @@ interface Props {
|
|||
unArchiveFilesHelper: () => void;
|
||||
activeCollection: number;
|
||||
isFavoriteCollection: boolean;
|
||||
isUncategorizedCollection: boolean;
|
||||
}
|
||||
|
||||
const SelectedFileOptions = ({
|
||||
|
@ -58,6 +59,7 @@ const SelectedFileOptions = ({
|
|||
unArchiveFilesHelper,
|
||||
activeCollection,
|
||||
isFavoriteCollection,
|
||||
isUncategorizedCollection,
|
||||
}: Props) => {
|
||||
const { setDialogMessage } = useContext(AppContext);
|
||||
const addToCollection = () =>
|
||||
|
@ -94,13 +96,13 @@ const SelectedFileOptions = ({
|
|||
|
||||
const removeFromCollectionHandler = () =>
|
||||
setDialogMessage({
|
||||
title: constants.CONFIRM_REMOVE,
|
||||
title: constants.REMOVE_FROM_COLLECTION,
|
||||
content: constants.CONFIRM_REMOVE_MESSAGE(),
|
||||
|
||||
proceed: {
|
||||
action: removeFromCollectionHelper,
|
||||
text: constants.REMOVE,
|
||||
variant: 'danger',
|
||||
text: constants.YES_REMOVE,
|
||||
variant: 'primary',
|
||||
},
|
||||
close: { text: constants.CANCEL },
|
||||
});
|
||||
|
@ -138,6 +140,19 @@ const SelectedFileOptions = ({
|
|||
</IconButton>
|
||||
</Tooltip>
|
||||
</>
|
||||
) : isUncategorizedCollection ? (
|
||||
<>
|
||||
<Tooltip title={constants.MOVE}>
|
||||
<IconButton onClick={moveToCollection}>
|
||||
<MoveIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={constants.DELETE}>
|
||||
<IconButton onClick={trashHandler}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Tooltip title={constants.FIX_CREATION_TIME}>
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
export const ARCHIVE_SECTION = -1;
|
||||
export const TRASH_SECTION = -2;
|
||||
export const DUMMY_UNCATEGORIZED_SECTION = -3;
|
||||
export const ALL_SECTION = 0;
|
||||
|
||||
export enum CollectionType {
|
||||
folder = 'folder',
|
||||
favorites = 'favorites',
|
||||
album = 'album',
|
||||
uncategorized = 'uncategorized',
|
||||
}
|
||||
|
||||
export enum CollectionSummaryType {
|
||||
|
@ -14,6 +15,7 @@ export enum CollectionSummaryType {
|
|||
album = 'album',
|
||||
archive = 'archive',
|
||||
trash = 'trash',
|
||||
uncategorized = 'uncategorized',
|
||||
all = 'all',
|
||||
outgoingShare = 'outgoingShare',
|
||||
incomingShare = 'incomingShare',
|
||||
|
@ -26,6 +28,9 @@ export enum COLLECTION_SORT_BY {
|
|||
UPDATION_TIME_DESCENDING,
|
||||
}
|
||||
|
||||
export const UNCATEGORIZED_COLLECTION_NAME = 'Uncategorized';
|
||||
export const FAVORITE_COLLECTION_NAME = 'Favorites';
|
||||
|
||||
export const COLLECTION_SHARE_DEFAULT_VALID_DURATION =
|
||||
10 * 24 * 60 * 60 * 1000 * 1000;
|
||||
export const COLLECTION_SHARE_DEFAULT_DEVICE_LIMIT = 4;
|
||||
|
@ -41,12 +46,14 @@ export const COLLECTION_SORT_ORDER = new Map([
|
|||
[CollectionSummaryType.archived, 2],
|
||||
[CollectionSummaryType.archive, 3],
|
||||
[CollectionSummaryType.trash, 4],
|
||||
[CollectionSummaryType.uncategorized, 4],
|
||||
]);
|
||||
|
||||
export const SYSTEM_COLLECTION_TYPES = new Set([
|
||||
CollectionSummaryType.all,
|
||||
CollectionSummaryType.archive,
|
||||
CollectionSummaryType.trash,
|
||||
CollectionSummaryType.uncategorized,
|
||||
]);
|
||||
|
||||
export const UPLOAD_NOT_ALLOWED_COLLECTION_TYPES = new Set([
|
||||
|
@ -54,6 +61,7 @@ export const UPLOAD_NOT_ALLOWED_COLLECTION_TYPES = new Set([
|
|||
CollectionSummaryType.archive,
|
||||
CollectionSummaryType.incomingShare,
|
||||
CollectionSummaryType.trash,
|
||||
CollectionSummaryType.uncategorized,
|
||||
]);
|
||||
|
||||
export const OPTIONS_NOT_HAVING_COLLECTION_TYPES = new Set([
|
||||
|
@ -64,4 +72,5 @@ export const OPTIONS_NOT_HAVING_COLLECTION_TYPES = new Set([
|
|||
export const HIDE_FROM_COLLECTION_BAR_TYPES = new Set([
|
||||
CollectionSummaryType.trash,
|
||||
CollectionSummaryType.archive,
|
||||
CollectionSummaryType.uncategorized,
|
||||
]);
|
||||
|
|
|
@ -60,18 +60,19 @@ import Uploader from 'components/Upload/Uploader';
|
|||
import {
|
||||
ALL_SECTION,
|
||||
ARCHIVE_SECTION,
|
||||
CollectionSummaryType,
|
||||
CollectionType,
|
||||
DUMMY_UNCATEGORIZED_SECTION,
|
||||
TRASH_SECTION,
|
||||
UNCATEGORIZED_COLLECTION_NAME,
|
||||
} from 'constants/collection';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { CustomError, ServerErrorCodes } from 'utils/error';
|
||||
import { PAGES } from 'constants/pages';
|
||||
import {
|
||||
COLLECTION_OPS_TYPE,
|
||||
isSharedCollection,
|
||||
handleCollectionOps,
|
||||
getSelectedCollection,
|
||||
isFavoriteCollection,
|
||||
getArchivedCollections,
|
||||
hasNonSystemCollections,
|
||||
} from 'utils/collection';
|
||||
|
@ -326,6 +327,8 @@ export default function Gallery() {
|
|||
collectionURL += constants.ARCHIVE;
|
||||
} else if (activeCollection === TRASH_SECTION) {
|
||||
collectionURL += constants.TRASH;
|
||||
} else if (activeCollection === DUMMY_UNCATEGORIZED_SECTION) {
|
||||
collectionURL += UNCATEGORIZED_COLLECTION_NAME;
|
||||
} else {
|
||||
collectionURL += activeCollection;
|
||||
}
|
||||
|
@ -408,7 +411,7 @@ export default function Gallery() {
|
|||
const archivedCollections = getArchivedCollections(collections);
|
||||
setArchivedCollections(archivedCollections);
|
||||
|
||||
const collectionSummaries = getCollectionSummaries(
|
||||
const collectionSummaries = await getCollectionSummaries(
|
||||
user,
|
||||
collections,
|
||||
files,
|
||||
|
@ -724,10 +727,10 @@ export default function Gallery() {
|
|||
deletedFileIds={deletedFileIds}
|
||||
setDeletedFileIds={setDeletedFileIds}
|
||||
activeCollection={activeCollection}
|
||||
isSharedCollection={isSharedCollection(
|
||||
activeCollection,
|
||||
collections
|
||||
)}
|
||||
isIncomingSharedCollection={
|
||||
collectionSummaries.get(activeCollection)?.type ===
|
||||
CollectionSummaryType.incomingShare
|
||||
}
|
||||
enableDownload={true}
|
||||
resetSearch={resetSearch}
|
||||
/>
|
||||
|
@ -773,10 +776,15 @@ export default function Gallery() {
|
|||
count={selected.count}
|
||||
clearSelection={clearSelection}
|
||||
activeCollection={activeCollection}
|
||||
isFavoriteCollection={isFavoriteCollection(
|
||||
activeCollection,
|
||||
collections
|
||||
)}
|
||||
isFavoriteCollection={
|
||||
collectionSummaries.get(activeCollection)
|
||||
?.type === CollectionSummaryType.favorites
|
||||
}
|
||||
isUncategorizedCollection={
|
||||
collectionSummaries.get(activeCollection)
|
||||
?.type ===
|
||||
CollectionSummaryType.uncategorized
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</FullScreenDropZone>
|
||||
|
|
|
@ -410,7 +410,7 @@ export default function PublicCollectionGallery() {
|
|||
selected={{ count: 0, collectionID: null }}
|
||||
isFirstLoad={true}
|
||||
activeCollection={ALL_SECTION}
|
||||
isSharedCollection
|
||||
isIncomingSharedCollection
|
||||
enableDownload={
|
||||
publicCollection?.publicURLs?.[0]?.enableDownload ??
|
||||
true
|
||||
|
|
|
@ -19,7 +19,6 @@ import {
|
|||
AddToCollectionRequest,
|
||||
MoveToCollectionRequest,
|
||||
EncryptedFileKey,
|
||||
RemoveFromCollectionRequest,
|
||||
CreatePublicAccessTokenRequest,
|
||||
PublicURL,
|
||||
UpdatePublicURL,
|
||||
|
@ -38,6 +37,9 @@ import {
|
|||
COLLECTION_SORT_ORDER,
|
||||
ALL_SECTION,
|
||||
CollectionSummaryType,
|
||||
UNCATEGORIZED_COLLECTION_NAME,
|
||||
FAVORITE_COLLECTION_NAME,
|
||||
DUMMY_UNCATEGORIZED_SECTION,
|
||||
} from 'constants/collection';
|
||||
import {
|
||||
NEW_COLLECTION_MAGIC_METADATA,
|
||||
|
@ -55,6 +57,7 @@ import {
|
|||
isSharedOnlyViaLink,
|
||||
} from 'utils/collection';
|
||||
import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
|
||||
import { getLocalFiles } from './fileService';
|
||||
|
||||
const ENDPOINT = getEndpoint();
|
||||
const COLLECTION_TABLE = 'collections';
|
||||
|
@ -359,7 +362,7 @@ export const addToFavorites = async (file: EnteFile) => {
|
|||
let favCollection = await getFavCollection();
|
||||
if (!favCollection) {
|
||||
favCollection = await createCollection(
|
||||
'Favorites',
|
||||
FAVORITE_COLLECTION_NAME,
|
||||
CollectionType.favorites
|
||||
);
|
||||
const localCollections = await getLocalCollections();
|
||||
|
@ -492,20 +495,60 @@ const encryptWithNewCollectionKey = async (
|
|||
};
|
||||
export const removeFromCollection = async (
|
||||
collectionID: number,
|
||||
files: EnteFile[]
|
||||
toRemoveFiles: EnteFile[],
|
||||
allFiles?: EnteFile[]
|
||||
) => {
|
||||
try {
|
||||
const token = getToken();
|
||||
const request: RemoveFromCollectionRequest = {
|
||||
collectionID: collectionID,
|
||||
fileIDs: files.map((file) => file.id),
|
||||
};
|
||||
if (!allFiles) {
|
||||
allFiles = await getLocalFiles();
|
||||
}
|
||||
|
||||
await HTTPService.post(
|
||||
`${ENDPOINT}/collections/v2/remove-files`,
|
||||
request,
|
||||
null,
|
||||
{ 'X-Auth-Token': token }
|
||||
const toRemoveFilesIds = new Set(toRemoveFiles.map((f) => f.id));
|
||||
const toRemoveFilesCopiesInOtherCollections = allFiles.filter((f) => {
|
||||
if (f.collectionID === collectionID) {
|
||||
return false;
|
||||
}
|
||||
return toRemoveFilesIds.has(f.id);
|
||||
});
|
||||
const groupiedFiles = groupFilesBasedOnCollectionID(
|
||||
toRemoveFilesCopiesInOtherCollections
|
||||
);
|
||||
|
||||
const collections = await getLocalCollections();
|
||||
const collectionsMap = new Map(collections.map((c) => [c.id, c]));
|
||||
|
||||
for (const [toMoveCollectionID, files] of groupiedFiles.entries()) {
|
||||
const toMoveFiles = files.filter((f) => {
|
||||
if (toRemoveFilesIds.has(f.id)) {
|
||||
toRemoveFilesIds.delete(f.id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (toMoveFiles.length === 0) {
|
||||
continue;
|
||||
}
|
||||
await moveToCollection(
|
||||
collectionsMap.get(toMoveCollectionID),
|
||||
collectionID,
|
||||
toMoveFiles
|
||||
);
|
||||
}
|
||||
const leftFiles = toRemoveFiles.filter((f) =>
|
||||
toRemoveFilesIds.has(f.id)
|
||||
);
|
||||
|
||||
if (leftFiles.length === 0) {
|
||||
return;
|
||||
}
|
||||
let uncategorizedCollection = await getUncategorizedCollection();
|
||||
if (!uncategorizedCollection) {
|
||||
uncategorizedCollection = await createUnCategorizedCollection();
|
||||
}
|
||||
await moveToCollection(
|
||||
uncategorizedCollection,
|
||||
collectionID,
|
||||
leftFiles
|
||||
);
|
||||
} catch (e) {
|
||||
logError(e, 'remove from collection failed ');
|
||||
|
@ -513,14 +556,24 @@ export const removeFromCollection = async (
|
|||
}
|
||||
};
|
||||
|
||||
export const deleteCollection = async (collectionID: number) => {
|
||||
export const deleteCollection = async (
|
||||
collectionID: number,
|
||||
keepFiles: boolean
|
||||
) => {
|
||||
try {
|
||||
if (keepFiles) {
|
||||
const allFiles = await getLocalFiles();
|
||||
const collectionFiles = allFiles.filter((file) => {
|
||||
return file.collectionID === collectionID;
|
||||
});
|
||||
await removeFromCollection(collectionID, collectionFiles, allFiles);
|
||||
}
|
||||
const token = getToken();
|
||||
|
||||
await HTTPService.delete(
|
||||
`${ENDPOINT}/collections/v2/${collectionID}`,
|
||||
null,
|
||||
`${ENDPOINT}/collections/v3/${collectionID}`,
|
||||
null,
|
||||
{ collectionID, keepFiles },
|
||||
{ 'X-Auth-Token': token }
|
||||
);
|
||||
} catch (e) {
|
||||
|
@ -817,12 +870,12 @@ function compareCollectionsLatestFile(first: EnteFile, second: EnteFile) {
|
|||
}
|
||||
}
|
||||
|
||||
export function getCollectionSummaries(
|
||||
export async function getCollectionSummaries(
|
||||
user: User,
|
||||
collections: Collection[],
|
||||
files: EnteFile[],
|
||||
archivedCollections: Set<number>
|
||||
): CollectionSummaries {
|
||||
): Promise<CollectionSummaries> {
|
||||
const collectionSummaries: CollectionSummaries = new Map();
|
||||
const collectionLatestFiles = getCollectionLatestFiles(
|
||||
files,
|
||||
|
@ -834,12 +887,15 @@ export function getCollectionSummaries(
|
|||
);
|
||||
|
||||
for (const collection of collections) {
|
||||
if (collectionFilesCount.get(collection.id)) {
|
||||
if (
|
||||
collectionFilesCount.get(collection.id) ||
|
||||
collection.type === CollectionType.uncategorized
|
||||
) {
|
||||
collectionSummaries.set(collection.id, {
|
||||
id: collection.id,
|
||||
name: collection.name,
|
||||
latestFile: collectionLatestFiles.get(collection.id),
|
||||
fileCount: collectionFilesCount.get(collection.id),
|
||||
fileCount: collectionFilesCount.get(collection.id) ?? 0,
|
||||
updationTime: collection.updationTime,
|
||||
type: isIncomingShare(collection, user)
|
||||
? CollectionSummaryType.incomingShare
|
||||
|
@ -853,6 +909,16 @@ export function getCollectionSummaries(
|
|||
});
|
||||
}
|
||||
}
|
||||
const uncategorizedCollection = await getUncategorizedCollection(
|
||||
collections
|
||||
);
|
||||
|
||||
if (!uncategorizedCollection) {
|
||||
collectionSummaries.set(
|
||||
DUMMY_UNCATEGORIZED_SECTION,
|
||||
getDummyUncategorizedCollectionSummaries()
|
||||
);
|
||||
}
|
||||
collectionSummaries.set(
|
||||
ALL_SECTION,
|
||||
getAllCollectionSummaries(collectionFilesCount, collectionLatestFiles)
|
||||
|
@ -920,6 +986,17 @@ function getAllCollectionSummaries(
|
|||
};
|
||||
}
|
||||
|
||||
function getDummyUncategorizedCollectionSummaries(): CollectionSummary {
|
||||
return {
|
||||
id: ALL_SECTION,
|
||||
name: UNCATEGORIZED_COLLECTION_NAME,
|
||||
type: CollectionSummaryType.uncategorized,
|
||||
latestFile: null,
|
||||
fileCount: 0,
|
||||
updationTime: 0,
|
||||
};
|
||||
}
|
||||
|
||||
function getArchivedCollectionSummaries(
|
||||
collectionFilesCount: CollectionFilesCount,
|
||||
collectionsLatestFile: CollectionLatestFiles
|
||||
|
@ -947,3 +1024,23 @@ function getTrashedCollectionSummaries(
|
|||
updationTime: collectionsLatestFile.get(TRASH_SECTION)?.updationTime,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getUncategorizedCollection(
|
||||
collections?: Collection[]
|
||||
): Promise<Collection> {
|
||||
if (!collections) {
|
||||
collections = await getLocalCollections();
|
||||
}
|
||||
const uncategorizedCollection = collections.find(
|
||||
(collection) => collection.type === CollectionType.uncategorized
|
||||
);
|
||||
|
||||
return uncategorizedCollection;
|
||||
}
|
||||
|
||||
export async function createUnCategorizedCollection() {
|
||||
return createCollection(
|
||||
UNCATEGORIZED_COLLECTION_NAME,
|
||||
CollectionType.uncategorized
|
||||
);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ export interface Collection
|
|||
> {
|
||||
key: string;
|
||||
name: string;
|
||||
isSharedCollection?: boolean;
|
||||
magicMetadata: CollectionMagicMetadata;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,12 @@ export interface DialogBoxAttributes {
|
|||
variant: ButtonProps['color'];
|
||||
disabled?: boolean;
|
||||
};
|
||||
secondary?: {
|
||||
text: string;
|
||||
action: () => void;
|
||||
variant: ButtonProps['color'];
|
||||
disabled?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export type SetDialogBoxAttributes = React.Dispatch<
|
||||
|
|
|
@ -20,7 +20,6 @@ import {
|
|||
} from 'types/collection';
|
||||
import {
|
||||
CollectionSummaryType,
|
||||
CollectionType,
|
||||
HIDE_FROM_COLLECTION_BAR_TYPES,
|
||||
OPTIONS_NOT_HAVING_COLLECTION_TYPES,
|
||||
SYSTEM_COLLECTION_TYPES,
|
||||
|
@ -77,31 +76,6 @@ export function getSelectedCollection(
|
|||
return collections.find((collection) => collection.id === collectionID);
|
||||
}
|
||||
|
||||
export function isSharedCollection(
|
||||
collectionID: number,
|
||||
collections: Collection[]
|
||||
) {
|
||||
const user: User = getData(LS_KEYS.USER);
|
||||
|
||||
const collection = getSelectedCollection(collectionID, collections);
|
||||
if (!collection) {
|
||||
return false;
|
||||
}
|
||||
return collection?.owner.id !== user.id;
|
||||
}
|
||||
|
||||
export function isFavoriteCollection(
|
||||
collectionID: number,
|
||||
collections: Collection[]
|
||||
) {
|
||||
const collection = getSelectedCollection(collectionID, collections);
|
||||
if (!collection) {
|
||||
return false;
|
||||
} else {
|
||||
return collection.type === CollectionType.favorites;
|
||||
}
|
||||
}
|
||||
|
||||
export async function downloadAllCollectionFiles(collectionID: number) {
|
||||
try {
|
||||
const allFiles = await getLocalFiles();
|
||||
|
@ -204,7 +178,10 @@ export const getArchivedCollections = (collections: Collection[]) => {
|
|||
export const hasNonSystemCollections = (
|
||||
collectionSummaries: CollectionSummaries
|
||||
) => {
|
||||
return collectionSummaries?.size > 3;
|
||||
for (const collectionSummary of collectionSummaries.values()) {
|
||||
if (!isSystemCollection(collectionSummary.type)) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const isUploadAllowedCollection = (type: CollectionSummaryType) => {
|
||||
|
|
|
@ -140,7 +140,8 @@ const englishConstants = {
|
|||
CREATE: 'Create',
|
||||
DOWNLOAD: 'Download',
|
||||
DOWNLOAD_OPTION: 'Download (D)',
|
||||
DOWNLOAD_FAVOURITES: 'Download favourites',
|
||||
DOWNLOAD_FAVORITES: 'Download favorites',
|
||||
DOWNLOAD_UNCATEGORIZED: 'Download uncategorized',
|
||||
COPY_OPTION: 'Copy as PNG (Ctrl/Cmd - C)',
|
||||
TOGGLE_FULLSCREEN: 'Toggle fullscreen (F)',
|
||||
ZOOM_IN_OUT: 'Zoom in/out',
|
||||
|
@ -368,8 +369,15 @@ const englishConstants = {
|
|||
DELETE_COLLECTION_TITLE: 'Delete album?',
|
||||
DELETE_COLLECTION: 'Delete album',
|
||||
DELETE_COLLECTION_FAILED: 'Album deletion failed, please try again',
|
||||
DELETE_COLLECTION_MESSAGE:
|
||||
'Files that are unique to this album will be moved to trash, and this album would be deleted.',
|
||||
DELETE_COLLECTION_MESSAGE: () => (
|
||||
<p>
|
||||
Also delete the photos (and videos) present in this album from
|
||||
<span style={{ color: '#fff' }}> all </span> other albums they are
|
||||
part of?
|
||||
</p>
|
||||
),
|
||||
DELETE_PHOTOS: 'Delete photos',
|
||||
KEEP_PHOTOS: 'Keep photos',
|
||||
SHARE: 'Share',
|
||||
SHARE_COLLECTION: 'Share album',
|
||||
SHARE_WITH_PEOPLE: 'Share with your loved ones',
|
||||
|
@ -587,6 +595,8 @@ const englishConstants = {
|
|||
THUMBNAIL_GENERATION_FAILED_INFO:
|
||||
'These files were uploaded, but unfortunately we could not generate the thumbnails for them.',
|
||||
UPLOAD_TO_COLLECTION: 'Upload to album',
|
||||
UNCATEGORIZED: 'Uncategorized',
|
||||
MOVE_TO_UNCATEGORIZED: 'Move to uncategorized',
|
||||
ARCHIVE: 'Archive',
|
||||
ARCHIVE_COLLECTION: 'Archive album',
|
||||
ARCHIVE_SECTION_NAME: 'Archive',
|
||||
|
@ -598,7 +608,9 @@ const englishConstants = {
|
|||
ADD: 'Add',
|
||||
SORT: 'Sort',
|
||||
REMOVE: 'Remove',
|
||||
YES_REMOVE: 'Yes, remove',
|
||||
CONFIRM_REMOVE: 'Confirm removal',
|
||||
REMOVE_FROM_COLLECTION: 'Remove from album',
|
||||
TRASH: 'Trash',
|
||||
MOVE_TO_TRASH: 'Move to trash',
|
||||
TRASH_FILES_MESSAGE:
|
||||
|
@ -622,9 +634,9 @@ const englishConstants = {
|
|||
'You will leave the album, and it will stop being visible to you.',
|
||||
CONFIRM_REMOVE_MESSAGE: () => (
|
||||
<>
|
||||
<p>Are you sure you want to remove these files from the album?</p>
|
||||
<p>
|
||||
All files that are unique to this album will be moved to trash
|
||||
Selected items will be removed from this album. Items which are
|
||||
only in this album will be moved to Uncategorized.
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue