Merge branch 'main' into folder-plus-file-drop

This commit is contained in:
Abhinav 2022-12-16 14:25:00 +05:30
commit 3665d9d4a5
17 changed files with 115 additions and 85 deletions

View file

@ -12,9 +12,6 @@ export default function CollectionSortOptions(props: CollectionSortProps) {
<SortByOption sortBy={COLLECTION_SORT_BY.NAME}> <SortByOption sortBy={COLLECTION_SORT_BY.NAME}>
{constants.SORT_BY_NAME} {constants.SORT_BY_NAME}
</SortByOption> </SortByOption>
<SortByOption sortBy={COLLECTION_SORT_BY.CREATION_TIME_DESCENDING}>
{constants.SORT_BY_CREATION_TIME_DESCENDING}
</SortByOption>
<SortByOption sortBy={COLLECTION_SORT_BY.CREATION_TIME_ASCENDING}> <SortByOption sortBy={COLLECTION_SORT_BY.CREATION_TIME_ASCENDING}>
{constants.SORT_BY_CREATION_TIME_ASCENDING} {constants.SORT_BY_CREATION_TIME_ASCENDING}
</SortByOption> </SortByOption>

View file

@ -15,7 +15,7 @@ import {
import { ManageDownloadAccess } from './downloadAccess'; import { ManageDownloadAccess } from './downloadAccess';
import { handleSharingErrors } from 'utils/error/ui'; import { handleSharingErrors } from 'utils/error/ui';
import { SetPublicShareProp } from 'types/publicCollection'; import { SetPublicShareProp } from 'types/publicCollection';
// import { ManagePublicCollect } from './publicCollect'; import { ManagePublicCollect } from './publicCollect';
interface Iprops { interface Iprops {
publicShareProp: PublicURL; publicShareProp: PublicURL;
@ -77,6 +77,13 @@ export default function PublicShareManage({
updatePublicShareURLHelper updatePublicShareURLHelper
} }
/> />
<ManagePublicCollect
collection={collection}
publicShareProp={publicShareProp}
updatePublicShareURLHelper={
updatePublicShareURLHelper
}
/>
<ManageDownloadAccess <ManageDownloadAccess
collection={collection} collection={collection}
publicShareProp={publicShareProp} publicShareProp={publicShareProp}
@ -91,13 +98,6 @@ export default function PublicShareManage({
updatePublicShareURLHelper updatePublicShareURLHelper
} }
/> />
{/* <ManagePublicCollect
collection={collection}
publicShareProp={publicShareProp}
updatePublicShareURLHelper={
updatePublicShareURLHelper
}
/> */}
</Stack> </Stack>
{sharableLinkError && ( {sharableLinkError && (
<Typography <Typography

View file

@ -1,6 +1,5 @@
import { Box, Typography } from '@mui/material'; import { Box, Typography } from '@mui/material';
import { AppContext } from 'pages/_app'; import React from 'react';
import React, { useContext } from 'react';
import { PublicURL, Collection, UpdatePublicURL } from 'types/collection'; import { PublicURL, Collection, UpdatePublicURL } from 'types/collection';
import constants from 'utils/strings/constants'; import constants from 'utils/strings/constants';
import PublicShareSwitch from '../switch'; import PublicShareSwitch from '../switch';
@ -16,35 +15,13 @@ export function ManagePublicCollect({
updatePublicShareURLHelper, updatePublicShareURLHelper,
collection, collection,
}: Iprops) { }: Iprops) {
const appContext = useContext(AppContext);
const handleFileDownloadSetting = () => { const handleFileDownloadSetting = () => {
if (!publicShareProp.enableCollect) {
enablePublicCollect();
} else {
updatePublicShareURLHelper({ updatePublicShareURLHelper({
collectionID: collection.id, collectionID: collection.id,
enableCollect: false, enableCollect: !publicShareProp.enableCollect,
}); });
}
}; };
const enablePublicCollect = () => {
appContext.setDialogMessage({
title: constants.ENABLE_PUBLIC_COLLECT,
content: constants.ENABLE_PUBLIC_COLLECT_MESSAGE(),
close: { text: constants.CANCEL },
proceed: {
text: constants.ENABLE,
action: () =>
updatePublicShareURLHelper({
collectionID: collection.id,
enableCollect: true,
}),
variant: 'accent',
},
});
};
return ( return (
<Box> <Box>
<Typography mb={0.5}>{constants.PUBLIC_COLLECT}</Typography> <Typography mb={0.5}>{constants.PUBLIC_COLLECT}</Typography>

View file

@ -28,6 +28,7 @@ export interface SingleInputFormProps {
hiddenPostInput?: any; hiddenPostInput?: any;
autoComplete?: string; autoComplete?: string;
blockButton?: boolean; blockButton?: boolean;
hiddenLabel?: boolean;
} }
export default function SingleInputForm(props: SingleInputFormProps) { export default function SingleInputForm(props: SingleInputFormProps) {
@ -88,12 +89,15 @@ export default function SingleInputForm(props: SingleInputFormProps) {
<form noValidate onSubmit={handleSubmit}> <form noValidate onSubmit={handleSubmit}>
{props.hiddenPreInput} {props.hiddenPreInput}
<TextField <TextField
hiddenLabel={props.hiddenLabel}
variant="filled" variant="filled"
fullWidth fullWidth
type={showPassword ? 'text' : props.fieldType} type={showPassword ? 'text' : props.fieldType}
id={props.fieldType} id={props.fieldType}
name={props.fieldType} name={props.fieldType}
label={props.placeholder} {...(props.hiddenLabel
? { placeholder: props.placeholder }
: { label: props.placeholder })}
value={values.inputValue} value={values.inputValue}
onChange={handleChange('inputValue')} onChange={handleChange('inputValue')}
error={Boolean(errors.inputValue)} error={Boolean(errors.inputValue)}

View file

@ -86,6 +86,12 @@ interface Props {
} }
export default function Uploader(props: Props) { export default function Uploader(props: Props) {
const appContext = useContext(AppContext);
const galleryContext = useContext(GalleryContext);
const publicCollectionGalleryContext = useContext(
PublicCollectionGalleryContext
);
const [uploadProgressView, setUploadProgressView] = useState(false); const [uploadProgressView, setUploadProgressView] = useState(false);
const [uploadStage, setUploadStage] = useState<UPLOAD_STAGES>( const [uploadStage, setUploadStage] = useState<UPLOAD_STAGES>(
UPLOAD_STAGES.START UPLOAD_STAGES.START
@ -109,8 +115,8 @@ export default function Uploader(props: Props) {
const [importSuggestion, setImportSuggestion] = useState<ImportSuggestion>( const [importSuggestion, setImportSuggestion] = useState<ImportSuggestion>(
DEFAULT_IMPORT_SUGGESTION DEFAULT_IMPORT_SUGGESTION
); );
const appContext = useContext(AppContext); const [electronFiles, setElectronFiles] = useState<ElectronFile[]>(null);
const galleryContext = useContext(GalleryContext); const [webFiles, setWebFiles] = useState([]);
const toUploadFiles = useRef<File[] | ElectronFile[]>(null); const toUploadFiles = useRef<File[] | ElectronFile[]>(null);
const isPendingDesktopUpload = useRef(false); const isPendingDesktopUpload = useRef(false);
@ -119,23 +125,17 @@ export default function Uploader(props: Props) {
const pickedUploadType = useRef<PICKED_UPLOAD_TYPE>(null); const pickedUploadType = useRef<PICKED_UPLOAD_TYPE>(null);
const zipPaths = useRef<string[]>(null); const zipPaths = useRef<string[]>(null);
const currentUploadPromise = useRef<Promise<void>>(null); const currentUploadPromise = useRef<Promise<void>>(null);
const [electronFiles, setElectronFiles] = useState<ElectronFile[]>(null); const uploadRunning = useRef(false);
const [webFiles, setWebFiles] = useState([]); const uploaderNameRef = useRef<string>(null);
const closeUploadProgress = () => setUploadProgressView(false); const closeUploadProgress = () => setUploadProgressView(false);
const showUserNameInputDialog = () => setUserNameInputDialogView(true); const showUserNameInputDialog = () => setUserNameInputDialogView(true);
const closeUserNameInputDialog = () => setUserNameInputDialogView(false);
const setCollectionName = (collectionName: string) => { const setCollectionName = (collectionName: string) => {
isPendingDesktopUpload.current = true; isPendingDesktopUpload.current = true;
pendingDesktopUploadCollectionName.current = collectionName; pendingDesktopUploadCollectionName.current = collectionName;
}; };
const uploadRunning = useRef(false);
const publicCollectionGalleryContext = useContext(
PublicCollectionGalleryContext
);
const handleChoiceModalClose = () => { const handleChoiceModalClose = () => {
setChoiceModalView(false); setChoiceModalView(false);
uploadRunning.current = false; uploadRunning.current = false;
@ -144,6 +144,11 @@ export default function Uploader(props: Props) {
uploadRunning.current = false; uploadRunning.current = false;
}; };
const handleUserNameInputDialogClose = () => {
setUserNameInputDialogView(false);
uploadRunning.current = false;
};
useEffect(() => { useEffect(() => {
uploadManager.init( uploadManager.init(
{ {
@ -308,7 +313,6 @@ export default function Uploader(props: Props) {
[collection], [collection],
uploaderName uploaderName
); );
toUploadFiles.current = null;
} catch (e) { } catch (e) {
logError(e, 'Failed to upload files to existing collections'); logError(e, 'Failed to upload files to existing collections');
} }
@ -470,11 +474,13 @@ export default function Uploader(props: Props) {
try { try {
addLogLine('user retrying failed upload'); addLogLine('user retrying failed upload');
const filesWithCollections = const filesWithCollections =
await uploadManager.getFailedFilesWithCollections(); uploadManager.getFailedFilesWithCollections();
const uploaderName = uploadManager.getUploaderName();
await preUploadAction(); await preUploadAction();
await uploadManager.queueFilesForUpload( await uploadManager.queueFilesForUpload(
filesWithCollections.files, filesWithCollections.files,
filesWithCollections.collections filesWithCollections.collections,
uploaderName
); );
} catch (err) { } catch (err) {
showUserFacingError(err.message); showUserFacingError(err.message);
@ -546,11 +552,8 @@ export default function Uploader(props: Props) {
const uploaderName = await getPublicCollectionUploaderName( const uploaderName = await getPublicCollectionUploaderName(
getPublicCollectionUID(publicCollectionGalleryContext.token) getPublicCollectionUID(publicCollectionGalleryContext.token)
); );
if (!uploaderName) { uploaderNameRef.current = uploaderName;
showUserNameInputDialog(); showUserNameInputDialog();
} else {
await handlePublicUpload(uploaderName, true);
}
return; return;
} }
if (isPendingDesktopUpload.current) { if (isPendingDesktopUpload.current) {
@ -709,8 +712,10 @@ export default function Uploader(props: Props) {
/> />
<UserNameInputDialog <UserNameInputDialog
open={userNameInputDialogView} open={userNameInputDialogView}
onClose={closeUserNameInputDialog} onClose={handleUserNameInputDialogClose}
onNameSubmit={handlePublicUpload} onNameSubmit={handlePublicUpload}
toUploadFilesCount={toUploadFiles.current?.length}
uploaderName={uploaderNameRef.current}
/> />
</> </>
); );

View file

@ -5,7 +5,13 @@ import AutoAwesomeOutlinedIcon from '@mui/icons-material/AutoAwesomeOutlined';
import { Typography } from '@mui/material'; import { Typography } from '@mui/material';
import SingleInputForm from './SingleInputForm'; import SingleInputForm from './SingleInputForm';
export default function UserNameInputDialog({ open, onClose, onNameSubmit }) { export default function UserNameInputDialog({
open,
onClose,
onNameSubmit,
toUploadFilesCount,
uploaderName,
}) {
const handleSubmit = async (inputValue: string) => { const handleSubmit = async (inputValue: string) => {
onClose(); onClose();
await onNameSubmit(inputValue); await onNameSubmit(inputValue);
@ -23,12 +29,14 @@ export default function UserNameInputDialog({ open, onClose, onNameSubmit }) {
{constants.PUBLIC_UPLOADER_NAME_MESSAGE} {constants.PUBLIC_UPLOADER_NAME_MESSAGE}
</Typography> </Typography>
<SingleInputForm <SingleInputForm
hiddenLabel
initialValue={uploaderName}
callback={handleSubmit} callback={handleSubmit}
placeholder={constants.ENTER_FILE_NAME} placeholder={constants.NAME_PLACEHOLDER}
buttonText={constants.CONTINUE} buttonText={constants.ADD_X_PHOTOS(toUploadFilesCount)}
fieldType="text" fieldType="text"
blockButton blockButton
secondaryButtonAction={() => {}} secondaryButtonAction={onClose}
/> />
</DialogBox> </DialogBox>
); );

View file

@ -21,7 +21,6 @@ export enum CollectionSummaryType {
export enum COLLECTION_SORT_BY { export enum COLLECTION_SORT_BY {
NAME, NAME,
CREATION_TIME_ASCENDING, CREATION_TIME_ASCENDING,
CREATION_TIME_DESCENDING,
UPDATION_TIME_DESCENDING, UPDATION_TIME_DESCENDING,
} }

View file

@ -736,11 +736,6 @@ export function sortCollectionSummaries(
return collectionSummaries return collectionSummaries
.sort((a, b) => { .sort((a, b) => {
switch (sortBy) { switch (sortBy) {
case COLLECTION_SORT_BY.CREATION_TIME_DESCENDING:
return compareCollectionsLatestFile(
b.latestFile,
a.latestFile
);
case COLLECTION_SORT_BY.CREATION_TIME_ASCENDING: case COLLECTION_SORT_BY.CREATION_TIME_ASCENDING:
return ( return (
-1 * -1 *

View file

@ -22,6 +22,7 @@ import {
getUint8ArrayView, getUint8ArrayView,
} from '../readerService'; } from '../readerService';
import { generateThumbnail } from './thumbnailService'; import { generateThumbnail } from './thumbnailService';
import { EncryptedMagicMetadataCore } from 'types/magicMetadata';
const EDITED_FILE_SUFFIX = '-edited'; const EDITED_FILE_SUFFIX = '-edited';
@ -110,6 +111,16 @@ export async function encryptFile(
const { file: encryptedMetadata }: EncryptionResult = const { file: encryptedMetadata }: EncryptionResult =
await worker.encryptMetadata(file.metadata, fileKey); await worker.encryptMetadata(file.metadata, fileKey);
const { file: encryptedPubMagicMetadataData }: EncryptionResult =
await worker.encryptMetadata(file.pubMagicMetadata.data, fileKey);
const encryptedPubMagicMetadata: EncryptedMagicMetadataCore = {
version: file.pubMagicMetadata.version,
count: file.pubMagicMetadata.count,
data: encryptedPubMagicMetadataData.encryptedData as unknown as string,
header: encryptedPubMagicMetadataData.decryptionHeader,
};
const encryptedKey: B64EncryptionResult = await worker.encryptToB64( const encryptedKey: B64EncryptionResult = await worker.encryptToB64(
fileKey, fileKey,
encryptionKey encryptionKey
@ -120,6 +131,7 @@ export async function encryptFile(
file: encryptedFiledata, file: encryptedFiledata,
thumbnail: encryptedThumbnail, thumbnail: encryptedThumbnail,
metadata: encryptedMetadata, metadata: encryptedMetadata,
pubMagicMetadata: encryptedPubMagicMetadata,
localID: file.localID, localID: file.localID,
}, },
fileKey: encryptedKey, fileKey: encryptedKey,

View file

@ -0,0 +1,17 @@
import {
FilePublicMagicMetadataProps,
FilePublicMagicMetadata,
} from 'types/file';
import { NEW_FILE_MAGIC_METADATA } from 'types/magicMetadata';
import { updateMagicMetadataProps } from 'utils/magicMetadata';
export async function constructPublicMagicMetadata(
publicMagicMetadataProps: FilePublicMagicMetadataProps
): Promise<FilePublicMagicMetadata> {
const pubMagicMetadata = await updateMagicMetadataProps(
NEW_FILE_MAGIC_METADATA,
null,
publicMagicMetadataProps
);
return pubMagicMetadata;
}

View file

@ -389,13 +389,17 @@ class UploadManager {
uploadCancelService.requestUploadCancelation(); uploadCancelService.requestUploadCancelation();
} }
async getFailedFilesWithCollections() { getFailedFilesWithCollections() {
return { return {
files: this.failedFiles, files: this.failedFiles,
collections: [...this.collections.values()], collections: [...this.collections.values()],
}; };
} }
getUploaderName() {
return this.uploaderName;
}
private updateExistingFiles(decryptedFile: EnteFile) { private updateExistingFiles(decryptedFile: EnteFile) {
if (!decryptedFile) { if (!decryptedFile) {
throw Error("decrypted file can't be undefined"); throw Error("decrypted file can't be undefined");

View file

@ -34,6 +34,8 @@ import { uploadStreamUsingMultipart } from './multiPartUploadService';
import UIService from './uiService'; import UIService from './uiService';
import { USE_CF_PROXY } from 'constants/upload'; import { USE_CF_PROXY } from 'constants/upload';
import publicUploadHttpClient from './publicUploadHttpClient'; import publicUploadHttpClient from './publicUploadHttpClient';
import { constructPublicMagicMetadata } from './magicMetadataService';
import { FilePublicMagicMetadataProps } from 'types/file';
class UploadService { class UploadService {
private uploadURLs: UploadURL[] = []; private uploadURLs: UploadURL[] = [];
@ -127,6 +129,12 @@ class UploadService {
return clusterLivePhotoFiles(mediaFiles); return clusterLivePhotoFiles(mediaFiles);
} }
constructPublicMagicMetadata(
publicMagicMetadataProps: FilePublicMagicMetadataProps
) {
return constructPublicMagicMetadata(publicMagicMetadataProps);
}
async encryptAsset( async encryptAsset(
worker: any, worker: any,
file: FileWithMetadata, file: FileWithMetadata,
@ -188,6 +196,7 @@ class UploadService {
objectKey: thumbnailObjectKey, objectKey: thumbnailObjectKey,
}, },
metadata: file.metadata, metadata: file.metadata,
pubMagicMetadata: file.pubMagicMetadata,
}; };
return backupedFile; return backupedFile;
} catch (e) { } catch (e) {

View file

@ -1,4 +1,4 @@
import { EnteFile } from 'types/file'; import { EnteFile, FilePublicMagicMetadata } from 'types/file';
import { handleUploadError, CustomError } from 'utils/error'; import { handleUploadError, CustomError } from 'utils/error';
import { logError } from 'utils/sentry'; import { logError } from 'utils/sentry';
import { findMatchingExistingFiles } from 'utils/upload'; import { findMatchingExistingFiles } from 'utils/upload';
@ -10,6 +10,7 @@ import {
FileWithCollection, FileWithCollection,
BackupedFile, BackupedFile,
UploadFile, UploadFile,
FileWithMetadata,
FileTypeInfo, FileTypeInfo,
} from 'types/upload'; } from 'types/upload';
import { addLocalLog, addLogLine } from 'utils/logging'; import { addLocalLog, addLogLine } from 'utils/logging';
@ -17,6 +18,7 @@ import { convertBytesToHumanReadable } from 'utils/file/size';
import { sleep } from 'utils/common'; import { sleep } from 'utils/common';
import { addToCollection } from 'services/collectionService'; import { addToCollection } from 'services/collectionService';
import uploadCancelService from './uploadCancelService'; import uploadCancelService from './uploadCancelService';
import uploadService from './uploadService';
interface UploadResponse { interface UploadResponse {
fileUploadResult: UPLOAD_RESULT; fileUploadResult: UPLOAD_RESULT;
@ -118,14 +120,18 @@ export default async function uploader(
if (file.hasStaticThumbnail) { if (file.hasStaticThumbnail) {
metadata.hasStaticThumbnail = true; metadata.hasStaticThumbnail = true;
} }
let pubMagicMetadata: FilePublicMagicMetadata;
if (uploaderName) { if (uploaderName) {
metadata.uploaderName = uploaderName; pubMagicMetadata = await uploadService.constructPublicMagicMetadata(
{ uploaderName }
);
} }
const fileWithMetadata = { const fileWithMetadata: FileWithMetadata = {
localID, localID,
filedata: file.filedata, filedata: file.filedata,
thumbnail: file.thumbnail, thumbnail: file.thumbnail,
metadata, metadata,
pubMagicMetadata,
}; };
if (uploadCancelService.isUploadCancelationRequested()) { if (uploadCancelService.isUploadCancelationRequested()) {

View file

@ -20,6 +20,7 @@ export interface FilePublicMagicMetadataProps {
editedTime?: number; editedTime?: number;
editedName?: string; editedName?: string;
caption?: string; caption?: string;
uploaderName?: string;
} }
export interface FilePublicMagicMetadata export interface FilePublicMagicMetadata

View file

@ -21,7 +21,7 @@ export enum SUB_TYPE {
} }
export const NEW_FILE_MAGIC_METADATA: MagicMetadataCore = { export const NEW_FILE_MAGIC_METADATA: MagicMetadataCore = {
version: 0, version: 1,
data: {}, data: {},
header: null, header: null,
count: 0, count: 0,

View file

@ -1,6 +1,7 @@
import { FILE_TYPE } from 'constants/file'; import { FILE_TYPE } from 'constants/file';
import { Collection } from 'types/collection'; import { Collection } from 'types/collection';
import { fileAttribute } from 'types/file'; import { fileAttribute, FilePublicMagicMetadata } from 'types/file';
import { EncryptedMagicMetadataCore } from 'types/magicMetadata';
export interface DataStream { export interface DataStream {
stream: ReadableStream<Uint8Array>; stream: ReadableStream<Uint8Array>;
@ -27,7 +28,6 @@ export interface Metadata {
hash?: string; hash?: string;
imageHash?: string; imageHash?: string;
videoHash?: string; videoHash?: string;
uploaderName?: string;
} }
export interface Location { export interface Location {
@ -114,6 +114,7 @@ export interface FileWithMetadata
extends Omit<FileInMemory, 'hasStaticThumbnail'> { extends Omit<FileInMemory, 'hasStaticThumbnail'> {
metadata: Metadata; metadata: Metadata;
localID: number; localID: number;
pubMagicMetadata: FilePublicMagicMetadata;
} }
export interface EncryptedFile { export interface EncryptedFile {
@ -124,6 +125,7 @@ export interface ProcessedFile {
file: fileAttribute; file: fileAttribute;
thumbnail: fileAttribute; thumbnail: fileAttribute;
metadata: fileAttribute; metadata: fileAttribute;
pubMagicMetadata: EncryptedMagicMetadataCore;
localID: number; localID: number;
} }
export interface BackupedFile extends Omit<ProcessedFile, 'localID'> {} export interface BackupedFile extends Omit<ProcessedFile, 'localID'> {}

View file

@ -685,7 +685,7 @@ const englishConstants = {
'Are you sure you want to disable public sharing?', 'Are you sure you want to disable public sharing?',
FILE_DOWNLOAD: 'Allow downloads', FILE_DOWNLOAD: 'Allow downloads',
LINK_PASSWORD_LOCK: 'Password lock', LINK_PASSWORD_LOCK: 'Password lock',
PUBLIC_COLLECT: 'Public collect', PUBLIC_COLLECT: 'Allow adding photos',
LINK_DEVICE_LIMIT: 'Device limit', LINK_DEVICE_LIMIT: 'Device limit',
LINK_EXPIRY: 'Link expiry', LINK_EXPIRY: 'Link expiry',
LINK_EXPIRY_NEVER: 'Never', LINK_EXPIRY_NEVER: 'Never',
@ -698,17 +698,10 @@ const englishConstants = {
</p>{' '} </p>{' '}
<p> <p>
Viewers can still take screenshots or save a copy of your photos Viewers can still take screenshots or save a copy of your photos
using external tools'{' '} using external tools{' '}
</p> </p>
</> </>
), ),
ENABLE_PUBLIC_COLLECT: 'Enable public collect',
ENABLE_PUBLIC_COLLECT_MESSAGE: () => (
<>
<p>Are you sure that you want to enable public collect? </p>{' '}
<p>Uploaded files will be counted towards your storage</p>
</>
),
ABUSE_REPORT: 'Abuse report', ABUSE_REPORT: 'Abuse report',
ABUSE_REPORT_BUTTON_TEXT: 'Report abuse?', ABUSE_REPORT_BUTTON_TEXT: 'Report abuse?',
MALICIOUS_CONTENT: 'Contains malicious content', MALICIOUS_CONTENT: 'Contains malicious content',
@ -890,6 +883,7 @@ const englishConstants = {
</p> </p>
</> </>
), ),
ADD_X_PHOTOS: (x: number) => `Add ${x} ${x > 1 ? 'photos' : 'photo'}`,
}; };
export default englishConstants; export default englishConstants;