Merge branch 'main' into fix-1554
This commit is contained in:
commit
be547a6cf5
10 changed files with 163 additions and 27 deletions
|
@ -85,9 +85,9 @@
|
|||
"ZOOM_IN_OUT": "In/uitzoomen",
|
||||
"PREVIOUS": "Vorige (←)",
|
||||
"NEXT": "Volgende (→)",
|
||||
"TITLE_PHOTOS": "",
|
||||
"TITLE_ALBUMS": "",
|
||||
"TITLE_AUTH": "",
|
||||
"TITLE_PHOTOS": "Ente Foto's",
|
||||
"TITLE_ALBUMS": "Ente Foto's",
|
||||
"TITLE_AUTH": "Ente Auth",
|
||||
"UPLOAD_FIRST_PHOTO": "Je eerste foto uploaden",
|
||||
"IMPORT_YOUR_FOLDERS": "Importeer uw mappen",
|
||||
"UPLOAD_DROPZONE_MESSAGE": "Sleep om een back-up van je bestanden te maken",
|
||||
|
@ -622,7 +622,7 @@
|
|||
"PHOTO_EDITOR": "Fotobewerker",
|
||||
"FASTER_UPLOAD": "Snellere uploads",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "Uploaden door nabije servers",
|
||||
"MAGIC_SEARCH_STATUS": "",
|
||||
"MAGIC_SEARCH_STATUS": "Magische Zoekfunctie Status",
|
||||
"INDEXED_ITEMS": "Geïndexeerde bestanden",
|
||||
"CACHE_DIRECTORY": "Cache map"
|
||||
}
|
||||
|
|
|
@ -140,8 +140,8 @@ function CollectionSelector({
|
|||
? t('UNHIDE_TO_COLLECTION')
|
||||
: t('SELECT_COLLECTION')}
|
||||
</DialogTitleWithCloseButton>
|
||||
<DialogContent>
|
||||
<FlexWrapper flexWrap="wrap" gap={0.5}>
|
||||
<DialogContent sx={{ '&&&': { padding: 0 } }}>
|
||||
<FlexWrapper flexWrap="wrap" gap={'4px'} padding={'16px'}>
|
||||
<AddCollectionButton
|
||||
showNextModal={attributes.showNextModal}
|
||||
/>
|
||||
|
|
|
@ -12,7 +12,6 @@ import PhotoViewer from 'components/PhotoViewer';
|
|||
import { TRASH_SECTION } from 'constants/collection';
|
||||
import { updateFileMsrcProps, updateFileSrcProps } from 'utils/photoFrame';
|
||||
import { SelectedState } from 'types/gallery';
|
||||
import { PublicCollectionGalleryContext } from 'utils/publicCollectionGallery';
|
||||
import { useRouter } from 'next/router';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
import { addLogLine } from '@ente/shared/logging';
|
||||
|
@ -89,9 +88,6 @@ const PhotoFrame = ({
|
|||
[k: number]: boolean;
|
||||
}>({});
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
const publicCollectionGalleryContext = useContext(
|
||||
PublicCollectionGalleryContext
|
||||
);
|
||||
const [rangeStart, setRangeStart] = useState(null);
|
||||
const [currentHover, setCurrentHover] = useState(null);
|
||||
const [isShiftKeyPressed, setIsShiftKeyPressed] = useState(false);
|
||||
|
@ -315,9 +311,7 @@ const PhotoFrame = ({
|
|||
file={item}
|
||||
updateURL={updateURL(index)}
|
||||
onClick={onThumbnailClick(index)}
|
||||
selectable={
|
||||
!publicCollectionGalleryContext?.accessedThroughSharedURL
|
||||
}
|
||||
selectable={enableDownload}
|
||||
onSelect={handleSelect(
|
||||
item.id,
|
||||
item.ownerID === galleryContext.user?.id,
|
||||
|
|
|
@ -192,6 +192,12 @@ function PhotoViewer(props: Iprops) {
|
|||
case 'L':
|
||||
onFavClick(photoSwipe?.currItem as EnteFile);
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
handleArrowClick(event, 'left');
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
handleArrowClick(event, 'right');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -352,6 +358,7 @@ function PhotoViewer(props: Iprops) {
|
|||
maxSpreadZoom: 5,
|
||||
index: currentIndex,
|
||||
showHideOpacity: true,
|
||||
arrowKeys: false,
|
||||
getDoubleTapZoom(isMouseClick, item) {
|
||||
if (isMouseClick) {
|
||||
return 2.5;
|
||||
|
@ -505,6 +512,24 @@ function PhotoViewer(props: Iprops) {
|
|||
appContext.setDialogMessage(getTrashFileMessage(() => trashFile(file)));
|
||||
};
|
||||
|
||||
const handleArrowClick = (
|
||||
e: KeyboardEvent,
|
||||
direction: 'left' | 'right'
|
||||
) => {
|
||||
// ignore arrow clicks if the user is typing in a text field
|
||||
if (
|
||||
e.target instanceof HTMLInputElement ||
|
||||
e.target instanceof HTMLTextAreaElement
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (direction === 'left') {
|
||||
photoSwipe.prev();
|
||||
} else {
|
||||
photoSwipe.next();
|
||||
}
|
||||
};
|
||||
|
||||
const updateItems = (items: EnteFile[]) => {
|
||||
try {
|
||||
if (photoSwipe) {
|
||||
|
|
|
@ -12,6 +12,7 @@ import UploadProgressContext from 'contexts/uploadProgress';
|
|||
import { t } from 'i18next';
|
||||
|
||||
import { UPLOAD_STAGES } from 'constants/upload';
|
||||
import { CaptionedText } from 'components/CaptionedText';
|
||||
|
||||
export const InProgressSection = () => {
|
||||
const { inProgressUploads, hasLivePhotos, uploadFileNames, uploadStage } =
|
||||
|
@ -44,9 +45,14 @@ export const InProgressSection = () => {
|
|||
return (
|
||||
<UploadProgressSection>
|
||||
<UploadProgressSectionTitle expandIcon={<ExpandMoreIcon />}>
|
||||
{uploadStage === UPLOAD_STAGES.EXTRACTING_METADATA
|
||||
? t('INPROGRESS_METADATA_EXTRACTION')
|
||||
: t('INPROGRESS_UPLOADS')}
|
||||
<CaptionedText
|
||||
mainText={
|
||||
uploadStage === UPLOAD_STAGES.EXTRACTING_METADATA
|
||||
? t('INPROGRESS_METADATA_EXTRACTION')
|
||||
: t('INPROGRESS_UPLOADS')
|
||||
}
|
||||
subText={String(inProgressUploads?.length ?? 0)}
|
||||
/>
|
||||
</UploadProgressSectionTitle>
|
||||
<UploadProgressSectionContent>
|
||||
{hasLivePhotos && (
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React, { useContext } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import ItemList from 'components/ItemList';
|
||||
import { Typography } from '@mui/material';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import { ResultItemContainer } from './styledComponents';
|
||||
import { UPLOAD_RESULT } from 'constants/upload';
|
||||
|
@ -11,6 +10,7 @@ import {
|
|||
UploadProgressSectionTitle,
|
||||
} from './section';
|
||||
import UploadProgressContext from 'contexts/uploadProgress';
|
||||
import { CaptionedText } from 'components/CaptionedText';
|
||||
|
||||
export interface ResultSectionProps {
|
||||
uploadResult: UPLOAD_RESULT;
|
||||
|
@ -46,7 +46,10 @@ export const ResultSection = (props: ResultSectionProps) => {
|
|||
return (
|
||||
<UploadProgressSection>
|
||||
<UploadProgressSectionTitle expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography> {props.sectionTitle}</Typography>
|
||||
<CaptionedText
|
||||
mainText={props.sectionTitle}
|
||||
subText={String(fileList?.length ?? 0)}
|
||||
/>
|
||||
</UploadProgressSectionTitle>
|
||||
<UploadProgressSectionContent>
|
||||
{props.sectionInfo && (
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import { EnteFile } from 'types/file';
|
||||
import { styled } from '@mui/material';
|
||||
import { Tooltip, styled } from '@mui/material';
|
||||
import PlayCircleOutlineOutlinedIcon from '@mui/icons-material/PlayCircleOutlineOutlined';
|
||||
import DownloadManager from 'services/download';
|
||||
import useLongPress from '@ente/shared/hooks/useLongPress';
|
||||
|
@ -298,7 +298,7 @@ export default function PreviewCard(props: IProps) {
|
|||
}
|
||||
};
|
||||
|
||||
return (
|
||||
const renderFn = () => (
|
||||
<Cont
|
||||
key={`thumb-${file.id}}`}
|
||||
onClick={handleClick}
|
||||
|
@ -360,4 +360,22 @@ export default function PreviewCard(props: IProps) {
|
|||
)}
|
||||
</Cont>
|
||||
);
|
||||
|
||||
if (deduplicateContext.isOnDeduplicatePage) {
|
||||
return (
|
||||
<Tooltip
|
||||
placement="bottom-start"
|
||||
enterDelay={300}
|
||||
enterNextDelay={100}
|
||||
title={`${
|
||||
file.metadata.title
|
||||
} - ${deduplicateContext.collectionNameMap.get(
|
||||
file.collectionID
|
||||
)}`}>
|
||||
{renderFn()}
|
||||
</Tooltip>
|
||||
);
|
||||
} else {
|
||||
return renderFn();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import { useContext } from 'react';
|
||||
import { FluidContainer } from '@ente/shared/components/Container';
|
||||
import { SelectionBar } from '@ente/shared/components/Navbar/SelectionBar';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { Box, IconButton, Stack, Tooltip } from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import DownloadIcon from '@mui/icons-material/Download';
|
||||
import { t } from 'i18next';
|
||||
import { formatNumber } from 'utils/number/format';
|
||||
|
||||
interface Props {
|
||||
count: number;
|
||||
ownCount: number;
|
||||
clearSelection: () => void;
|
||||
downloadFilesHelper: () => void;
|
||||
}
|
||||
|
||||
const SelectedFileOptions = ({
|
||||
downloadFilesHelper,
|
||||
count,
|
||||
ownCount,
|
||||
clearSelection,
|
||||
}: Props) => {
|
||||
const { isMobile } = useContext(AppContext);
|
||||
|
||||
return (
|
||||
<SelectionBar isMobile={isMobile}>
|
||||
<FluidContainer>
|
||||
<IconButton onClick={clearSelection}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<Box ml={1.5}>
|
||||
{formatNumber(count)} {t('SELECTED')}{' '}
|
||||
{ownCount !== count &&
|
||||
`(${formatNumber(ownCount)} ${t('YOURS')})`}
|
||||
</Box>
|
||||
</FluidContainer>
|
||||
<Stack spacing={2} direction="row" mr={2}>
|
||||
<Tooltip title={t('DOWNLOAD')}>
|
||||
<IconButton onClick={downloadFilesHelper}>
|
||||
<DownloadIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
</SelectionBar>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectedFileOptions;
|
|
@ -16,7 +16,13 @@ import {
|
|||
} from 'services/publicCollectionService';
|
||||
import { Collection } from 'types/collection';
|
||||
import { EnteFile } from 'types/file';
|
||||
import { downloadFile, mergeMetadata, sortFiles } from 'utils/file';
|
||||
import {
|
||||
downloadFile,
|
||||
downloadFiles,
|
||||
getSelectedFiles,
|
||||
mergeMetadata,
|
||||
sortFiles,
|
||||
} from 'utils/file';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { PublicCollectionGalleryContext } from 'utils/publicCollectionGallery';
|
||||
import { CustomError, parseSharingErrorCodes } from '@ente/shared/error';
|
||||
|
@ -52,7 +58,7 @@ import UploadButton from 'components/Upload/UploadButton';
|
|||
import bs58 from 'bs58';
|
||||
import AddPhotoAlternateOutlined from '@mui/icons-material/AddPhotoAlternateOutlined';
|
||||
import ComlinkCryptoWorker from '@ente/shared/crypto';
|
||||
import { UploadTypeSelectorIntent } from 'types/gallery';
|
||||
import { SelectedState, UploadTypeSelectorIntent } from 'types/gallery';
|
||||
import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
|
||||
import MoreHoriz from '@mui/icons-material/MoreHoriz';
|
||||
import OverflowMenu from '@ente/shared/components/OverflowMenu/menu';
|
||||
|
@ -60,6 +66,7 @@ import { OverflowMenuOption } from '@ente/shared/components/OverflowMenu/option'
|
|||
import { ENTE_WEBSITE_LINK } from '@ente/shared/constants/urls';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import downloadManager from 'services/download';
|
||||
import SelectedFileOptions from 'components/pages/sharedAlbum/SelectedFileOptions';
|
||||
|
||||
export default function PublicCollectionGallery() {
|
||||
const token = useRef<string>(null);
|
||||
|
@ -87,6 +94,12 @@ export default function PublicCollectionGallery() {
|
|||
const [blockingLoad, setBlockingLoad] = useState(false);
|
||||
const [shouldDisableDropzone, setShouldDisableDropzone] = useState(false);
|
||||
|
||||
const [selected, setSelected] = useState<SelectedState>({
|
||||
ownCount: 0,
|
||||
count: 0,
|
||||
collectionID: 0,
|
||||
});
|
||||
|
||||
const {
|
||||
getRootProps: getDragAndDropRootProps,
|
||||
getInputProps: getDragAndDropInputProps,
|
||||
|
@ -441,6 +454,22 @@ export default function PublicCollectionGallery() {
|
|||
}
|
||||
}
|
||||
|
||||
const downloadFilesHelper = async () => {
|
||||
try {
|
||||
const selectedFiles = getSelectedFiles(selected, publicFiles);
|
||||
await downloadFiles(selectedFiles);
|
||||
} catch (e) {
|
||||
logError(e, 'failed to download selected files');
|
||||
}
|
||||
};
|
||||
|
||||
const clearSelection = () => {
|
||||
if (!selected?.count) {
|
||||
return;
|
||||
}
|
||||
setSelected({ ownCount: 0, count: 0, collectionID: 0 });
|
||||
};
|
||||
|
||||
return (
|
||||
<PublicCollectionGalleryContext.Provider
|
||||
value={{
|
||||
|
@ -468,8 +497,8 @@ export default function PublicCollectionGallery() {
|
|||
page={PAGES.SHARED_ALBUMS}
|
||||
files={publicFiles}
|
||||
syncWithRemote={syncWithRemote}
|
||||
setSelected={() => null}
|
||||
selected={{ count: 0, collectionID: null, ownCount: 0 }}
|
||||
setSelected={setSelected}
|
||||
selected={selected}
|
||||
activeCollectionID={ALL_SECTION}
|
||||
enableDownload={downloadEnabled}
|
||||
fileToCollectionsMap={null}
|
||||
|
@ -498,6 +527,14 @@ export default function PublicCollectionGallery() {
|
|||
UploadTypeSelectorIntent.collectPhotos
|
||||
}
|
||||
/>
|
||||
{selected.count > 0 && (
|
||||
<SelectedFileOptions
|
||||
downloadFilesHelper={downloadFilesHelper}
|
||||
clearSelection={clearSelection}
|
||||
count={selected.count}
|
||||
ownCount={selected.ownCount}
|
||||
/>
|
||||
)}
|
||||
</FullScreenDropZone>
|
||||
</PublicCollectionGalleryContext.Provider>
|
||||
);
|
||||
|
|
|
@ -26,7 +26,11 @@ export async function getDuplicates(
|
|||
collectionNameMap: Map<number, string>
|
||||
) {
|
||||
try {
|
||||
const dupes = await fetchDuplicateFileIDs();
|
||||
const ascDupes = await fetchDuplicateFileIDs();
|
||||
|
||||
const descSortedDupes = ascDupes.sort((firstDupe, secondDupe) => {
|
||||
return secondDupe.size - firstDupe.size;
|
||||
});
|
||||
|
||||
const fileMap = new Map<number, EnteFile>();
|
||||
for (const file of files) {
|
||||
|
@ -35,7 +39,7 @@ export async function getDuplicates(
|
|||
|
||||
let result: Duplicate[] = [];
|
||||
|
||||
for (const dupe of dupes) {
|
||||
for (const dupe of descSortedDupes) {
|
||||
let duplicateFiles: EnteFile[] = [];
|
||||
for (const fileID of dupe.fileIDs) {
|
||||
if (fileMap.has(fileID)) {
|
||||
|
|
Loading…
Add table
Reference in a new issue