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