commit
aa305cc8d9
|
@ -26,8 +26,6 @@ module.exports = {
|
|||
'base-uri ': "'self'",
|
||||
'frame-ancestors': " 'none'",
|
||||
'form-action': "'none'",
|
||||
'report-uri': 'https://csp-reporter.ente.io',
|
||||
'report-to': 'https://csp-reporter.ente.io',
|
||||
},
|
||||
|
||||
WORKBOX_CONFIG: {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as Sentry from '@sentry/nextjs';
|
||||
import { getSentryTunnelUrl } from 'utils/common/apiUtil';
|
||||
import { getSentryTunnelURL } from 'utils/common/apiUtil';
|
||||
import { getUserAnonymizedID } from 'utils/user';
|
||||
import {
|
||||
getSentryDSN,
|
||||
|
@ -21,7 +21,7 @@ Sentry.init({
|
|||
release: SENTRY_RELEASE,
|
||||
attachStacktrace: true,
|
||||
autoSessionTracking: false,
|
||||
tunnel: getSentryTunnelUrl(),
|
||||
tunnel: getSentryTunnelURL(),
|
||||
// ...
|
||||
// Note: if you want to override the automatic release value, do not set a
|
||||
// `release` value here - use the environment variable `SENTRY_RELEASE`, so
|
||||
|
|
|
@ -209,7 +209,7 @@ const PhotoFrame = ({
|
|||
});
|
||||
}, [files, deleted, search, activeCollection]);
|
||||
|
||||
const updateUrl = (index: number) => (url: string) => {
|
||||
const updateURL = (index: number) => (url: string) => {
|
||||
files[index] = {
|
||||
...files[index],
|
||||
msrc: url,
|
||||
|
@ -240,7 +240,7 @@ const PhotoFrame = ({
|
|||
setFiles(files);
|
||||
};
|
||||
|
||||
const updateSrcUrl = async (index: number, url: string) => {
|
||||
const updateSrcURL = async (index: number, url: string) => {
|
||||
files[index] = {
|
||||
...files[index],
|
||||
w: window.innerWidth,
|
||||
|
@ -337,7 +337,7 @@ const PhotoFrame = ({
|
|||
selected[file[index].id] ?? false
|
||||
}`}
|
||||
file={file[index]}
|
||||
updateUrl={updateUrl(file[index].dataIndex)}
|
||||
updateURL={updateURL(file[index].dataIndex)}
|
||||
onClick={onThumbnailClick(index)}
|
||||
selectable={!isSharedCollection}
|
||||
onSelect={handleSelect(file[index].id, index)}
|
||||
|
@ -370,7 +370,7 @@ const PhotoFrame = ({
|
|||
url = await DownloadManager.getThumbnail(item);
|
||||
galleryContext.thumbs.set(item.id, url);
|
||||
}
|
||||
updateUrl(item.dataIndex)(url);
|
||||
updateURL(item.dataIndex)(url);
|
||||
item.msrc = url;
|
||||
if (!item.src) {
|
||||
item.src = url;
|
||||
|
@ -397,7 +397,7 @@ const PhotoFrame = ({
|
|||
url = await DownloadManager.getFile(item, true);
|
||||
galleryContext.files.set(item.id, url);
|
||||
}
|
||||
await updateSrcUrl(item.dataIndex, url);
|
||||
await updateSrcURL(item.dataIndex, url);
|
||||
item.html = files[item.dataIndex].html;
|
||||
item.src = files[item.dataIndex].src;
|
||||
item.w = files[item.dataIndex].w;
|
||||
|
|
|
@ -91,9 +91,11 @@ const renderInfoItem = (label: string, value: string | JSX.Element) => (
|
|||
);
|
||||
|
||||
function RenderCreationTime({
|
||||
shouldDisableEdits,
|
||||
file,
|
||||
scheduleUpdate,
|
||||
}: {
|
||||
shouldDisableEdits: boolean;
|
||||
file: EnteFile;
|
||||
scheduleUpdate: () => void;
|
||||
}) {
|
||||
|
@ -160,7 +162,8 @@ function RenderCreationTime({
|
|||
<Value
|
||||
width={isInEditMode ? '20%' : '10%'}
|
||||
style={{ cursor: 'pointer', marginLeft: '10px' }}>
|
||||
{!isInEditMode ? (
|
||||
{!shouldDisableEdits &&
|
||||
(!isInEditMode ? (
|
||||
<IconButton onClick={openEditMode}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
|
@ -177,7 +180,7 @@ function RenderCreationTime({
|
|||
<CloseIcon />
|
||||
</IconButton>
|
||||
</>
|
||||
)}
|
||||
))}
|
||||
</Value>
|
||||
</Row>
|
||||
</>
|
||||
|
@ -275,9 +278,11 @@ const FileNameEditForm = ({ filename, saveEdits, discardEdits, extension }) => {
|
|||
};
|
||||
|
||||
function RenderFileName({
|
||||
shouldDisableEdits,
|
||||
file,
|
||||
scheduleUpdate,
|
||||
}: {
|
||||
shouldDisableEdits: boolean;
|
||||
file: EnteFile;
|
||||
scheduleUpdate: () => void;
|
||||
}) {
|
||||
|
@ -322,13 +327,18 @@ function RenderFileName({
|
|||
{getFileTitle(filename, extension)}
|
||||
</FreeFlowText>
|
||||
</Value>
|
||||
{!shouldDisableEdits && (
|
||||
<Value
|
||||
width="10%"
|
||||
style={{ cursor: 'pointer', marginLeft: '10px' }}>
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
marginLeft: '10px',
|
||||
}}>
|
||||
<IconButton onClick={openEditMode}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</Value>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<FileNameEditForm
|
||||
|
@ -396,6 +406,7 @@ function ExifData(props: { exif: any }) {
|
|||
}
|
||||
|
||||
function InfoModal({
|
||||
shouldDisableEdits,
|
||||
showInfo,
|
||||
handleCloseInfo,
|
||||
items,
|
||||
|
@ -419,12 +430,14 @@ function InfoModal({
|
|||
)}
|
||||
{metadata?.title && (
|
||||
<RenderFileName
|
||||
shouldDisableEdits={shouldDisableEdits}
|
||||
file={items[photoSwipe?.getCurrentIndex()]}
|
||||
scheduleUpdate={scheduleUpdate}
|
||||
/>
|
||||
)}
|
||||
{metadata?.creationTime && (
|
||||
<RenderCreationTime
|
||||
shouldDisableEdits={shouldDisableEdits}
|
||||
file={items[photoSwipe?.getCurrentIndex()]}
|
||||
scheduleUpdate={scheduleUpdate}
|
||||
/>
|
||||
|
@ -744,6 +757,7 @@ function PhotoSwipe(props: Iprops) {
|
|||
</div>
|
||||
</div>
|
||||
<InfoModal
|
||||
shouldDisableEdits={props.isSharedCollection}
|
||||
showInfo={showInfo}
|
||||
handleCloseInfo={handleCloseInfo}
|
||||
items={items}
|
||||
|
|
|
@ -39,12 +39,13 @@ function RecoveryKeyModal({ somethingWentWrong, ...props }: Props) {
|
|||
return;
|
||||
}
|
||||
const main = async () => {
|
||||
try {
|
||||
const recoveryKey = await getRecoveryKey();
|
||||
if (!recoveryKey) {
|
||||
setRecoveryKey(bip39.entropyToMnemonic(recoveryKey));
|
||||
} catch (e) {
|
||||
somethingWentWrong();
|
||||
props.onHide();
|
||||
}
|
||||
setRecoveryKey(bip39.entropyToMnemonic(recoveryKey));
|
||||
};
|
||||
main();
|
||||
}, [props.show]);
|
||||
|
|
|
@ -238,7 +238,9 @@ export default function Sidebar(props: Props) {
|
|||
onHide={() => setRecoveryModalView(false)}
|
||||
somethingWentWrong={() =>
|
||||
props.setDialogMessage({
|
||||
title: constants.RECOVER_KEY_GENERATION_FAILED,
|
||||
title: constants.ERROR,
|
||||
content:
|
||||
constants.RECOVER_KEY_GENERATION_FAILED,
|
||||
close: { variant: 'danger' },
|
||||
})
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ function CollectionSelector({
|
|||
<CollectionCard>
|
||||
<PreviewCard
|
||||
file={item.file}
|
||||
updateUrl={() => {}}
|
||||
updateURL={() => {}}
|
||||
onSelect={() => {}}
|
||||
forcedEnable
|
||||
/>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Form, Modal, Button } from 'react-bootstrap';
|
||||
import constants from 'utils/strings/constants';
|
||||
import styled, { css } from 'styled-components';
|
||||
|
@ -21,9 +21,10 @@ import { reverseString } from 'utils/common';
|
|||
import { SetDialogMessage } from 'components/MessageDialog';
|
||||
import ArrowEast from 'components/icons/ArrowEast';
|
||||
import LinkButton from './LinkButton';
|
||||
import { DeadCenter } from 'pages/gallery';
|
||||
import { DeadCenter, GalleryContext } from 'pages/gallery';
|
||||
import billingService from 'services/billingService';
|
||||
import { SetLoading } from 'types/gallery';
|
||||
import { logError } from 'utils/sentry';
|
||||
|
||||
export const PlanIcon = styled.div<{ currentlySubscribed: boolean }>`
|
||||
border-radius: 20px;
|
||||
|
@ -86,6 +87,8 @@ function PlanSelector(props: Props) {
|
|||
const subscription: Subscription = getUserSubscription();
|
||||
const [plans, setPlans] = useState<Plan[]>(null);
|
||||
const [planPeriod, setPlanPeriod] = useState<PLAN_PERIOD>(PLAN_PERIOD.YEAR);
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
|
||||
const togglePeriod = () => {
|
||||
setPlanPeriod((prevPeriod) =>
|
||||
prevPeriod === PLAN_PERIOD.MONTH
|
||||
|
@ -93,9 +96,16 @@ function PlanSelector(props: Props) {
|
|||
: PLAN_PERIOD.MONTH
|
||||
);
|
||||
};
|
||||
function onReopenClick() {
|
||||
galleryContext.closeMessageDialog();
|
||||
galleryContext.showPlanSelectorModal();
|
||||
}
|
||||
useEffect(() => {
|
||||
if (props.modalView) {
|
||||
if (!props.modalView) {
|
||||
return;
|
||||
}
|
||||
const main = async () => {
|
||||
try {
|
||||
props.setLoading(true);
|
||||
let plans = await billingService.getPlans();
|
||||
|
||||
|
@ -111,10 +121,24 @@ function PlanSelector(props: Props) {
|
|||
plans = [planForSubscription(subscription), ...plans];
|
||||
}
|
||||
setPlans(plans);
|
||||
} catch (e) {
|
||||
logError(e, 'plan selector modal open failed');
|
||||
props.closeModal();
|
||||
props.setDialogMessage({
|
||||
title: constants.OPEN_PLAN_SELECTOR_MODAL_FAILED,
|
||||
content: constants.UNKNOWN_ERROR,
|
||||
close: { text: 'close', variant: 'danger' },
|
||||
proceed: {
|
||||
text: constants.REOPEN_PLAN_SELECTOR_MODAL,
|
||||
variant: 'success',
|
||||
action: onReopenClick,
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
props.setLoading(false);
|
||||
}
|
||||
};
|
||||
main();
|
||||
}
|
||||
}, [props.modalView]);
|
||||
|
||||
async function onPlanSelect(plan: Plan) {
|
||||
|
|
|
@ -9,7 +9,7 @@ import { GAP_BTW_TILES } from 'constants/gallery';
|
|||
|
||||
interface IProps {
|
||||
file: EnteFile;
|
||||
updateUrl: (url: string) => void;
|
||||
updateURL: (url: string) => void;
|
||||
onClick?: () => void;
|
||||
forcedEnable?: boolean;
|
||||
selectable?: boolean;
|
||||
|
@ -161,7 +161,7 @@ export default function PreviewCard(props: IProps) {
|
|||
const {
|
||||
file,
|
||||
onClick,
|
||||
updateUrl,
|
||||
updateURL,
|
||||
forcedEnable,
|
||||
selectable,
|
||||
selected,
|
||||
|
@ -185,7 +185,7 @@ export default function PreviewCard(props: IProps) {
|
|||
if (!file.src) {
|
||||
file.src = url;
|
||||
}
|
||||
updateUrl(url);
|
||||
updateURL(url);
|
||||
}
|
||||
} catch (e) {
|
||||
// no-op
|
||||
|
|
|
@ -129,10 +129,7 @@ const InProgressSection = (props: InProgressProps) => {
|
|||
<FileList>
|
||||
{fileList.map(({ fileName, progress }) => (
|
||||
<li key={fileName}>
|
||||
{constants.FILE_UPLOAD_PROGRESS(
|
||||
fileName,
|
||||
progress
|
||||
)}
|
||||
{`${fileName} - ${progress}%`}
|
||||
</li>
|
||||
))}
|
||||
</FileList>
|
||||
|
@ -235,10 +232,20 @@ export default function UploadProgress(props: Props) {
|
|||
/>
|
||||
<ResultSection
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={FileUploadResults.SKIPPED}
|
||||
fileUploadResult={FileUploadResults.ALREADY_UPLOADED}
|
||||
sectionTitle={constants.SKIPPED_FILES}
|
||||
sectionInfo={constants.SKIPPED_INFO}
|
||||
/>
|
||||
<ResultSection
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={
|
||||
FileUploadResults.LARGER_THAN_AVAILABLE_STORAGE
|
||||
}
|
||||
sectionTitle={
|
||||
constants.LARGER_THAN_AVAILABLE_STORAGE_UPLOADS
|
||||
}
|
||||
sectionInfo={constants.LARGER_THAN_AVAILABLE_STORAGE_INFO}
|
||||
/>
|
||||
<ResultSection
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={FileUploadResults.UNSUPPORTED}
|
||||
|
|
|
@ -30,10 +30,11 @@ export enum UPLOAD_STAGES {
|
|||
}
|
||||
|
||||
export enum FileUploadResults {
|
||||
FAILED = -1,
|
||||
SKIPPED = -2,
|
||||
UNSUPPORTED = -3,
|
||||
BLOCKED = -4,
|
||||
TOO_LARGE = -5,
|
||||
UPLOADED = 100,
|
||||
FAILED,
|
||||
ALREADY_UPLOADED,
|
||||
UNSUPPORTED,
|
||||
BLOCKED,
|
||||
TOO_LARGE,
|
||||
LARGER_THAN_AVAILABLE_STORAGE,
|
||||
UPLOADED,
|
||||
}
|
||||
|
|
22
src/pages/404.tsx
Normal file
22
src/pages/404.tsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import Container from 'components/Container';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { AppContext } from './_app';
|
||||
|
||||
export default function NotFound() {
|
||||
const appContext = useContext(AppContext);
|
||||
const [loading, setLoading] = useState(true);
|
||||
useEffect(() => {
|
||||
appContext.showNavBar(true);
|
||||
setLoading(false);
|
||||
}, []);
|
||||
return (
|
||||
<Container>
|
||||
{loading ? (
|
||||
<span className="sr-only">Loading...</span>
|
||||
) : (
|
||||
constants.NOT_FOUND
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
}
|
|
@ -479,8 +479,8 @@ type AppContextType = {
|
|||
sharedFiles: File[];
|
||||
resetSharedFiles: () => void;
|
||||
setDisappearingFlashMessage: (message: FlashMessage) => void;
|
||||
redirectUrl: string;
|
||||
setRedirectUrl: (url: string) => void;
|
||||
redirectURL: string;
|
||||
setRedirectURL: (url: string) => void;
|
||||
};
|
||||
|
||||
export enum FLASH_MESSAGE_TYPE {
|
||||
|
@ -510,7 +510,7 @@ export default function App({ Component, err }) {
|
|||
const [sharedFiles, setSharedFiles] = useState<File[]>(null);
|
||||
const [redirectName, setRedirectName] = useState<string>(null);
|
||||
const [flashMessage, setFlashMessage] = useState<FlashMessage>(null);
|
||||
const [redirectUrl, setRedirectUrl] = useState(null);
|
||||
const [redirectURL, setRedirectURL] = useState(null);
|
||||
useEffect(() => {
|
||||
if (
|
||||
!('serviceWorker' in navigator) ||
|
||||
|
@ -644,8 +644,8 @@ export default function App({ Component, err }) {
|
|||
sharedFiles,
|
||||
resetSharedFiles,
|
||||
setDisappearingFlashMessage,
|
||||
redirectUrl,
|
||||
setRedirectUrl,
|
||||
redirectURL,
|
||||
setRedirectURL,
|
||||
}}>
|
||||
{loading ? (
|
||||
<Container>
|
||||
|
|
|
@ -76,9 +76,9 @@ export default function Credentials() {
|
|||
}
|
||||
await SaveKeyInSessionStore(SESSION_KEYS.ENCRYPTION_KEY, key);
|
||||
await decryptAndStoreToken(key);
|
||||
const redirectUrl = appContext.redirectUrl;
|
||||
appContext.setRedirectUrl(null);
|
||||
router.push(redirectUrl ?? PAGES.GALLERY);
|
||||
const redirectURL = appContext.redirectURL;
|
||||
appContext.setRedirectURL(null);
|
||||
router.push(redirectURL ?? PAGES.GALLERY);
|
||||
} catch (e) {
|
||||
logError(e, 'user entered a wrong password');
|
||||
setFieldError('passphrase', constants.INCORRECT_PASSPHRASE);
|
||||
|
|
|
@ -119,6 +119,7 @@ const defaultGalleryContext: GalleryContextType = {
|
|||
thumbs: new Map(),
|
||||
files: new Map(),
|
||||
showPlanSelectorModal: () => null,
|
||||
closeMessageDialog: () => null,
|
||||
setActiveCollection: () => null,
|
||||
syncWithRemote: () => null,
|
||||
};
|
||||
|
@ -144,7 +145,7 @@ export default function Gallery() {
|
|||
collectionID: 0,
|
||||
});
|
||||
const [dialogMessage, setDialogMessage] = useState<MessageAttributes>();
|
||||
const [dialogView, setDialogView] = useState(false);
|
||||
const [messageDialogView, setMessageDialogView] = useState(false);
|
||||
const [planModalView, setPlanModalView] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [collectionSelectorAttributes, setCollectionSelectorAttributes] =
|
||||
|
@ -186,10 +187,13 @@ export default function Gallery() {
|
|||
const [fixCreationTimeAttributes, setFixCreationTimeAttributes] =
|
||||
useState<FixCreationTimeAttributes>(null);
|
||||
|
||||
const showPlanSelectorModal = () => setPlanModalView(true);
|
||||
const closeMessageDialog = () => setMessageDialogView(false);
|
||||
|
||||
useEffect(() => {
|
||||
const key = getKey(SESSION_KEYS.ENCRYPTION_KEY);
|
||||
if (!key) {
|
||||
appContext.setRedirectUrl(router.asPath);
|
||||
appContext.setRedirectURL(router.asPath);
|
||||
router.push(PAGES.ROOT);
|
||||
return;
|
||||
}
|
||||
|
@ -218,7 +222,7 @@ export default function Gallery() {
|
|||
appContext.showNavBar(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => setDialogView(true), [dialogMessage]);
|
||||
useEffect(() => setMessageDialogView(true), [dialogMessage]);
|
||||
|
||||
useEffect(
|
||||
() => collectionSelectorAttributes && setCollectionSelectorView(true),
|
||||
|
@ -536,7 +540,8 @@ export default function Gallery() {
|
|||
<GalleryContext.Provider
|
||||
value={{
|
||||
...defaultGalleryContext,
|
||||
showPlanSelectorModal: () => setPlanModalView(true),
|
||||
showPlanSelectorModal,
|
||||
closeMessageDialog,
|
||||
setActiveCollection,
|
||||
syncWithRemote,
|
||||
}}>
|
||||
|
@ -563,8 +568,8 @@ export default function Gallery() {
|
|||
<AlertBanner bannerMessage={bannerMessage} />
|
||||
<MessageDialog
|
||||
size="lg"
|
||||
show={dialogView}
|
||||
onHide={() => setDialogView(false)}
|
||||
show={messageDialogView}
|
||||
onHide={closeMessageDialog}
|
||||
attributes={dialogMessage}
|
||||
/>
|
||||
<SearchBar
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { getEndpoint, getPaymentsUrl } from 'utils/common/apiUtil';
|
||||
import { getEndpoint, getPaymentsURL } from 'utils/common/apiUtil';
|
||||
import { getToken } from 'utils/common/key';
|
||||
import { setData, LS_KEYS } from 'utils/storage/localStorage';
|
||||
import { convertToHumanReadable } from 'utils/billing';
|
||||
|
@ -154,7 +154,7 @@ class billingService {
|
|||
action: string
|
||||
) {
|
||||
try {
|
||||
window.location.href = `${getPaymentsUrl()}?productID=${productID}&paymentToken=${paymentToken}&action=${action}&redirectURL=${
|
||||
window.location.href = `${getPaymentsURL()}?productID=${productID}&paymentToken=${paymentToken}&action=${action}&redirectURL=${
|
||||
window.location.origin
|
||||
}/gallery`;
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { getToken } from 'utils/common/key';
|
||||
import { getFileUrl, getThumbnailUrl } from 'utils/common/apiUtil';
|
||||
import { getFileURL, getThumbnailURL } from 'utils/common/apiUtil';
|
||||
import CryptoWorker from 'utils/crypto';
|
||||
import {
|
||||
generateStreamFromArrayBuffer,
|
||||
|
@ -13,8 +13,8 @@ import { logError } from 'utils/sentry';
|
|||
import { FILE_TYPE } from 'constants/file';
|
||||
|
||||
class DownloadManager {
|
||||
private fileObjectUrlPromise = new Map<string, Promise<string>>();
|
||||
private thumbnailObjectUrlPromise = new Map<number, Promise<string>>();
|
||||
private fileObjectURLPromise = new Map<string, Promise<string>>();
|
||||
private thumbnailObjectURLPromise = new Map<number, Promise<string>>();
|
||||
|
||||
public async getThumbnail(file: EnteFile) {
|
||||
try {
|
||||
|
@ -22,10 +22,18 @@ class DownloadManager {
|
|||
if (!token) {
|
||||
return null;
|
||||
}
|
||||
if (!this.thumbnailObjectUrlPromise.get(file.id)) {
|
||||
if (!this.thumbnailObjectURLPromise.get(file.id)) {
|
||||
const downloadPromise = async () => {
|
||||
const thumbnailCache = await caches.open('thumbs');
|
||||
const cacheResp: Response = await thumbnailCache.match(
|
||||
const thumbnailCache = await (async () => {
|
||||
try {
|
||||
return await caches.open('thumbs');
|
||||
} catch (e) {
|
||||
return null;
|
||||
// ignore
|
||||
}
|
||||
})();
|
||||
|
||||
const cacheResp: Response = await thumbnailCache?.match(
|
||||
file.id.toString()
|
||||
);
|
||||
if (cacheResp) {
|
||||
|
@ -34,7 +42,7 @@ class DownloadManager {
|
|||
const thumb = await this.downloadThumb(token, file);
|
||||
const thumbBlob = new Blob([thumb]);
|
||||
try {
|
||||
await thumbnailCache.put(
|
||||
await thumbnailCache?.put(
|
||||
file.id.toString(),
|
||||
new Response(thumbBlob)
|
||||
);
|
||||
|
@ -43,12 +51,12 @@ class DownloadManager {
|
|||
}
|
||||
return URL.createObjectURL(thumbBlob);
|
||||
};
|
||||
this.thumbnailObjectUrlPromise.set(file.id, downloadPromise());
|
||||
this.thumbnailObjectURLPromise.set(file.id, downloadPromise());
|
||||
}
|
||||
|
||||
return await this.thumbnailObjectUrlPromise.get(file.id);
|
||||
return await this.thumbnailObjectURLPromise.get(file.id);
|
||||
} catch (e) {
|
||||
this.thumbnailObjectUrlPromise.delete(file.id);
|
||||
this.thumbnailObjectURLPromise.delete(file.id);
|
||||
logError(e, 'get preview Failed');
|
||||
throw e;
|
||||
}
|
||||
|
@ -56,7 +64,7 @@ class DownloadManager {
|
|||
|
||||
downloadThumb = async (token: string, file: EnteFile) => {
|
||||
const resp = await HTTPService.get(
|
||||
getThumbnailUrl(file.id),
|
||||
getThumbnailURL(file.id),
|
||||
null,
|
||||
{ 'X-Auth-Token': token },
|
||||
{ responseType: 'arraybuffer' }
|
||||
|
@ -84,23 +92,23 @@ class DownloadManager {
|
|||
}
|
||||
return URL.createObjectURL(fileBlob);
|
||||
};
|
||||
if (!this.fileObjectUrlPromise.get(fileKey)) {
|
||||
this.fileObjectUrlPromise.set(
|
||||
if (!this.fileObjectURLPromise.get(fileKey)) {
|
||||
this.fileObjectURLPromise.set(
|
||||
fileKey,
|
||||
getFilePromise(shouldBeConverted)
|
||||
);
|
||||
}
|
||||
const fileURL = await this.fileObjectUrlPromise.get(fileKey);
|
||||
const fileURL = await this.fileObjectURLPromise.get(fileKey);
|
||||
return fileURL;
|
||||
} catch (e) {
|
||||
this.fileObjectUrlPromise.delete(fileKey);
|
||||
this.fileObjectURLPromise.delete(fileKey);
|
||||
logError(e, 'Failed to get File');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
public async getCachedOriginalFile(file: EnteFile) {
|
||||
return await this.fileObjectUrlPromise.get(file.id.toString());
|
||||
return await this.fileObjectURLPromise.get(file.id.toString());
|
||||
}
|
||||
|
||||
async downloadFile(file: EnteFile) {
|
||||
|
@ -114,7 +122,7 @@ class DownloadManager {
|
|||
file.metadata.fileType === FILE_TYPE.LIVE_PHOTO
|
||||
) {
|
||||
const resp = await HTTPService.get(
|
||||
getFileUrl(file.id),
|
||||
getFileURL(file.id),
|
||||
null,
|
||||
{ 'X-Auth-Token': token },
|
||||
{ responseType: 'arraybuffer' }
|
||||
|
@ -126,7 +134,7 @@ class DownloadManager {
|
|||
);
|
||||
return generateStreamFromArrayBuffer(decrypted);
|
||||
}
|
||||
const resp = await fetch(getFileUrl(file.id), {
|
||||
const resp = await fetch(getFileURL(file.id), {
|
||||
headers: {
|
||||
'X-Auth-Token': token,
|
||||
},
|
||||
|
|
|
@ -129,6 +129,7 @@ export const updateTrash = async (
|
|||
} catch (e) {
|
||||
logError(e, 'Get trash files failed');
|
||||
}
|
||||
return currentTrash;
|
||||
};
|
||||
|
||||
function removeDuplicates(trash: Trash) {
|
||||
|
|
|
@ -85,11 +85,11 @@ export async function updateFileCreationDateInEXIF(
|
|||
|
||||
export async function convertImageToDataURL(reader: FileReader, url: string) {
|
||||
const blob = await fetch(url).then((r) => r.blob());
|
||||
const dataUrl = await new Promise<string>((resolve) => {
|
||||
const dataURL = await new Promise<string>((resolve) => {
|
||||
reader.onload = () => resolve(reader.result as string);
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
return dataUrl;
|
||||
return dataURL;
|
||||
}
|
||||
|
||||
function dataURIToBlob(dataURI) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
FileUploadResults,
|
||||
RANDOM_PERCENTAGE_PROGRESS_FOR_PUT,
|
||||
UPLOAD_STAGES,
|
||||
} from 'constants/upload';
|
||||
|
@ -9,7 +10,7 @@ class UIService {
|
|||
private filesUploaded: number;
|
||||
private totalFileCount: number;
|
||||
private fileProgress: Map<string, number>;
|
||||
private uploadResult: Map<string, number>;
|
||||
private uploadResult: Map<string, FileUploadResults>;
|
||||
private progressUpdater: ProgressUpdater;
|
||||
|
||||
init(progressUpdater: ProgressUpdater) {
|
||||
|
@ -47,8 +48,8 @@ class UIService {
|
|||
this.updateProgressBarUI();
|
||||
}
|
||||
|
||||
moveFileToResultList(filename: string) {
|
||||
this.uploadResult.set(filename, this.fileProgress.get(filename));
|
||||
moveFileToResultList(filename: string, uploadResult: FileUploadResults) {
|
||||
this.uploadResult.set(filename, uploadResult);
|
||||
this.fileProgress.delete(filename);
|
||||
this.updateProgressBarUI();
|
||||
}
|
||||
|
|
|
@ -185,7 +185,11 @@ class UploadManager {
|
|||
this.failedFiles.push(fileWithCollection);
|
||||
}
|
||||
|
||||
UIService.moveFileToResultList(fileWithCollection.file.name);
|
||||
UIService.moveFileToResultList(
|
||||
fileWithCollection.file.name,
|
||||
fileUploadResult
|
||||
);
|
||||
UploadService.reducePendingUploadCount();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,10 @@ class UploadService {
|
|||
await this.preFetchUploadURLs();
|
||||
}
|
||||
|
||||
reducePendingUploadCount() {
|
||||
this.pendingUploadCount--;
|
||||
}
|
||||
|
||||
async readFile(
|
||||
worker: any,
|
||||
reader: FileReader,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { EnteFile } from 'types/file';
|
||||
import { sleep } from 'utils/common';
|
||||
import { handleUploadError, CustomError } from 'utils/error';
|
||||
import { decryptFile } from 'utils/file';
|
||||
import { logError } from 'utils/sentry';
|
||||
|
@ -22,7 +21,6 @@ import {
|
|||
import { FILE_TYPE } from 'constants/file';
|
||||
import { FileUploadResults } from 'constants/upload';
|
||||
|
||||
const TwoSecondInMillSeconds = 2000;
|
||||
const FIVE_GB_IN_BYTES = 5 * 1024 * 1024 * 1024;
|
||||
interface UploadResponse {
|
||||
fileUploadResult: FileUploadResults;
|
||||
|
@ -46,12 +44,6 @@ export default async function uploader(
|
|||
|
||||
try {
|
||||
if (rawFile.size >= FIVE_GB_IN_BYTES) {
|
||||
UIService.setFileProgress(
|
||||
rawFile.name,
|
||||
FileUploadResults.TOO_LARGE
|
||||
);
|
||||
// wait two second before removing the file from the progress in file section
|
||||
await sleep(TwoSecondInMillSeconds);
|
||||
return { fileUploadResult: FileUploadResults.TOO_LARGE };
|
||||
}
|
||||
fileTypeInfo = await getFileType(reader, rawFile);
|
||||
|
@ -65,10 +57,7 @@ export default async function uploader(
|
|||
);
|
||||
|
||||
if (fileAlreadyInCollection(existingFilesInCollection, metadata)) {
|
||||
UIService.setFileProgress(rawFile.name, FileUploadResults.SKIPPED);
|
||||
// wait two second before removing the file from the progress in file section
|
||||
await sleep(TwoSecondInMillSeconds);
|
||||
return { fileUploadResult: FileUploadResults.SKIPPED };
|
||||
return { fileUploadResult: FileUploadResults.ALREADY_UPLOADED };
|
||||
}
|
||||
|
||||
file = await UploadService.readFile(
|
||||
|
@ -105,7 +94,6 @@ export default async function uploader(
|
|||
const uploadedFile = await UploadHttpClient.uploadFile(uploadFile);
|
||||
const decryptedFile = await decryptFile(uploadedFile, collection);
|
||||
|
||||
UIService.setFileProgress(rawFile.name, FileUploadResults.UPLOADED);
|
||||
UIService.increaseFileUploaded();
|
||||
return {
|
||||
fileUploadResult: FileUploadResults.UPLOADED,
|
||||
|
@ -118,29 +106,15 @@ export default async function uploader(
|
|||
const error = handleUploadError(e);
|
||||
switch (error.message) {
|
||||
case CustomError.ETAG_MISSING:
|
||||
UIService.setFileProgress(
|
||||
rawFile.name,
|
||||
FileUploadResults.BLOCKED
|
||||
);
|
||||
return { fileUploadResult: FileUploadResults.BLOCKED };
|
||||
case CustomError.UNSUPPORTED_FILE_FORMAT:
|
||||
UIService.setFileProgress(
|
||||
rawFile.name,
|
||||
FileUploadResults.UNSUPPORTED
|
||||
);
|
||||
return { fileUploadResult: FileUploadResults.UNSUPPORTED };
|
||||
|
||||
case CustomError.FILE_TOO_LARGE:
|
||||
UIService.setFileProgress(
|
||||
rawFile.name,
|
||||
FileUploadResults.TOO_LARGE
|
||||
);
|
||||
return { fileUploadResult: FileUploadResults.TOO_LARGE };
|
||||
return {
|
||||
fileUploadResult:
|
||||
FileUploadResults.LARGER_THAN_AVAILABLE_STORAGE,
|
||||
};
|
||||
default:
|
||||
UIService.setFileProgress(
|
||||
rawFile.name,
|
||||
FileUploadResults.FAILED
|
||||
);
|
||||
return { fileUploadResult: FileUploadResults.FAILED };
|
||||
}
|
||||
} finally {
|
||||
|
|
|
@ -72,13 +72,21 @@ export const setRecoveryKey = (token: string, recoveryKey: RecoveryKey) =>
|
|||
});
|
||||
|
||||
export const logoutUser = async () => {
|
||||
try {
|
||||
// ignore server logout result as logoutUser can be triggered before sign up or on token expiry
|
||||
await _logout();
|
||||
clearKeys();
|
||||
clearData();
|
||||
try {
|
||||
await caches.delete('thumbs');
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
await clearFiles();
|
||||
router.push(PAGES.ROOT);
|
||||
} catch (e) {
|
||||
logError(e, 'logoutUser failed');
|
||||
}
|
||||
};
|
||||
|
||||
export const clearFiles = async () => {
|
||||
|
|
|
@ -26,6 +26,7 @@ export type GalleryContextType = {
|
|||
thumbs: Map<number, string>;
|
||||
files: Map<number, string>;
|
||||
showPlanSelectorModal: () => void;
|
||||
closeMessageDialog: () => void;
|
||||
setActiveCollection: (collection: number) => void;
|
||||
syncWithRemote: (force?: boolean, silent?: boolean) => Promise<void>;
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ export const getEndpoint = () => {
|
|||
return endPoint;
|
||||
};
|
||||
|
||||
export const getFileUrl = (id: number) => {
|
||||
export const getFileURL = (id: number) => {
|
||||
if (process.env.NEXT_PUBLIC_ENTE_ENDPOINT !== undefined) {
|
||||
return (
|
||||
`${process.env.NEXT_PUBLIC_ENTE_ENDPOINT}/files/download/${id}` ??
|
||||
|
@ -14,7 +14,7 @@ export const getFileUrl = (id: number) => {
|
|||
return `https://files.ente.io/?fileID=${id}`;
|
||||
};
|
||||
|
||||
export const getThumbnailUrl = (id: number) => {
|
||||
export const getThumbnailURL = (id: number) => {
|
||||
if (process.env.NEXT_PUBLIC_ENTE_ENDPOINT !== undefined) {
|
||||
return (
|
||||
`${process.env.NEXT_PUBLIC_ENTE_ENDPOINT}/files/preview/${id}` ??
|
||||
|
@ -24,11 +24,11 @@ export const getThumbnailUrl = (id: number) => {
|
|||
return `https://thumbnails.ente.io/?fileID=${id}`;
|
||||
};
|
||||
|
||||
export const getSentryTunnelUrl = () => {
|
||||
export const getSentryTunnelURL = () => {
|
||||
return `https://sentry-reporter.ente.io`;
|
||||
};
|
||||
|
||||
export const getPaymentsUrl = () => {
|
||||
export const getPaymentsURL = () => {
|
||||
if (process.env.NEXT_PUBLIC_ENTE_ENDPOINT !== undefined) {
|
||||
return process.env.NEXT_PUBLIC_ENTE_PAYMENT_ENDPOINT;
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ export const SaveKeyInSessionStore = async (
|
|||
};
|
||||
|
||||
export const getRecoveryKey = async () => {
|
||||
let recoveryKey = null;
|
||||
let recoveryKey: string = null;
|
||||
try {
|
||||
const cryptoWorker = await new CryptoWorker();
|
||||
|
||||
|
@ -129,6 +129,7 @@ export const getRecoveryKey = async () => {
|
|||
return recoveryKey;
|
||||
} catch (e) {
|
||||
logError(e, 'getRecoveryKey failed');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -138,7 +139,7 @@ async function createNewRecoveryKey() {
|
|||
|
||||
const cryptoWorker = await new CryptoWorker();
|
||||
|
||||
const recoveryKey = await cryptoWorker.generateEncryptionKey();
|
||||
const recoveryKey: string = await cryptoWorker.generateEncryptionKey();
|
||||
const encryptedMasterKey: B64EncryptionResult =
|
||||
await cryptoWorker.encryptToB64(masterKey, recoveryKey);
|
||||
const encryptedRecoveryKey: B64EncryptionResult =
|
||||
|
|
|
@ -4,7 +4,6 @@ import localForage from 'localforage';
|
|||
|
||||
if (runningInBrowser()) {
|
||||
localForage.config({
|
||||
driver: localForage.INDEXEDDB,
|
||||
name: 'ente-files',
|
||||
version: 1.0,
|
||||
storeName: 'files',
|
||||
|
|
|
@ -109,26 +109,6 @@ const englishConstants = {
|
|||
},
|
||||
UPLOADING_FILES: 'file upload',
|
||||
FILE_NOT_UPLOADED_LIST: 'the following files were not uploaded',
|
||||
FILE_UPLOAD_PROGRESS: (name: string, progress: number) => (
|
||||
<div id={name}>
|
||||
{name}
|
||||
{' - '}
|
||||
<span style={{ color: '#eee' }}>
|
||||
{(() => {
|
||||
switch (progress) {
|
||||
case -1:
|
||||
return 'failed';
|
||||
case -2:
|
||||
return 'already uploaded, skipping...';
|
||||
case -3:
|
||||
return 'unsupported file format, skipping....';
|
||||
default:
|
||||
return `${progress}%`;
|
||||
}
|
||||
})()}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
SUBSCRIPTION_EXPIRED: (action) => (
|
||||
<>
|
||||
your subscription has expired, please a{' '}
|
||||
|
@ -546,8 +526,11 @@ const englishConstants = {
|
|||
BLOCKED_UPLOADS: 'blocked uploads',
|
||||
INPROGRESS_UPLOADS: 'uploads in progress',
|
||||
TOO_LARGE_UPLOADS: 'large files',
|
||||
TOO_LARGE_INFO:
|
||||
LARGER_THAN_AVAILABLE_STORAGE_UPLOADS: 'insufficient storage',
|
||||
LARGER_THAN_AVAILABLE_STORAGE_INFO:
|
||||
'these files were not uploaded as they exceed the maximum size limit for your storage plan',
|
||||
TOO_LARGE_INFO:
|
||||
'these files were not uploaded as they exceed our maximum file size limit',
|
||||
UPLOAD_TO_COLLECTION: 'upload to album',
|
||||
ARCHIVE: 'archive',
|
||||
ALL: 'all',
|
||||
|
@ -621,6 +604,9 @@ const englishConstants = {
|
|||
DATE_TIME_ORIGINAL: 'EXIF:DateTimeOriginal',
|
||||
DATE_TIME_DIGITIZED: 'EXIF:DateTimeDigitized',
|
||||
CUSTOM_TIME: 'custom time',
|
||||
REOPEN_PLAN_SELECTOR_MODAL: 're-open plans',
|
||||
OPEN_PLAN_SELECTOR_MODAL_FAILED: 'failed to open plans',
|
||||
NOT_FOUND: '404 not found',
|
||||
};
|
||||
|
||||
export default englishConstants;
|
||||
|
|
|
@ -4592,9 +4592,9 @@ ms@2.1.2, ms@^2.1.1:
|
|||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
nanoid@^3.1.23:
|
||||
version "3.1.25"
|
||||
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz"
|
||||
integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c"
|
||||
integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==
|
||||
|
||||
native-url@0.3.4:
|
||||
version "0.3.4"
|
||||
|
|
Loading…
Reference in a new issue