Switch to prettier defaults
This is unfortunately a huge diff, but now is as good as ever to do this pending chore. - Move to prettier (v3) defaults. The only thing we tweak is the tabWidth (the default is 2, we change it to 4). - Use the prettier plugin to sort imports. We were not changing the defaults of prettier much anyway, the main thing that has changed is the switching from the overridden single quotes to the default double quotes.
This commit is contained in:
parent
0b8d9fafee
commit
c1bf193211
669 changed files with 12321 additions and 12054 deletions
3
.github/.prettierrc.json
vendored
3
.github/.prettierrc.json
vendored
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"tabWidth": 4
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
{
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true,
|
||||
"bracketSameLine": true
|
||||
"plugins": ["prettier-plugin-organize-imports"]
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ module.exports = {
|
|||
// This is required here to ensure desktop picks the right eslint config, where this app is
|
||||
// packaged as a submodule.
|
||||
root: true,
|
||||
extends: ['@ente/eslint-config'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
extends: ["@ente/eslint-config"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json',
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js', 'out'],
|
||||
ignorePatterns: [".eslintrc.js", "out"],
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const nextConfigBase = require('@/next/next.config.base.js');
|
||||
const nextConfigBase = require("@/next/next.config.base.js");
|
||||
|
||||
module.exports = {
|
||||
...nextConfigBase,
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import { initSentry } from '@ente/shared/sentry/config/sentry.config.base';
|
||||
import { initSentry } from "@ente/shared/sentry/config/sentry.config.base";
|
||||
|
||||
initSentry('https://0f7214c7feb9b1dd2fed5db09b42fa1b@sentry.ente.io/5');
|
||||
initSentry("https://0f7214c7feb9b1dd2fed5db09b42fa1b@sentry.ente.io/5");
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
import { CacheProvider } from '@emotion/react';
|
||||
import { APPS, APP_TITLES } from '@ente/shared/apps/constants';
|
||||
import { EnteAppProps } from '@ente/shared/apps/types';
|
||||
import { Overlay } from '@ente/shared/components/Container';
|
||||
import DialogBoxV2 from '@ente/shared/components/DialogBoxV2';
|
||||
import { setupI18n } from "@/ui/i18n";
|
||||
import { CacheProvider } from "@emotion/react";
|
||||
import { APPS, APP_TITLES } from "@ente/shared/apps/constants";
|
||||
import { EnteAppProps } from "@ente/shared/apps/types";
|
||||
import { Overlay } from "@ente/shared/components/Container";
|
||||
import DialogBoxV2 from "@ente/shared/components/DialogBoxV2";
|
||||
import {
|
||||
DialogBoxAttributesV2,
|
||||
SetDialogBoxAttributesV2,
|
||||
} from '@ente/shared/components/DialogBoxV2/types';
|
||||
import EnteSpinner from '@ente/shared/components/EnteSpinner';
|
||||
import AppNavbar from '@ente/shared/components/Navbar/app';
|
||||
import { useLocalState } from '@ente/shared/hooks/useLocalState';
|
||||
import { setupI18n } from '@/ui/i18n';
|
||||
import HTTPService from '@ente/shared/network/HTTPService';
|
||||
import { LS_KEYS, getData } from '@ente/shared/storage/localStorage';
|
||||
import { getTheme } from '@ente/shared/themes';
|
||||
import { THEME_COLOR } from '@ente/shared/themes/constants';
|
||||
import createEmotionCache from '@ente/shared/themes/createEmotionCache';
|
||||
import { CssBaseline, useMediaQuery } from '@mui/material';
|
||||
import { ThemeProvider } from '@mui/material/styles';
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import { createContext, useEffect, useState } from 'react';
|
||||
import 'styles/global.css';
|
||||
} from "@ente/shared/components/DialogBoxV2/types";
|
||||
import EnteSpinner from "@ente/shared/components/EnteSpinner";
|
||||
import AppNavbar from "@ente/shared/components/Navbar/app";
|
||||
import { useLocalState } from "@ente/shared/hooks/useLocalState";
|
||||
import HTTPService from "@ente/shared/network/HTTPService";
|
||||
import { LS_KEYS, getData } from "@ente/shared/storage/localStorage";
|
||||
import { getTheme } from "@ente/shared/themes";
|
||||
import { THEME_COLOR } from "@ente/shared/themes/constants";
|
||||
import createEmotionCache from "@ente/shared/themes/createEmotionCache";
|
||||
import { CssBaseline, useMediaQuery } from "@mui/material";
|
||||
import { ThemeProvider } from "@mui/material/styles";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import { createContext, useEffect, useState } from "react";
|
||||
import "styles/global.css";
|
||||
|
||||
interface AppContextProps {
|
||||
isMobile: boolean;
|
||||
|
@ -50,7 +50,7 @@ export default function App(props: EnteAppProps) {
|
|||
|
||||
const showNavBar = (show: boolean) => setShowNavBar(show);
|
||||
|
||||
const isMobile = useMediaQuery('(max-width:428px)');
|
||||
const isMobile = useMediaQuery("(max-width:428px)");
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
|
@ -70,14 +70,14 @@ export default function App(props: EnteAppProps) {
|
|||
const pkg = getData(LS_KEYS.CLIENT_PACKAGE);
|
||||
if (!pkg) return;
|
||||
HTTPService.setHeaders({
|
||||
'X-Client-Package': pkg.name,
|
||||
"X-Client-Package": pkg.name,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
router.events.on('routeChangeComplete', setupPackageName);
|
||||
router.events.on("routeChangeComplete", setupPackageName);
|
||||
return () => {
|
||||
router.events.off('routeChangeComplete', setupPackageName);
|
||||
router.events.off("routeChangeComplete", setupPackageName);
|
||||
};
|
||||
}, [router.events]);
|
||||
|
||||
|
@ -111,17 +111,19 @@ export default function App(props: EnteAppProps) {
|
|||
showNavBar,
|
||||
setDialogBoxAttributesV2:
|
||||
setDialogBoxAttributesV2 as any,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{!isI18nReady && (
|
||||
<Overlay
|
||||
sx={(theme) => ({
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
zIndex: 2000,
|
||||
backgroundColor: (theme as any).colors
|
||||
.background.base,
|
||||
})}>
|
||||
})}
|
||||
>
|
||||
<EnteSpinner />
|
||||
</Overlay>
|
||||
)}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import DocumentPage, {
|
||||
EnteDocumentProps,
|
||||
} from '@ente/shared/next/pages/_document';
|
||||
} from "@ente/shared/next/pages/_document";
|
||||
|
||||
export default function Document(props: EnteDocumentProps) {
|
||||
return <DocumentPage {...props} />;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { VerticallyCentered } from '@ente/shared/components/Container';
|
||||
import EnteSpinner from '@ente/shared/components/EnteSpinner';
|
||||
import { ACCOUNTS_PAGES } from '@ente/shared/constants/pages';
|
||||
import HTTPService from '@ente/shared/network/HTTPService';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
import { LS_KEYS, getData, setData } from '@ente/shared/storage/localStorage';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect } from 'react';
|
||||
import { VerticallyCentered } from "@ente/shared/components/Container";
|
||||
import EnteSpinner from "@ente/shared/components/EnteSpinner";
|
||||
import { ACCOUNTS_PAGES } from "@ente/shared/constants/pages";
|
||||
import HTTPService from "@ente/shared/network/HTTPService";
|
||||
import { logError } from "@ente/shared/sentry";
|
||||
import { LS_KEYS, getData, setData } from "@ente/shared/storage/localStorage";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const AccountHandoff = () => {
|
||||
const router = useRouter();
|
||||
|
@ -16,26 +16,26 @@ const AccountHandoff = () => {
|
|||
|
||||
router.push(ACCOUNTS_PAGES.PASSKEYS);
|
||||
} catch (e) {
|
||||
logError(e, 'Failed to deserialize and set passed user data');
|
||||
logError(e, "Failed to deserialize and set passed user data");
|
||||
router.push(ACCOUNTS_PAGES.LOGIN);
|
||||
}
|
||||
};
|
||||
|
||||
const getClientPackageName = () => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const pkg = urlParams.get('package');
|
||||
const pkg = urlParams.get("package");
|
||||
if (!pkg) return;
|
||||
setData(LS_KEYS.CLIENT_PACKAGE, { name: pkg });
|
||||
HTTPService.setHeaders({
|
||||
'X-Client-Package': pkg,
|
||||
"X-Client-Package": pkg,
|
||||
});
|
||||
};
|
||||
|
||||
const extractAccountsToken = () => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const token = urlParams.get('token');
|
||||
const token = urlParams.get("token");
|
||||
if (!token) {
|
||||
throw new Error('token not found');
|
||||
throw new Error("token not found");
|
||||
}
|
||||
|
||||
const user = getData(LS_KEYS.USER) || {};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import CredentialPage from '@ente/accounts/pages/credentials';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from '../_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import CredentialPage from "@ente/accounts/pages/credentials";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { useContext } from "react";
|
||||
import { AppContext } from "../_app";
|
||||
|
||||
export default function Credential() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import GeneratePage from '@ente/accounts/pages/generate';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import GeneratePage from "@ente/accounts/pages/generate";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function Generate() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { useRouter } from 'next/router';
|
||||
import { useEffect } from 'react';
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const Index = () => {
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
router.push('/login');
|
||||
router.push("/login");
|
||||
}, []);
|
||||
return <></>;
|
||||
};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import LoginPage from '@ente/accounts/pages/login';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from '../_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import LoginPage from "@ente/accounts/pages/login";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { useContext } from "react";
|
||||
import { AppContext } from "../_app";
|
||||
|
||||
export default function Login() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import DialogBoxV2 from '@ente/shared/components/DialogBoxV2';
|
||||
import EnteButton from '@ente/shared/components/EnteButton';
|
||||
import { Button, Stack, Typography } from '@mui/material';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext, useState } from 'react';
|
||||
import { deletePasskey } from 'services/passkeysService';
|
||||
import { PasskeysContext } from '.';
|
||||
import { t } from 'i18next';
|
||||
import DialogBoxV2 from "@ente/shared/components/DialogBoxV2";
|
||||
import EnteButton from "@ente/shared/components/EnteButton";
|
||||
import { Button, Stack, Typography } from "@mui/material";
|
||||
import { t } from "i18next";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext, useState } from "react";
|
||||
import { deletePasskey } from "services/passkeysService";
|
||||
import { PasskeysContext } from ".";
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
|
@ -41,27 +41,30 @@ const DeletePasskeyModal = (props: IProps) => {
|
|||
onClose={props.onClose}
|
||||
fullScreen={isMobile}
|
||||
attributes={{
|
||||
title: t('DELETE_PASSKEY'),
|
||||
title: t("DELETE_PASSKEY"),
|
||||
secondary: {
|
||||
action: props.onClose,
|
||||
text: t('CANCEL'),
|
||||
text: t("CANCEL"),
|
||||
},
|
||||
}}>
|
||||
<Stack spacing={'8px'}>
|
||||
<Typography>{t('DELETE_PASSKEY_CONFIRMATION')}</Typography>
|
||||
}}
|
||||
>
|
||||
<Stack spacing={"8px"}>
|
||||
<Typography>{t("DELETE_PASSKEY_CONFIRMATION")}</Typography>
|
||||
<EnteButton
|
||||
type="submit"
|
||||
size="large"
|
||||
color="critical"
|
||||
loading={loading}
|
||||
onClick={doDelete}>
|
||||
{t('DELETE')}
|
||||
onClick={doDelete}
|
||||
>
|
||||
{t("DELETE")}
|
||||
</EnteButton>
|
||||
<Button
|
||||
size="large"
|
||||
color={'secondary'}
|
||||
onClick={props.onClose}>
|
||||
{t('CANCEL')}
|
||||
color={"secondary"}
|
||||
onClick={props.onClose}
|
||||
>
|
||||
{t("CANCEL")}
|
||||
</Button>
|
||||
</Stack>
|
||||
</DialogBoxV2>
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
import { EnteDrawer } from '@ente/shared/components/EnteDrawer';
|
||||
import { PasskeysContext } from '.';
|
||||
import { Stack } from '@mui/material';
|
||||
import Titlebar from '@ente/shared/components/Titlebar';
|
||||
import { MenuItemGroup } from '@ente/shared/components/Menu/MenuItemGroup';
|
||||
import { EnteMenuItem } from '@ente/shared/components/Menu/EnteMenuItem';
|
||||
import { useContext, useState } from 'react';
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import MenuItemDivider from '@ente/shared/components/Menu/MenuItemDivider';
|
||||
import DeletePasskeyModal from './DeletePasskeyModal';
|
||||
import RenamePasskeyModal from './RenamePasskeyModal';
|
||||
import InfoItem from '@ente/shared/components/Info/InfoItem';
|
||||
import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
|
||||
import { formatDateTimeFull } from '@ente/shared/time/format';
|
||||
import { t } from 'i18next';
|
||||
import { EnteDrawer } from "@ente/shared/components/EnteDrawer";
|
||||
import InfoItem from "@ente/shared/components/Info/InfoItem";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import MenuItemDivider from "@ente/shared/components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "@ente/shared/components/Menu/MenuItemGroup";
|
||||
import Titlebar from "@ente/shared/components/Titlebar";
|
||||
import { formatDateTimeFull } from "@ente/shared/time/format";
|
||||
import CalendarTodayIcon from "@mui/icons-material/CalendarToday";
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import EditIcon from "@mui/icons-material/Edit";
|
||||
import { Stack } from "@mui/material";
|
||||
import { t } from "i18next";
|
||||
import { useContext, useState } from "react";
|
||||
import { PasskeysContext } from ".";
|
||||
import DeletePasskeyModal from "./DeletePasskeyModal";
|
||||
import RenamePasskeyModal from "./RenamePasskeyModal";
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
|
@ -33,10 +33,11 @@ const ManagePasskeyDrawer = (props: IProps) => {
|
|||
open={props.open}
|
||||
onClose={() => {
|
||||
setShowPasskeyDrawer(false);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{selectedPasskey && (
|
||||
<>
|
||||
<Stack spacing={'4px'} py={'12px'}>
|
||||
<Stack spacing={"4px"} py={"12px"}>
|
||||
<Titlebar
|
||||
onClose={() => {
|
||||
setShowPasskeyDrawer(false);
|
||||
|
@ -48,11 +49,11 @@ const ManagePasskeyDrawer = (props: IProps) => {
|
|||
/>
|
||||
<InfoItem
|
||||
icon={<CalendarTodayIcon />}
|
||||
title={t('CREATED_AT')}
|
||||
title={t("CREATED_AT")}
|
||||
caption={
|
||||
`${formatDateTimeFull(
|
||||
selectedPasskey.createdAt / 1000
|
||||
)}` || ''
|
||||
selectedPasskey.createdAt / 1000,
|
||||
)}` || ""
|
||||
}
|
||||
loading={!selectedPasskey}
|
||||
hideEditOption
|
||||
|
@ -63,7 +64,7 @@ const ManagePasskeyDrawer = (props: IProps) => {
|
|||
setShowRenamePasskeyModal(true);
|
||||
}}
|
||||
startIcon={<EditIcon />}
|
||||
label={'Rename Passkey'}
|
||||
label={"Rename Passkey"}
|
||||
/>
|
||||
<MenuItemDivider />
|
||||
<EnteMenuItem
|
||||
|
@ -71,7 +72,7 @@ const ManagePasskeyDrawer = (props: IProps) => {
|
|||
setShowDeletePasskeyModal(true);
|
||||
}}
|
||||
startIcon={<DeleteIcon />}
|
||||
label={'Delete Passkey'}
|
||||
label={"Delete Passkey"}
|
||||
color="critical"
|
||||
/>
|
||||
</MenuItemGroup>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { EnteMenuItem } from '@ente/shared/components/Menu/EnteMenuItem';
|
||||
import { Passkey } from 'types/passkey';
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
import { useContext } from 'react';
|
||||
import { PasskeysContext } from '.';
|
||||
import KeyIcon from '@mui/icons-material/Key';
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
||||
import KeyIcon from "@mui/icons-material/Key";
|
||||
import { useContext } from "react";
|
||||
import { Passkey } from "types/passkey";
|
||||
import { PasskeysContext } from ".";
|
||||
|
||||
interface IProps {
|
||||
passkey: Passkey;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { MenuItemGroup } from '@ente/shared/components/Menu/MenuItemGroup';
|
||||
import { Passkey } from 'types/passkey';
|
||||
import PasskeyListItem from './PasskeyListItem';
|
||||
import MenuItemDivider from '@ente/shared/components/Menu/MenuItemDivider';
|
||||
import { Fragment } from 'react';
|
||||
import MenuItemDivider from "@ente/shared/components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "@ente/shared/components/Menu/MenuItemGroup";
|
||||
import { Fragment } from "react";
|
||||
import { Passkey } from "types/passkey";
|
||||
import PasskeyListItem from "./PasskeyListItem";
|
||||
|
||||
interface IProps {
|
||||
passkeys: Passkey[];
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import DialogBoxV2 from '@ente/shared/components/DialogBoxV2';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { PasskeysContext } from '.';
|
||||
import SingleInputForm from '@ente/shared/components/SingleInputForm';
|
||||
import { t } from 'i18next';
|
||||
import { renamePasskey } from 'services/passkeysService';
|
||||
import DialogBoxV2 from "@ente/shared/components/DialogBoxV2";
|
||||
import SingleInputForm from "@ente/shared/components/SingleInputForm";
|
||||
import { t } from "i18next";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
import { renamePasskey } from "services/passkeysService";
|
||||
import { PasskeysContext } from ".";
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
|
@ -34,17 +34,18 @@ const RenamePasskeyModal = (props: IProps) => {
|
|||
onClose={props.onClose}
|
||||
fullScreen={isMobile}
|
||||
attributes={{
|
||||
title: t('RENAME_PASSKEY'),
|
||||
title: t("RENAME_PASSKEY"),
|
||||
secondary: {
|
||||
action: props.onClose,
|
||||
text: t('CANCEL'),
|
||||
text: t("CANCEL"),
|
||||
},
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<SingleInputForm
|
||||
initialValue={selectedPasskey?.friendlyName}
|
||||
callback={onSubmit}
|
||||
placeholder={t('ENTER_PASSKEY_NAME')}
|
||||
buttonText={t('RENAME')}
|
||||
placeholder={t("ENTER_PASSKEY_NAME")}
|
||||
buttonText={t("RENAME")}
|
||||
fieldType="text"
|
||||
secondaryButtonAction={props.onClose}
|
||||
submitButtonProps={{ sx: { mt: 1, mb: 2 } }}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import PasskeysFinishPage from '@ente/accounts/pages/passkeys/finish';
|
||||
import PasskeysFinishPage from "@ente/accounts/pages/passkeys/finish";
|
||||
const PasskeysFinish = () => {
|
||||
return <PasskeysFinishPage />;
|
||||
};
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
import { APPS, CLIENT_PACKAGE_NAMES } from '@ente/shared/apps/constants';
|
||||
import { APPS, CLIENT_PACKAGE_NAMES } from "@ente/shared/apps/constants";
|
||||
import {
|
||||
CenteredFlex,
|
||||
VerticallyCentered,
|
||||
} from '@ente/shared/components/Container';
|
||||
import EnteButton from '@ente/shared/components/EnteButton';
|
||||
import EnteSpinner from '@ente/shared/components/EnteSpinner';
|
||||
import FormPaper from '@ente/shared/components/Form/FormPaper';
|
||||
import HTTPService from '@ente/shared/network/HTTPService';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
import { LS_KEYS, setData } from '@ente/shared/storage/localStorage';
|
||||
import InfoIcon from '@mui/icons-material/Info';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { t } from 'i18next';
|
||||
import _sodium from 'libsodium-wrappers';
|
||||
import Image from 'next/image';
|
||||
import { useEffect, useState } from 'react';
|
||||
} from "@ente/shared/components/Container";
|
||||
import EnteButton from "@ente/shared/components/EnteButton";
|
||||
import EnteSpinner from "@ente/shared/components/EnteSpinner";
|
||||
import FormPaper from "@ente/shared/components/Form/FormPaper";
|
||||
import HTTPService from "@ente/shared/network/HTTPService";
|
||||
import { logError } from "@ente/shared/sentry";
|
||||
import { LS_KEYS, setData } from "@ente/shared/storage/localStorage";
|
||||
import InfoIcon from "@mui/icons-material/Info";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { t } from "i18next";
|
||||
import _sodium from "libsodium-wrappers";
|
||||
import Image from "next/image";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
BeginPasskeyAuthenticationResponse,
|
||||
beginPasskeyAuthentication,
|
||||
finishPasskeyAuthentication,
|
||||
} from 'services/passkeysService';
|
||||
} from "services/passkeysService";
|
||||
|
||||
const PasskeysFlow = () => {
|
||||
const [errored, setErrored] = useState(false);
|
||||
|
@ -32,18 +32,18 @@ const PasskeysFlow = () => {
|
|||
const searchParams = new URLSearchParams(window.location.search);
|
||||
|
||||
// get redirect from the query params
|
||||
const redirect = searchParams.get('redirect') as string;
|
||||
const redirect = searchParams.get("redirect") as string;
|
||||
|
||||
const redirectURL = new URL(redirect);
|
||||
if (process.env.NEXT_PUBLIC_DISABLE_REDIRECT_CHECK !== 'true') {
|
||||
if (process.env.NEXT_PUBLIC_DISABLE_REDIRECT_CHECK !== "true") {
|
||||
if (
|
||||
redirect !== '' &&
|
||||
redirect !== "" &&
|
||||
!(
|
||||
redirectURL.host.endsWith('.ente.io') ||
|
||||
redirectURL.host.endsWith('bada-frame.pages.dev')
|
||||
redirectURL.host.endsWith(".ente.io") ||
|
||||
redirectURL.host.endsWith("bada-frame.pages.dev")
|
||||
) &&
|
||||
redirectURL.protocol !== 'ente:' &&
|
||||
redirectURL.protocol !== 'enteauth:'
|
||||
redirectURL.protocol !== "ente:" &&
|
||||
redirectURL.protocol !== "enteauth:"
|
||||
) {
|
||||
setInvalidInfo(true);
|
||||
setLoading(false);
|
||||
|
@ -52,19 +52,19 @@ const PasskeysFlow = () => {
|
|||
}
|
||||
|
||||
let pkg = CLIENT_PACKAGE_NAMES.get(APPS.PHOTOS);
|
||||
if (redirectURL.protocol === 'enteauth:') {
|
||||
if (redirectURL.protocol === "enteauth:") {
|
||||
pkg = CLIENT_PACKAGE_NAMES.get(APPS.AUTH);
|
||||
} else if (redirectURL.hostname.startsWith('accounts')) {
|
||||
} else if (redirectURL.hostname.startsWith("accounts")) {
|
||||
pkg = CLIENT_PACKAGE_NAMES.get(APPS.ACCOUNTS);
|
||||
}
|
||||
|
||||
setData(LS_KEYS.CLIENT_PACKAGE, { name: pkg });
|
||||
HTTPService.setHeaders({
|
||||
'X-Client-Package': pkg,
|
||||
"X-Client-Package": pkg,
|
||||
});
|
||||
|
||||
// get passkeySessionID from the query params
|
||||
const passkeySessionID = searchParams.get('passkeySessionID') as string;
|
||||
const passkeySessionID = searchParams.get("passkeySessionID") as string;
|
||||
|
||||
setLoading(true);
|
||||
|
||||
|
@ -100,7 +100,7 @@ const PasskeysFlow = () => {
|
|||
|
||||
if (!credential) {
|
||||
if (!isWebAuthnSupported()) {
|
||||
alert('WebAuthn is not supported in this browser');
|
||||
alert("WebAuthn is not supported in this browser");
|
||||
}
|
||||
setErrored(true);
|
||||
return;
|
||||
|
@ -114,7 +114,7 @@ const PasskeysFlow = () => {
|
|||
finishData = await finishAuthentication(
|
||||
credential,
|
||||
passkeySessionID,
|
||||
beginData.ceremonySessionID
|
||||
beginData.ceremonySessionID,
|
||||
);
|
||||
} catch (e) {
|
||||
logError(e, "Couldn't finish passkey authentication");
|
||||
|
@ -142,28 +142,28 @@ const PasskeysFlow = () => {
|
|||
|
||||
const getCredential = async (
|
||||
publicKey: any,
|
||||
timeoutMillis: number = 60000 // Default timeout of 60 seconds
|
||||
timeoutMillis: number = 60000, // Default timeout of 60 seconds
|
||||
): Promise<Credential | null> => {
|
||||
publicKey.challenge = _sodium.from_base64(
|
||||
publicKey.challenge,
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
);
|
||||
publicKey.allowCredentials?.forEach(function (listItem: any) {
|
||||
listItem.id = _sodium.from_base64(
|
||||
listItem.id,
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
);
|
||||
// note: we are orverwriting the transports array with all possible values.
|
||||
// This is because the browser will only prompt the user for the transport that is available.
|
||||
// Warning: In case of invalid transport value, the webauthn will fail on Safari & iOS browsers
|
||||
listItem.transports = ['usb', 'nfc', 'ble', 'internal'];
|
||||
listItem.transports = ["usb", "nfc", "ble", "internal"];
|
||||
});
|
||||
publicKey.timeout = timeoutMillis;
|
||||
const publicKeyCredentialCreationOptions: CredentialRequestOptions = {
|
||||
publicKey: publicKey,
|
||||
};
|
||||
const credential = await navigator.credentials.get(
|
||||
publicKeyCredentialCreationOptions
|
||||
publicKeyCredentialCreationOptions,
|
||||
);
|
||||
return credential;
|
||||
};
|
||||
|
@ -171,12 +171,12 @@ const PasskeysFlow = () => {
|
|||
const finishAuthentication = async (
|
||||
credential: Credential,
|
||||
sessionId: string,
|
||||
ceremonySessionId: string
|
||||
ceremonySessionId: string,
|
||||
) => {
|
||||
const data = await finishPasskeyAuthentication(
|
||||
credential,
|
||||
sessionId,
|
||||
ceremonySessionId
|
||||
ceremonySessionId,
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
@ -199,18 +199,20 @@ const PasskeysFlow = () => {
|
|||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
height="100%">
|
||||
height="100%"
|
||||
>
|
||||
<Box maxWidth="30rem">
|
||||
<FormPaper
|
||||
style={{
|
||||
padding: '1rem',
|
||||
}}>
|
||||
padding: "1rem",
|
||||
}}
|
||||
>
|
||||
<InfoIcon />
|
||||
<Typography fontWeight="bold" variant="h1">
|
||||
{t('PASSKEY_LOGIN_FAILED')}
|
||||
{t("PASSKEY_LOGIN_FAILED")}
|
||||
</Typography>
|
||||
<Typography marginTop="1rem">
|
||||
{t('PASSKEY_LOGIN_URL_INVALID')}
|
||||
{t("PASSKEY_LOGIN_URL_INVALID")}
|
||||
</Typography>
|
||||
</FormPaper>
|
||||
</Box>
|
||||
|
@ -224,18 +226,20 @@ const PasskeysFlow = () => {
|
|||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
height="100%">
|
||||
height="100%"
|
||||
>
|
||||
<Box maxWidth="30rem">
|
||||
<FormPaper
|
||||
style={{
|
||||
padding: '1rem',
|
||||
}}>
|
||||
padding: "1rem",
|
||||
}}
|
||||
>
|
||||
<InfoIcon />
|
||||
<Typography fontWeight="bold" variant="h1">
|
||||
{t('PASSKEY_LOGIN_FAILED')}
|
||||
{t("PASSKEY_LOGIN_FAILED")}
|
||||
</Typography>
|
||||
<Typography marginTop="1rem">
|
||||
{t('PASSKEY_LOGIN_ERRORED')}
|
||||
{t("PASSKEY_LOGIN_ERRORED")}
|
||||
</Typography>
|
||||
<EnteButton
|
||||
onClick={() => {
|
||||
|
@ -244,12 +248,13 @@ const PasskeysFlow = () => {
|
|||
}}
|
||||
fullWidth
|
||||
style={{
|
||||
marginTop: '1rem',
|
||||
marginTop: "1rem",
|
||||
}}
|
||||
color="primary"
|
||||
type="button"
|
||||
variant="contained">
|
||||
{t('TRY_AGAIN')}
|
||||
variant="contained"
|
||||
>
|
||||
{t("TRY_AGAIN")}
|
||||
</EnteButton>
|
||||
</FormPaper>
|
||||
</Box>
|
||||
|
@ -263,18 +268,20 @@ const PasskeysFlow = () => {
|
|||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
height="100%">
|
||||
height="100%"
|
||||
>
|
||||
<Box maxWidth="30rem">
|
||||
<FormPaper
|
||||
style={{
|
||||
padding: '1rem',
|
||||
}}>
|
||||
padding: "1rem",
|
||||
}}
|
||||
>
|
||||
<InfoIcon />
|
||||
<Typography fontWeight="bold" variant="h1">
|
||||
{t('LOGIN_WITH_PASSKEY')}
|
||||
{t("LOGIN_WITH_PASSKEY")}
|
||||
</Typography>
|
||||
<Typography marginTop="1rem">
|
||||
{t('PASSKEY_FOLLOW_THE_STEPS_FROM_YOUR_BROWSER')}
|
||||
{t("PASSKEY_FOLLOW_THE_STEPS_FROM_YOUR_BROWSER")}
|
||||
</Typography>
|
||||
<CenteredFlex marginTop="1rem">
|
||||
<Image
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { CenteredFlex } from '@ente/shared/components/Container';
|
||||
import FormPaper from '@ente/shared/components/Form/FormPaper';
|
||||
import SingleInputForm from '@ente/shared/components/SingleInputForm';
|
||||
import { ACCOUNTS_PAGES } from '@ente/shared/constants/pages';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
import { getToken } from '@ente/shared/storage/localStorage/helpers';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { t } from 'i18next';
|
||||
import _sodium from 'libsodium-wrappers';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { CenteredFlex } from "@ente/shared/components/Container";
|
||||
import FormPaper from "@ente/shared/components/Form/FormPaper";
|
||||
import SingleInputForm from "@ente/shared/components/SingleInputForm";
|
||||
import { ACCOUNTS_PAGES } from "@ente/shared/constants/pages";
|
||||
import { logError } from "@ente/shared/sentry";
|
||||
import { getToken } from "@ente/shared/storage/localStorage/helpers";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { t } from "i18next";
|
||||
import _sodium from "libsodium-wrappers";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import {
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
|
@ -16,15 +16,15 @@ import {
|
|||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { Passkey } from 'types/passkey';
|
||||
} from "react";
|
||||
import { Passkey } from "types/passkey";
|
||||
import {
|
||||
finishPasskeyRegistration,
|
||||
getPasskeyRegistrationOptions,
|
||||
getPasskeys,
|
||||
} from '../../services/passkeysService';
|
||||
import ManagePasskeyDrawer from './ManagePasskeyDrawer';
|
||||
import PasskeysList from './PasskeysList';
|
||||
} from "../../services/passkeysService";
|
||||
import ManagePasskeyDrawer from "./ManagePasskeyDrawer";
|
||||
import PasskeysList from "./PasskeysList";
|
||||
|
||||
export const PasskeysContext = createContext(
|
||||
{} as {
|
||||
|
@ -32,14 +32,14 @@ export const PasskeysContext = createContext(
|
|||
setSelectedPasskey: Dispatch<SetStateAction<Passkey | null>>;
|
||||
setShowPasskeyDrawer: Dispatch<SetStateAction<boolean>>;
|
||||
refreshPasskeys: () => void;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const Passkeys = () => {
|
||||
const { showNavBar } = useContext(AppContext);
|
||||
|
||||
const [selectedPasskey, setSelectedPasskey] = useState<Passkey | null>(
|
||||
null
|
||||
null,
|
||||
);
|
||||
|
||||
const [showPasskeyDrawer, setShowPasskeyDrawer] = useState(false);
|
||||
|
@ -69,7 +69,7 @@ const Passkeys = () => {
|
|||
const handleSubmit = async (
|
||||
inputValue: string,
|
||||
setFieldError: (errorMessage: string) => void,
|
||||
resetForm: (nextState?: unknown) => void
|
||||
resetForm: (nextState?: unknown) => void,
|
||||
) => {
|
||||
let response: {
|
||||
options: {
|
||||
|
@ -81,7 +81,7 @@ const Passkeys = () => {
|
|||
try {
|
||||
response = await getPasskeyRegistrationOptions();
|
||||
} catch {
|
||||
setFieldError('Failed to begin registration');
|
||||
setFieldError("Failed to begin registration");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -90,12 +90,12 @@ const Passkeys = () => {
|
|||
options.publicKey.challenge = _sodium.from_base64(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
options.publicKey.challenge
|
||||
options.publicKey.challenge,
|
||||
);
|
||||
options.publicKey.user.id = _sodium.from_base64(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
options.publicKey.user.id
|
||||
options.publicKey.user.id,
|
||||
);
|
||||
|
||||
// create new credential
|
||||
|
@ -104,8 +104,8 @@ const Passkeys = () => {
|
|||
try {
|
||||
newCredential = await navigator.credentials.create(options);
|
||||
} catch (e) {
|
||||
logError(e, 'Error creating credential');
|
||||
setFieldError('Failed to create credential');
|
||||
logError(e, "Error creating credential");
|
||||
setFieldError("Failed to create credential");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -113,10 +113,10 @@ const Passkeys = () => {
|
|||
await finishPasskeyRegistration(
|
||||
inputValue,
|
||||
newCredential,
|
||||
response.sessionID
|
||||
response.sessionID,
|
||||
);
|
||||
} catch {
|
||||
setFieldError('Failed to finish registration');
|
||||
setFieldError("Failed to finish registration");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -132,21 +132,23 @@ const Passkeys = () => {
|
|||
setSelectedPasskey,
|
||||
setShowPasskeyDrawer,
|
||||
refreshPasskeys: init,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<CenteredFlex>
|
||||
<Box maxWidth="20rem">
|
||||
<Box marginBottom="1rem">
|
||||
<Typography>{t('PASSKEYS_DESCRIPTION')}</Typography>
|
||||
<Typography>{t("PASSKEYS_DESCRIPTION")}</Typography>
|
||||
</Box>
|
||||
<FormPaper
|
||||
style={{
|
||||
padding: '1rem',
|
||||
}}>
|
||||
padding: "1rem",
|
||||
}}
|
||||
>
|
||||
<SingleInputForm
|
||||
fieldType="text"
|
||||
placeholder={t('ENTER_PASSKEY_NAME')}
|
||||
buttonText={t('ADD_PASSKEY')}
|
||||
initialValue={''}
|
||||
placeholder={t("ENTER_PASSKEY_NAME")}
|
||||
buttonText={t("ADD_PASSKEY")}
|
||||
initialValue={""}
|
||||
callback={handleSubmit}
|
||||
submitButtonProps={{
|
||||
sx: {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import RecoverPage from '@ente/accounts/pages/recover';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import RecoverPage from "@ente/accounts/pages/recover";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function Recover() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import SignupPage from '@ente/accounts/pages/signup';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import SignupPage from "@ente/accounts/pages/signup";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function Sigup() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import TwoFactorRecoverPage from '@ente/accounts/pages/two-factor/recover';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import TwoFactorRecoverPage from "@ente/accounts/pages/two-factor/recover";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function TwoFactorRecover() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import TwoFactorSetupPage from '@ente/accounts/pages/two-factor/setup';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import TwoFactorSetupPage from "@ente/accounts/pages/two-factor/setup";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function TwoFactorSetup() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import TwoFactorVerifyPage from '@ente/accounts/pages/two-factor/verify';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import TwoFactorVerifyPage from "@ente/accounts/pages/two-factor/verify";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function TwoFactorVerify() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import VerifyPage from '@ente/accounts/pages/verify';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import VerifyPage from "@ente/accounts/pages/verify";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function Verify() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import HTTPService from '@ente/shared/network/HTTPService';
|
||||
import { getEndpoint } from '@ente/shared/network/api';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
import { getToken } from '@ente/shared/storage/localStorage/helpers';
|
||||
import _sodium from 'libsodium-wrappers';
|
||||
import HTTPService from "@ente/shared/network/HTTPService";
|
||||
import { getEndpoint } from "@ente/shared/network/api";
|
||||
import { logError } from "@ente/shared/sentry";
|
||||
import { getToken } from "@ente/shared/storage/localStorage/helpers";
|
||||
import _sodium from "libsodium-wrappers";
|
||||
const ENDPOINT = getEndpoint();
|
||||
|
||||
export const getPasskeys = async () => {
|
||||
|
@ -12,11 +12,11 @@ export const getPasskeys = async () => {
|
|||
const response = await HTTPService.get(
|
||||
`${ENDPOINT}/passkeys`,
|
||||
{},
|
||||
{ 'X-Auth-Token': token }
|
||||
{ "X-Auth-Token": token },
|
||||
);
|
||||
return await response.data;
|
||||
} catch (e) {
|
||||
logError(e, 'get passkeys failed');
|
||||
logError(e, "get passkeys failed");
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
@ -29,11 +29,11 @@ export const renamePasskey = async (id: string, name: string) => {
|
|||
`${ENDPOINT}/passkeys/${id}`,
|
||||
{},
|
||||
{ friendlyName: name },
|
||||
{ 'X-Auth-Token': token }
|
||||
{ "X-Auth-Token": token },
|
||||
);
|
||||
return await response.data;
|
||||
} catch (e) {
|
||||
logError(e, 'rename passkey failed');
|
||||
logError(e, "rename passkey failed");
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
@ -46,11 +46,11 @@ export const deletePasskey = async (id: string) => {
|
|||
`${ENDPOINT}/passkeys/${id}`,
|
||||
{},
|
||||
{},
|
||||
{ 'X-Auth-Token': token }
|
||||
{ "X-Auth-Token": token },
|
||||
);
|
||||
return await response.data;
|
||||
} catch (e) {
|
||||
logError(e, 'delete passkey failed');
|
||||
logError(e, "delete passkey failed");
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
@ -63,12 +63,12 @@ export const getPasskeyRegistrationOptions = async () => {
|
|||
`${ENDPOINT}/passkeys/registration/begin`,
|
||||
{},
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
"X-Auth-Token": token,
|
||||
},
|
||||
);
|
||||
return await response.data;
|
||||
} catch (e) {
|
||||
logError(e, 'get passkey registration options failed');
|
||||
logError(e, "get passkey registration options failed");
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
@ -76,20 +76,20 @@ export const getPasskeyRegistrationOptions = async () => {
|
|||
export const finishPasskeyRegistration = async (
|
||||
friendlyName: string,
|
||||
credential: Credential,
|
||||
sessionId: string
|
||||
sessionId: string,
|
||||
) => {
|
||||
try {
|
||||
const attestationObjectB64 = _sodium.to_base64(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
new Uint8Array(credential.response.attestationObject),
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
);
|
||||
const clientDataJSONB64 = _sodium.to_base64(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
new Uint8Array(credential.response.clientDataJSON),
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
);
|
||||
|
||||
const token = getToken();
|
||||
|
@ -111,12 +111,12 @@ export const finishPasskeyRegistration = async (
|
|||
sessionID: sessionId,
|
||||
},
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
"X-Auth-Token": token,
|
||||
},
|
||||
);
|
||||
return await response.data;
|
||||
} catch (e) {
|
||||
logError(e, 'finish passkey registration failed');
|
||||
logError(e, "finish passkey registration failed");
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
@ -130,19 +130,19 @@ interface Options {
|
|||
}
|
||||
|
||||
export const beginPasskeyAuthentication = async (
|
||||
sessionId: string
|
||||
sessionId: string,
|
||||
): Promise<BeginPasskeyAuthenticationResponse> => {
|
||||
try {
|
||||
const data = await HTTPService.post(
|
||||
`${ENDPOINT}/users/two-factor/passkeys/begin`,
|
||||
{
|
||||
sessionID: sessionId,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return data.data;
|
||||
} catch (e) {
|
||||
logError(e, 'begin passkey authentication failed');
|
||||
logError(e, "begin passkey authentication failed");
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
@ -150,7 +150,7 @@ export const beginPasskeyAuthentication = async (
|
|||
export const finishPasskeyAuthentication = async (
|
||||
credential: Credential,
|
||||
sessionId: string,
|
||||
ceremonySessionId: string
|
||||
ceremonySessionId: string,
|
||||
) => {
|
||||
try {
|
||||
const data = await HTTPService.post(
|
||||
|
@ -164,37 +164,37 @@ export const finishPasskeyAuthentication = async (
|
|||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
new Uint8Array(credential.response.authenticatorData),
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
),
|
||||
clientDataJSON: _sodium.to_base64(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
new Uint8Array(credential.response.clientDataJSON),
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
),
|
||||
signature: _sodium.to_base64(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
new Uint8Array(credential.response.signature),
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
),
|
||||
userHandle: _sodium.to_base64(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
new Uint8Array(credential.response.userHandle),
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
sessionID: sessionId,
|
||||
ceremonySessionID: ceremonySessionId,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return data.data;
|
||||
} catch (e) {
|
||||
logError(e, 'finish passkey authentication failed');
|
||||
logError(e, "finish passkey authentication failed");
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,39 +1,39 @@
|
|||
/* inter-regular - latin */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src:
|
||||
local(''),
|
||||
url('/fonts/inter-v11-latin-500.woff2') format('woff2'),
|
||||
local(""),
|
||||
url("/fonts/inter-v11-latin-500.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url('/fonts/inter-v11-latin-500.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url("/fonts/inter-v11-latin-500.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
font-display: swap; /*https://web.dev/font-display/?utm_source=lighthouse&utm_medium=devtools#how-to-avoid-showing-invisible-text*/
|
||||
}
|
||||
|
||||
/* inter-600 - latin */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src:
|
||||
local(''),
|
||||
url('/fonts/inter-v11-latin-600.woff2') format('woff2'),
|
||||
local(""),
|
||||
url("/fonts/inter-v11-latin-600.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url('/fonts/inter-v11-latin-600.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url("/fonts/inter-v11-latin-600.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* inter-800 - latin */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
src:
|
||||
local(''),
|
||||
url('/fonts/inter-v11-latin-800.woff2') format('woff2'),
|
||||
local(""),
|
||||
url("/fonts/inter-v11-latin-800.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url('/fonts/inter-v11-latin-800.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url("/fonts/inter-v11-latin-800.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@ module.exports = {
|
|||
// This is required here to ensure desktop picks the right eslint config, where this app is
|
||||
// packaged as a submodule.
|
||||
root: true,
|
||||
extends: ['@ente/eslint-config'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
extends: ["@ente/eslint-config"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json',
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js', 'out'],
|
||||
ignorePatterns: [".eslintrc.js", "out"],
|
||||
};
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
const nextConfigBase = require('@/next/next.config.base.js');
|
||||
const nextConfigBase = require("@/next/next.config.base.js");
|
||||
|
||||
module.exports = nextConfigBase;
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import { initSentry } from '@ente/shared/sentry/config/sentry.config.base';
|
||||
import { initSentry } from "@ente/shared/sentry/config/sentry.config.base";
|
||||
|
||||
initSentry('https://5d344112b570b1a368b6f5c1d0bb798b@sentry.ente.io/8');
|
||||
initSentry("https://5d344112b570b1a368b6f5c1d0bb798b@sentry.ente.io/8");
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import { Button } from '@mui/material';
|
||||
import { t } from 'i18next';
|
||||
import { Button } from "@mui/material";
|
||||
import { t } from "i18next";
|
||||
|
||||
export const AuthFooter = () => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
<p>{t('AUTH_DOWNLOAD_MOBILE_APP')}</p>
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<p>{t("AUTH_DOWNLOAD_MOBILE_APP")}</p>
|
||||
<a href="https://github.com/ente-io/auth#-download" download>
|
||||
<Button color="accent">{t('DOWNLOAD')}</Button>
|
||||
<Button color="accent">{t("DOWNLOAD")}</Button>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,31 +1,33 @@
|
|||
import { HorizontalFlex } from '@ente/shared/components/Container';
|
||||
import NavbarBase from '@ente/shared/components/Navbar/base';
|
||||
import React from 'react';
|
||||
import { t } from 'i18next';
|
||||
import { logoutUser } from '@ente/accounts/services/user';
|
||||
import { EnteLogo } from '@ente/shared/components/EnteLogo';
|
||||
import OverflowMenu from '@ente/shared/components/OverflowMenu/menu';
|
||||
import { OverflowMenuOption } from '@ente/shared/components/OverflowMenu/option';
|
||||
import MoreHoriz from '@mui/icons-material/MoreHoriz';
|
||||
import LogoutOutlined from '@mui/icons-material/LogoutOutlined';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { logoutUser } from "@ente/accounts/services/user";
|
||||
import { HorizontalFlex } from "@ente/shared/components/Container";
|
||||
import { EnteLogo } from "@ente/shared/components/EnteLogo";
|
||||
import NavbarBase from "@ente/shared/components/Navbar/base";
|
||||
import OverflowMenu from "@ente/shared/components/OverflowMenu/menu";
|
||||
import { OverflowMenuOption } from "@ente/shared/components/OverflowMenu/option";
|
||||
import LogoutOutlined from "@mui/icons-material/LogoutOutlined";
|
||||
import MoreHoriz from "@mui/icons-material/MoreHoriz";
|
||||
import { t } from "i18next";
|
||||
import { AppContext } from "pages/_app";
|
||||
import React from "react";
|
||||
|
||||
export default function AuthNavbar() {
|
||||
const { isMobile } = React.useContext(AppContext);
|
||||
return (
|
||||
<NavbarBase isMobile={isMobile}>
|
||||
<HorizontalFlex flex={1} justifyContent={'center'}>
|
||||
<HorizontalFlex flex={1} justifyContent={"center"}>
|
||||
<EnteLogo />
|
||||
</HorizontalFlex>
|
||||
<HorizontalFlex position={'absolute'} right="24px">
|
||||
<HorizontalFlex position={"absolute"} right="24px">
|
||||
<OverflowMenu
|
||||
ariaControls={'auth-options'}
|
||||
triggerButtonIcon={<MoreHoriz />}>
|
||||
ariaControls={"auth-options"}
|
||||
triggerButtonIcon={<MoreHoriz />}
|
||||
>
|
||||
<OverflowMenuOption
|
||||
color="critical"
|
||||
startIcon={<LogoutOutlined />}
|
||||
onClick={logoutUser}>
|
||||
{t('LOGOUT')}
|
||||
onClick={logoutUser}
|
||||
>
|
||||
{t("LOGOUT")}
|
||||
</OverflowMenuOption>
|
||||
</OverflowMenu>
|
||||
</HorizontalFlex>
|
||||
|
|
|
@ -1,98 +1,107 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { TOTP, HOTP } from 'otpauth';
|
||||
import { Code } from 'types/code';
|
||||
import TimerProgress from './TimerProgress';
|
||||
import { t } from 'i18next';
|
||||
import { ButtonBase, Snackbar } from '@mui/material';
|
||||
import { ButtonBase, Snackbar } from "@mui/material";
|
||||
import { t } from "i18next";
|
||||
import { HOTP, TOTP } from "otpauth";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Code } from "types/code";
|
||||
import TimerProgress from "./TimerProgress";
|
||||
|
||||
const TOTPDisplay = ({ issuer, account, code, nextCode, period }) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: 'rgba(40, 40, 40, 0.6)',
|
||||
borderRadius: '4px',
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
backgroundColor: "rgba(40, 40, 40, 0.6)",
|
||||
borderRadius: "4px",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<TimerProgress period={period ?? Code.defaultPeriod} />
|
||||
<div
|
||||
style={{
|
||||
padding: '12px 20px 0px 20px',
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
minWidth: '320px',
|
||||
minHeight: '120px',
|
||||
justifyContent: 'space-between',
|
||||
}}>
|
||||
padding: "12px 20px 0px 20px",
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
minWidth: "320px",
|
||||
minHeight: "120px",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
minWidth: '200px',
|
||||
}}>
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
minWidth: "200px",
|
||||
}}
|
||||
>
|
||||
<p
|
||||
style={{
|
||||
fontWeight: 'bold',
|
||||
margin: '0px',
|
||||
fontSize: '14px',
|
||||
textAlign: 'left',
|
||||
}}>
|
||||
fontWeight: "bold",
|
||||
margin: "0px",
|
||||
fontSize: "14px",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
{issuer}
|
||||
</p>
|
||||
<p
|
||||
style={{
|
||||
marginTop: '0px',
|
||||
marginBottom: '8px',
|
||||
textAlign: 'left',
|
||||
fontSize: '12px',
|
||||
maxWidth: '200px',
|
||||
minHeight: '16px',
|
||||
color: 'grey',
|
||||
}}>
|
||||
marginTop: "0px",
|
||||
marginBottom: "8px",
|
||||
textAlign: "left",
|
||||
fontSize: "12px",
|
||||
maxWidth: "200px",
|
||||
minHeight: "16px",
|
||||
color: "grey",
|
||||
}}
|
||||
>
|
||||
{account}
|
||||
</p>
|
||||
<p
|
||||
style={{
|
||||
margin: '0px',
|
||||
marginBottom: '1rem',
|
||||
fontSize: '24px',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'left',
|
||||
}}>
|
||||
margin: "0px",
|
||||
marginBottom: "1rem",
|
||||
fontSize: "24px",
|
||||
fontWeight: "bold",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
{code}
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ flex: 1 }} />
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-end',
|
||||
minWidth: '120px',
|
||||
textAlign: 'right',
|
||||
marginTop: 'auto',
|
||||
marginBottom: '1rem',
|
||||
}}>
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-end",
|
||||
minWidth: "120px",
|
||||
textAlign: "right",
|
||||
marginTop: "auto",
|
||||
marginBottom: "1rem",
|
||||
}}
|
||||
>
|
||||
<p
|
||||
style={{
|
||||
fontWeight: 'bold',
|
||||
marginBottom: '0px',
|
||||
fontSize: '10px',
|
||||
marginTop: 'auto',
|
||||
textAlign: 'right',
|
||||
color: 'grey',
|
||||
}}>
|
||||
{t('AUTH_NEXT')}
|
||||
fontWeight: "bold",
|
||||
marginBottom: "0px",
|
||||
fontSize: "10px",
|
||||
marginTop: "auto",
|
||||
textAlign: "right",
|
||||
color: "grey",
|
||||
}}
|
||||
>
|
||||
{t("AUTH_NEXT")}
|
||||
</p>
|
||||
<p
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 'bold',
|
||||
marginBottom: '0px',
|
||||
marginTop: 'auto',
|
||||
textAlign: 'right',
|
||||
color: 'grey',
|
||||
}}>
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
marginBottom: "0px",
|
||||
marginTop: "auto",
|
||||
textAlign: "right",
|
||||
color: "grey",
|
||||
}}
|
||||
>
|
||||
{nextCode}
|
||||
</p>
|
||||
</div>
|
||||
|
@ -111,7 +120,7 @@ function BadCodeInfo({ codeInfo, codeErr }) {
|
|||
<div>
|
||||
{showRawData ? (
|
||||
<div onClick={() => setShowRawData(false)}>
|
||||
{codeInfo.rawData ?? 'no raw data'}
|
||||
{codeInfo.rawData ?? "no raw data"}
|
||||
</div>
|
||||
) : (
|
||||
<div onClick={() => setShowRawData(true)}>Show rawData</div>
|
||||
|
@ -127,15 +136,15 @@ interface OTPDisplayProps {
|
|||
|
||||
const OTPDisplay = (props: OTPDisplayProps) => {
|
||||
const { codeInfo } = props;
|
||||
const [code, setCode] = useState('');
|
||||
const [nextCode, setNextCode] = useState('');
|
||||
const [codeErr, setCodeErr] = useState('');
|
||||
const [code, setCode] = useState("");
|
||||
const [nextCode, setNextCode] = useState("");
|
||||
const [codeErr, setCodeErr] = useState("");
|
||||
const [hasCopied, setHasCopied] = useState(false);
|
||||
|
||||
const generateCodes = () => {
|
||||
try {
|
||||
const currentTime = new Date().getTime();
|
||||
if (codeInfo.type.toLowerCase() === 'totp') {
|
||||
if (codeInfo.type.toLowerCase() === "totp") {
|
||||
const totp = new TOTP({
|
||||
secret: codeInfo.secret,
|
||||
algorithm: codeInfo.algorithm ?? Code.defaultAlgo,
|
||||
|
@ -146,9 +155,9 @@ const OTPDisplay = (props: OTPDisplayProps) => {
|
|||
setNextCode(
|
||||
totp.generate({
|
||||
timestamp: currentTime + codeInfo.period * 1000,
|
||||
})
|
||||
}),
|
||||
);
|
||||
} else if (codeInfo.type.toLowerCase() === 'hotp') {
|
||||
} else if (codeInfo.type.toLowerCase() === "hotp") {
|
||||
const hotp = new HOTP({
|
||||
secret: codeInfo.secret,
|
||||
counter: 0,
|
||||
|
@ -184,8 +193,8 @@ const OTPDisplay = (props: OTPDisplayProps) => {
|
|||
// we need to call generateCodes() once before the interval loop
|
||||
// to set the initial code and nextCode
|
||||
generateCodes();
|
||||
codeType.toLowerCase() === 'totp' ||
|
||||
codeType.toLowerCase() === 'hotp'
|
||||
codeType.toLowerCase() === "totp" ||
|
||||
codeType.toLowerCase() === "hotp"
|
||||
? setInterval(() => {
|
||||
generateCodes();
|
||||
}, codePeriodInMs)
|
||||
|
@ -198,13 +207,14 @@ const OTPDisplay = (props: OTPDisplayProps) => {
|
|||
}, [codeInfo]);
|
||||
|
||||
return (
|
||||
<div style={{ padding: '8px' }}>
|
||||
{codeErr === '' ? (
|
||||
<div style={{ padding: "8px" }}>
|
||||
{codeErr === "" ? (
|
||||
<ButtonBase
|
||||
component="div"
|
||||
onClick={() => {
|
||||
copyCode();
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<TOTPDisplay
|
||||
period={codeInfo.period}
|
||||
issuer={codeInfo.issuer}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const TimerProgress = ({ period }) => {
|
||||
const [progress, setProgress] = useState(0);
|
||||
|
@ -24,14 +24,14 @@ const TimerProgress = ({ period }) => {
|
|||
return () => clearInterval(ticker);
|
||||
}, []);
|
||||
|
||||
const color = progress > 0.4 ? 'green' : 'orange';
|
||||
const color = progress > 0.4 ? "green" : "orange";
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
borderTopLeftRadius: '3px',
|
||||
borderTopLeftRadius: "3px",
|
||||
width: `${progress * 100}%`,
|
||||
height: '3px',
|
||||
height: "3px",
|
||||
backgroundColor: color,
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import NotFoundPage from '@ente/shared/next/pages/404';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import NotFoundPage from "@ente/shared/next/pages/404";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function NotFound() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,42 +1,42 @@
|
|||
import React, { createContext, useEffect, useRef, useState } from 'react';
|
||||
import AppNavbar from '@ente/shared/components/Navbar/app';
|
||||
import { t } from 'i18next';
|
||||
import AppNavbar from "@ente/shared/components/Navbar/app";
|
||||
import { t } from "i18next";
|
||||
import { createContext, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { useRouter } from 'next/router';
|
||||
import { Overlay } from '@ente/shared/components/Container';
|
||||
import EnteSpinner from '@ente/shared/components/EnteSpinner';
|
||||
import { LS_KEYS } from '@ente/shared/storage/localStorage';
|
||||
import HTTPService from '@ente/shared/network/HTTPService';
|
||||
import Head from 'next/head';
|
||||
import LoadingBar from 'react-top-loading-bar';
|
||||
import DialogBoxV2 from '@ente/shared/components/DialogBoxV2';
|
||||
import { ThemeProvider } from '@mui/material/styles';
|
||||
import { MessageContainer } from '@ente/shared/components/MessageContainer';
|
||||
import { CssBaseline, useMediaQuery } from '@mui/material';
|
||||
import { Overlay } from "@ente/shared/components/Container";
|
||||
import DialogBoxV2 from "@ente/shared/components/DialogBoxV2";
|
||||
import {
|
||||
DialogBoxAttributesV2,
|
||||
SetDialogBoxAttributesV2,
|
||||
} from '@ente/shared/components/DialogBoxV2/types';
|
||||
} from "@ente/shared/components/DialogBoxV2/types";
|
||||
import EnteSpinner from "@ente/shared/components/EnteSpinner";
|
||||
import { MessageContainer } from "@ente/shared/components/MessageContainer";
|
||||
import {
|
||||
clearLogsIfLocalStorageLimitExceeded,
|
||||
logStartupMessage,
|
||||
} from '@ente/shared/logging/web';
|
||||
} from "@ente/shared/logging/web";
|
||||
import HTTPService from "@ente/shared/network/HTTPService";
|
||||
import { LS_KEYS } from "@ente/shared/storage/localStorage";
|
||||
import { CssBaseline, useMediaQuery } from "@mui/material";
|
||||
import { ThemeProvider } from "@mui/material/styles";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import LoadingBar from "react-top-loading-bar";
|
||||
|
||||
import { CacheProvider } from '@emotion/react';
|
||||
import { setupI18n } from "@/ui/i18n";
|
||||
import { CacheProvider } from "@emotion/react";
|
||||
import {
|
||||
APP_TITLES,
|
||||
APPS,
|
||||
CLIENT_PACKAGE_NAMES,
|
||||
} from '@ente/shared/apps/constants';
|
||||
import { EnteAppProps } from '@ente/shared/apps/types';
|
||||
import createEmotionCache from '@ente/shared/themes/createEmotionCache';
|
||||
import { THEME_COLOR } from '@ente/shared/themes/constants';
|
||||
import { SetTheme } from '@ente/shared/themes/types';
|
||||
import { setupI18n } from '@/ui/i18n';
|
||||
import { useLocalState } from '@ente/shared/hooks/useLocalState';
|
||||
import { PHOTOS_PAGES as PAGES } from '@ente/shared/constants/pages';
|
||||
import { getTheme } from '@ente/shared/themes';
|
||||
import '../../public/css/global.css';
|
||||
} from "@ente/shared/apps/constants";
|
||||
import { EnteAppProps } from "@ente/shared/apps/types";
|
||||
import { PHOTOS_PAGES as PAGES } from "@ente/shared/constants/pages";
|
||||
import { useLocalState } from "@ente/shared/hooks/useLocalState";
|
||||
import { getTheme } from "@ente/shared/themes";
|
||||
import { THEME_COLOR } from "@ente/shared/themes/constants";
|
||||
import createEmotionCache from "@ente/shared/themes/createEmotionCache";
|
||||
import { SetTheme } from "@ente/shared/themes/types";
|
||||
import "../../public/css/global.css";
|
||||
|
||||
type AppContextType = {
|
||||
showNavBar: (show: boolean) => void;
|
||||
|
@ -64,7 +64,7 @@ export default function App(props: EnteAppProps) {
|
|||
const [isI18nReady, setIsI18nReady] = useState<boolean>(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [offline, setOffline] = useState(
|
||||
typeof window !== 'undefined' && !window.navigator.onLine
|
||||
typeof window !== "undefined" && !window.navigator.onLine,
|
||||
);
|
||||
const [showNavbar, setShowNavBar] = useState(false);
|
||||
const isLoadingBarRunning = useRef(false);
|
||||
|
@ -72,10 +72,10 @@ export default function App(props: EnteAppProps) {
|
|||
const [dialogBoxAttributeV2, setDialogBoxAttributesV2] =
|
||||
useState<DialogBoxAttributesV2>();
|
||||
const [dialogBoxV2View, setDialogBoxV2View] = useState(false);
|
||||
const isMobile = useMediaQuery('(max-width:428px)');
|
||||
const isMobile = useMediaQuery("(max-width:428px)");
|
||||
const [themeColor, setThemeColor] = useLocalState(
|
||||
LS_KEYS.THEME,
|
||||
THEME_COLOR.DARK
|
||||
THEME_COLOR.DARK,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -83,7 +83,7 @@ export default function App(props: EnteAppProps) {
|
|||
setupI18n().finally(() => setIsI18nReady(true));
|
||||
// set client package name in headers
|
||||
HTTPService.setHeaders({
|
||||
'X-Client-Package': CLIENT_PACKAGE_NAMES.get(APPS.AUTH),
|
||||
"X-Client-Package": CLIENT_PACKAGE_NAMES.get(APPS.AUTH),
|
||||
});
|
||||
// setup logging
|
||||
clearLogsIfLocalStorageLimitExceeded();
|
||||
|
@ -96,31 +96,31 @@ export default function App(props: EnteAppProps) {
|
|||
useEffect(() => {
|
||||
if (isI18nReady) {
|
||||
console.log(
|
||||
`%c${t('CONSOLE_WARNING_STOP')}`,
|
||||
'color: red; font-size: 52px;'
|
||||
`%c${t("CONSOLE_WARNING_STOP")}`,
|
||||
"color: red; font-size: 52px;",
|
||||
);
|
||||
console.log(`%c${t('CONSOLE_WARNING_DESC')}`, 'font-size: 20px;');
|
||||
console.log(`%c${t("CONSOLE_WARNING_DESC")}`, "font-size: 20px;");
|
||||
}
|
||||
}, [isI18nReady]);
|
||||
|
||||
useEffect(() => {
|
||||
router.events.on('routeChangeStart', (url: string) => {
|
||||
const newPathname = url.split('?')[0] as PAGES;
|
||||
router.events.on("routeChangeStart", (url: string) => {
|
||||
const newPathname = url.split("?")[0] as PAGES;
|
||||
if (window.location.pathname !== newPathname) {
|
||||
setLoading(true);
|
||||
}
|
||||
});
|
||||
|
||||
router.events.on('routeChangeComplete', () => {
|
||||
router.events.on("routeChangeComplete", () => {
|
||||
setLoading(false);
|
||||
});
|
||||
|
||||
window.addEventListener('online', setUserOnline);
|
||||
window.addEventListener('offline', setUserOffline);
|
||||
window.addEventListener("online", setUserOnline);
|
||||
window.addEventListener("offline", setUserOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', setUserOnline);
|
||||
window.removeEventListener('offline', setUserOffline);
|
||||
window.removeEventListener("online", setUserOnline);
|
||||
window.removeEventListener("offline", setUserOffline);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
@ -145,9 +145,9 @@ export default function App(props: EnteAppProps) {
|
|||
|
||||
const somethingWentWrong = () =>
|
||||
setDialogBoxAttributesV2({
|
||||
title: t('ERROR'),
|
||||
close: { variant: 'critical' },
|
||||
content: t('UNKNOWN_ERROR'),
|
||||
title: t("ERROR"),
|
||||
close: { variant: "critical" },
|
||||
content: t("UNKNOWN_ERROR"),
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -155,7 +155,7 @@ export default function App(props: EnteAppProps) {
|
|||
<Head>
|
||||
<title>
|
||||
{isI18nReady
|
||||
? t('TITLE', { context: APPS.AUTH })
|
||||
? t("TITLE", { context: APPS.AUTH })
|
||||
: APP_TITLES.get(APPS.AUTH)}
|
||||
</title>
|
||||
<meta
|
||||
|
@ -168,7 +168,7 @@ export default function App(props: EnteAppProps) {
|
|||
<CssBaseline enableColorScheme />
|
||||
{showNavbar && <AppNavbar isMobile={isMobile} />}
|
||||
<MessageContainer>
|
||||
{offline && t('OFFLINE_MSG')}
|
||||
{offline && t("OFFLINE_MSG")}
|
||||
</MessageContainer>
|
||||
|
||||
<LoadingBar color="#51cd7c" ref={loadingBar} />
|
||||
|
@ -190,16 +190,18 @@ export default function App(props: EnteAppProps) {
|
|||
setThemeColor,
|
||||
somethingWentWrong,
|
||||
setDialogBoxAttributesV2,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{(loading || !isI18nReady) && (
|
||||
<Overlay
|
||||
sx={(theme) => ({
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
zIndex: 2000,
|
||||
backgroundColor: theme.colors.background.base,
|
||||
})}>
|
||||
})}
|
||||
>
|
||||
<EnteSpinner />
|
||||
</Overlay>
|
||||
)}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import DocumentPage, {
|
||||
EnteDocumentProps,
|
||||
} from '@ente/shared/next/pages/_document';
|
||||
} from "@ente/shared/next/pages/_document";
|
||||
|
||||
export default function Document(props: EnteDocumentProps) {
|
||||
return <DocumentPage {...props} />;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import ErrorPage from '@ente/shared/next/pages/_error';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import ErrorPage from "@ente/shared/next/pages/_error";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function Error() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import OTPDisplay from 'components/OTPDisplay';
|
||||
import { getAuthCodes } from 'services';
|
||||
import { CustomError } from '@ente/shared/error';
|
||||
import { AUTH_PAGES as PAGES } from '@ente/shared/constants/pages';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AuthFooter } from 'components/AuthFooter';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { TextField } from '@mui/material';
|
||||
import AuthNavbar from 'components/Navbar';
|
||||
import { t } from 'i18next';
|
||||
import EnteSpinner from '@ente/shared/components/EnteSpinner';
|
||||
import { VerticallyCentered } from '@ente/shared/components/Container';
|
||||
import InMemoryStore, { MS_KEYS } from '@ente/shared/storage/InMemoryStore';
|
||||
import { VerticallyCentered } from "@ente/shared/components/Container";
|
||||
import EnteSpinner from "@ente/shared/components/EnteSpinner";
|
||||
import { AUTH_PAGES as PAGES } from "@ente/shared/constants/pages";
|
||||
import { CustomError } from "@ente/shared/error";
|
||||
import InMemoryStore, { MS_KEYS } from "@ente/shared/storage/InMemoryStore";
|
||||
import { TextField } from "@mui/material";
|
||||
import { AuthFooter } from "components/AuthFooter";
|
||||
import AuthNavbar from "components/Navbar";
|
||||
import OTPDisplay from "components/OTPDisplay";
|
||||
import { t } from "i18next";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { getAuthCodes } from "services";
|
||||
|
||||
const AuthenticatorCodesPage = () => {
|
||||
const appContext = useContext(AppContext);
|
||||
const router = useRouter();
|
||||
const [codes, setCodes] = useState([]);
|
||||
const [hasFetched, setHasFetched] = useState(false);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCodes = async () => {
|
||||
|
@ -41,12 +41,12 @@ const AuthenticatorCodesPage = () => {
|
|||
|
||||
const filteredCodes = codes.filter(
|
||||
(secret) =>
|
||||
(secret.issuer ?? '')
|
||||
(secret.issuer ?? "")
|
||||
.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase()) ||
|
||||
(secret.account ?? '')
|
||||
(secret.account ?? "")
|
||||
.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase())
|
||||
.includes(searchTerm.toLowerCase()),
|
||||
);
|
||||
|
||||
if (!hasFetched) {
|
||||
|
@ -64,47 +64,50 @@ const AuthenticatorCodesPage = () => {
|
|||
<AuthNavbar />
|
||||
<div
|
||||
style={{
|
||||
maxWidth: '800px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
margin: '0 auto',
|
||||
}}>
|
||||
<div style={{ marginBottom: '1rem' }} />
|
||||
maxWidth: "800px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
margin: "0 auto",
|
||||
}}
|
||||
>
|
||||
<div style={{ marginBottom: "1rem" }} />
|
||||
{filteredCodes.length === 0 && searchTerm.length === 0 ? (
|
||||
<></>
|
||||
) : (
|
||||
<TextField
|
||||
id="search"
|
||||
name="search"
|
||||
label={t('SEARCH')}
|
||||
label={t("SEARCH")}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
variant="filled"
|
||||
style={{ width: '350px' }}
|
||||
style={{ width: "350px" }}
|
||||
value={searchTerm}
|
||||
autoFocus
|
||||
/>
|
||||
)}
|
||||
|
||||
<div style={{ marginBottom: '1rem' }} />
|
||||
<div style={{ marginBottom: "1rem" }} />
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
{filteredCodes.length === 0 ? (
|
||||
<div
|
||||
style={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
textAlign: 'center',
|
||||
marginTop: '32px',
|
||||
}}>
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
textAlign: "center",
|
||||
marginTop: "32px",
|
||||
}}
|
||||
>
|
||||
{searchTerm.length !== 0 ? (
|
||||
<p>{t('NO_RESULTS')}</p>
|
||||
<p>{t("NO_RESULTS")}</p>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
|
@ -115,9 +118,9 @@ const AuthenticatorCodesPage = () => {
|
|||
))
|
||||
)}
|
||||
</div>
|
||||
<div style={{ marginBottom: '2rem' }} />
|
||||
<div style={{ marginBottom: "2rem" }} />
|
||||
<AuthFooter />
|
||||
<div style={{ marginBottom: '4rem' }} />
|
||||
<div style={{ marginBottom: "4rem" }} />
|
||||
</div>
|
||||
<style jsx>{`
|
||||
@media (min-width: 800px) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import ChangeEmailPage from '@ente/accounts/pages/change-email';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import ChangeEmailPage from "@ente/accounts/pages/change-email";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function ChangeEmail() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import ChangePasswordPage from '@ente/accounts/pages/change-password';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import ChangePasswordPage from "@ente/accounts/pages/change-password";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function ChangePassword() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import CredentialPage from '@ente/accounts/pages/credentials';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import CredentialPage from "@ente/accounts/pages/credentials";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function Credential() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import GeneratePage from '@ente/accounts/pages/generate';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import GeneratePage from "@ente/accounts/pages/generate";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function Generate() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { PHOTOS_PAGES as PAGES } from '@ente/shared/constants/pages';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect } from 'react';
|
||||
import { PHOTOS_PAGES as PAGES } from "@ente/shared/constants/pages";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const IndexPage = () => {
|
||||
const router = useRouter();
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import LoginPage from '@ente/accounts/pages/login';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import LoginPage from "@ente/accounts/pages/login";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function Login() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import RecoverPage from '@ente/accounts/pages/recover';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import RecoverPage from "@ente/accounts/pages/recover";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function Recover() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import SignupPage from '@ente/accounts/pages/signup';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import SignupPage from "@ente/accounts/pages/signup";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function Sigup() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import TwoFactorRecoverPage from '@ente/accounts/pages/two-factor/recover';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import TwoFactorRecoverPage from "@ente/accounts/pages/two-factor/recover";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function TwoFactorRecover() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import TwoFactorSetupPage from '@ente/accounts/pages/two-factor/setup';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import TwoFactorSetupPage from "@ente/accounts/pages/two-factor/setup";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function TwoFactorSetup() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import TwoFactorVerifyPage from '@ente/accounts/pages/two-factor/verify';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import TwoFactorVerifyPage from "@ente/accounts/pages/two-factor/verify";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function TwoFactorVerify() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import VerifyPage from '@ente/accounts/pages/verify';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { useContext } from 'react';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import VerifyPage from "@ente/accounts/pages/verify";
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { useRouter } from "next/router";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default function Verify() {
|
||||
const appContext = useContext(AppContext);
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { HttpStatusCode } from 'axios';
|
||||
import HTTPService from '@ente/shared/network/HTTPService';
|
||||
import { AuthEntity, AuthKey } from 'types/api';
|
||||
import { Code } from 'types/code';
|
||||
import ComlinkCryptoWorker from '@ente/shared/crypto';
|
||||
import { getEndpoint } from '@ente/shared/network/api';
|
||||
import { getActualKey } from '@ente/shared/user';
|
||||
import { getToken } from '@ente/shared/storage/localStorage/helpers';
|
||||
import { ApiError, CustomError } from '@ente/shared/error';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
import ComlinkCryptoWorker from "@ente/shared/crypto";
|
||||
import { ApiError, CustomError } from "@ente/shared/error";
|
||||
import HTTPService from "@ente/shared/network/HTTPService";
|
||||
import { getEndpoint } from "@ente/shared/network/api";
|
||||
import { logError } from "@ente/shared/sentry";
|
||||
import { getToken } from "@ente/shared/storage/localStorage/helpers";
|
||||
import { getActualKey } from "@ente/shared/user";
|
||||
import { HttpStatusCode } from "axios";
|
||||
import { AuthEntity, AuthKey } from "types/api";
|
||||
import { Code } from "types/code";
|
||||
|
||||
const ENDPOINT = getEndpoint();
|
||||
export const getAuthCodes = async (): Promise<Code[]> => {
|
||||
|
@ -18,7 +18,7 @@ export const getAuthCodes = async (): Promise<Code[]> => {
|
|||
const authenticatorKey = await cryptoWorker.decryptB64(
|
||||
authKeyData.encryptedKey,
|
||||
authKeyData.header,
|
||||
masterKey
|
||||
masterKey,
|
||||
);
|
||||
// always fetch all data from server for now
|
||||
const authEntity: AuthEntity[] = await getDiff(0);
|
||||
|
@ -31,21 +31,21 @@ export const getAuthCodes = async (): Promise<Code[]> => {
|
|||
await cryptoWorker.decryptMetadata(
|
||||
entity.encryptedData,
|
||||
entity.header,
|
||||
authenticatorKey
|
||||
authenticatorKey,
|
||||
);
|
||||
return Code.fromRawData(entity.id, decryptedCode);
|
||||
} catch (e) {
|
||||
logError(
|
||||
Error('failed to parse code'),
|
||||
'codeId = ' + entity.id
|
||||
Error("failed to parse code"),
|
||||
"codeId = " + entity.id,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
// Remove null and undefined values
|
||||
const filteredAuthCodes = authCodes.filter(
|
||||
(f) => f !== null && f !== undefined
|
||||
(f) => f !== null && f !== undefined,
|
||||
);
|
||||
filteredAuthCodes.sort((a, b) => {
|
||||
if (a.issuer && b.issuer) {
|
||||
|
@ -62,7 +62,7 @@ export const getAuthCodes = async (): Promise<Code[]> => {
|
|||
return filteredAuthCodes;
|
||||
} catch (e) {
|
||||
if (e.message !== CustomError.AUTH_KEY_NOT_FOUND) {
|
||||
logError(e, 'get authenticator entities failed');
|
||||
logError(e, "get authenticator entities failed");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
@ -74,8 +74,8 @@ export const getAuthKey = async (): Promise<AuthKey> => {
|
|||
`${ENDPOINT}/authenticator/key`,
|
||||
{},
|
||||
{
|
||||
'X-Auth-Token': getToken(),
|
||||
}
|
||||
"X-Auth-Token": getToken(),
|
||||
},
|
||||
);
|
||||
return resp.data;
|
||||
} catch (e) {
|
||||
|
@ -85,7 +85,7 @@ export const getAuthKey = async (): Promise<AuthKey> => {
|
|||
) {
|
||||
throw Error(CustomError.AUTH_KEY_NOT_FOUND);
|
||||
} else {
|
||||
logError(e, 'Get key failed');
|
||||
logError(e, "Get key failed");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ export const getAuthKey = async (): Promise<AuthKey> => {
|
|||
// return a promise which resolves to list of AuthEnitity
|
||||
export const getDiff = async (
|
||||
sinceTime: number,
|
||||
limit = 2500
|
||||
limit = 2500,
|
||||
): Promise<AuthEntity[]> => {
|
||||
try {
|
||||
const resp = await HTTPService.get(
|
||||
|
@ -104,12 +104,12 @@ export const getDiff = async (
|
|||
limit,
|
||||
},
|
||||
{
|
||||
'X-Auth-Token': getToken(),
|
||||
}
|
||||
"X-Auth-Token": getToken(),
|
||||
},
|
||||
);
|
||||
return resp.data.diff;
|
||||
} catch (e) {
|
||||
logError(e, 'Get diff failed');
|
||||
logError(e, "Get diff failed");
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import { URI } from 'vscode-uri';
|
||||
import { URI } from "vscode-uri";
|
||||
|
||||
type Type = 'totp' | 'TOTP' | 'hotp' | 'HOTP';
|
||||
type Type = "totp" | "TOTP" | "hotp" | "HOTP";
|
||||
|
||||
type AlgorithmType =
|
||||
| 'sha1'
|
||||
| 'SHA1'
|
||||
| 'sha256'
|
||||
| 'SHA256'
|
||||
| 'sha512'
|
||||
| 'SHA512';
|
||||
| "sha1"
|
||||
| "SHA1"
|
||||
| "sha256"
|
||||
| "SHA256"
|
||||
| "sha512"
|
||||
| "SHA512";
|
||||
|
||||
export class Code {
|
||||
static readonly defaultDigits = 6;
|
||||
static readonly defaultAlgo = 'sha1';
|
||||
static readonly defaultAlgo = "sha1";
|
||||
static readonly defaultPeriod = 30;
|
||||
|
||||
// id for the corresponding auth entity
|
||||
|
@ -35,7 +35,7 @@ export class Code {
|
|||
algorithm: AlgorithmType,
|
||||
type: Type,
|
||||
rawData?: string,
|
||||
id?: string
|
||||
id?: string,
|
||||
) {
|
||||
this.account = account;
|
||||
this.issuer = issuer;
|
||||
|
@ -50,36 +50,36 @@ export class Code {
|
|||
|
||||
static fromRawData(id: string, rawData: string): Code {
|
||||
let santizedRawData = rawData
|
||||
.replace(/\+/g, '%2B')
|
||||
.replace(/:/g, '%3A')
|
||||
.replaceAll('\r', '');
|
||||
.replace(/\+/g, "%2B")
|
||||
.replace(/:/g, "%3A")
|
||||
.replaceAll("\r", "");
|
||||
if (santizedRawData.startsWith('"')) {
|
||||
santizedRawData = santizedRawData.substring(1);
|
||||
}
|
||||
if (santizedRawData.endsWith('"')) {
|
||||
santizedRawData = santizedRawData.substring(
|
||||
0,
|
||||
santizedRawData.length - 1
|
||||
santizedRawData.length - 1,
|
||||
);
|
||||
}
|
||||
|
||||
const uriParams = {};
|
||||
const searchParamsString =
|
||||
decodeURIComponent(santizedRawData).split('?')[1];
|
||||
searchParamsString.split('&').forEach((pair) => {
|
||||
const [key, value] = pair.split('=');
|
||||
decodeURIComponent(santizedRawData).split("?")[1];
|
||||
searchParamsString.split("&").forEach((pair) => {
|
||||
const [key, value] = pair.split("=");
|
||||
uriParams[key] = value;
|
||||
});
|
||||
|
||||
const uri = URI.parse(santizedRawData);
|
||||
let uriPath = decodeURIComponent(uri.path);
|
||||
if (
|
||||
uriPath.startsWith('/otpauth://') ||
|
||||
uriPath.startsWith('otpauth://')
|
||||
uriPath.startsWith("/otpauth://") ||
|
||||
uriPath.startsWith("otpauth://")
|
||||
) {
|
||||
uriPath = uriPath.split('otpauth://')[1];
|
||||
} else if (uriPath.startsWith('otpauth%3A//')) {
|
||||
uriPath = uriPath.split('otpauth%3A//')[1];
|
||||
uriPath = uriPath.split("otpauth://")[1];
|
||||
} else if (uriPath.startsWith("otpauth%3A//")) {
|
||||
uriPath = uriPath.split("otpauth%3A//")[1];
|
||||
}
|
||||
|
||||
return new Code(
|
||||
|
@ -91,54 +91,54 @@ export class Code {
|
|||
Code._getAlgorithm(uriParams),
|
||||
Code._getType(uriPath),
|
||||
rawData,
|
||||
id
|
||||
id,
|
||||
);
|
||||
}
|
||||
|
||||
private static _getAccount(uriPath: string): string {
|
||||
try {
|
||||
const path = decodeURIComponent(uriPath);
|
||||
if (path.includes(':')) {
|
||||
return path.split(':')[1];
|
||||
} else if (path.includes('/')) {
|
||||
return path.split('/')[1];
|
||||
if (path.includes(":")) {
|
||||
return path.split(":")[1];
|
||||
} else if (path.includes("/")) {
|
||||
return path.split("/")[1];
|
||||
}
|
||||
} catch (e) {
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static _getIssuer(
|
||||
uriPath: string,
|
||||
uriParams: { get?: any }
|
||||
uriParams: { get?: any },
|
||||
): string {
|
||||
try {
|
||||
if (uriParams['issuer'] !== undefined) {
|
||||
let issuer = uriParams['issuer'];
|
||||
if (uriParams["issuer"] !== undefined) {
|
||||
let issuer = uriParams["issuer"];
|
||||
// This is to handle bug in the ente auth app
|
||||
if (issuer.endsWith('period')) {
|
||||
if (issuer.endsWith("period")) {
|
||||
issuer = issuer.substring(0, issuer.length - 6);
|
||||
}
|
||||
return issuer;
|
||||
}
|
||||
let path = decodeURIComponent(uriPath);
|
||||
if (path.startsWith('totp/') || path.startsWith('hotp/')) {
|
||||
if (path.startsWith("totp/") || path.startsWith("hotp/")) {
|
||||
path = path.substring(5);
|
||||
}
|
||||
if (path.includes(':')) {
|
||||
return path.split(':')[0];
|
||||
} else if (path.includes('-')) {
|
||||
return path.split('-')[0];
|
||||
if (path.includes(":")) {
|
||||
return path.split(":")[0];
|
||||
} else if (path.includes("-")) {
|
||||
return path.split("-")[0];
|
||||
}
|
||||
return path;
|
||||
} catch (e) {
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static _getDigits(uriParams): number {
|
||||
try {
|
||||
return parseInt(uriParams['digits'], 10) || Code.defaultDigits;
|
||||
return parseInt(uriParams["digits"], 10) || Code.defaultDigits;
|
||||
} catch (e) {
|
||||
return Code.defaultDigits;
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ export class Code {
|
|||
|
||||
private static _getPeriod(uriParams): number {
|
||||
try {
|
||||
return parseInt(uriParams['period'], 10) || Code.defaultPeriod;
|
||||
return parseInt(uriParams["period"], 10) || Code.defaultPeriod;
|
||||
} catch (e) {
|
||||
return Code.defaultPeriod;
|
||||
}
|
||||
|
@ -154,29 +154,29 @@ export class Code {
|
|||
|
||||
private static _getAlgorithm(uriParams): AlgorithmType {
|
||||
try {
|
||||
const algorithm = uriParams['algorithm'].toLowerCase();
|
||||
if (algorithm === 'sha256') {
|
||||
const algorithm = uriParams["algorithm"].toLowerCase();
|
||||
if (algorithm === "sha256") {
|
||||
return algorithm;
|
||||
} else if (algorithm === 'sha512') {
|
||||
} else if (algorithm === "sha512") {
|
||||
return algorithm;
|
||||
}
|
||||
} catch (e) {
|
||||
// nothing
|
||||
}
|
||||
return 'sha1';
|
||||
return "sha1";
|
||||
}
|
||||
|
||||
private static _getType(uriPath: string): Type {
|
||||
const oauthType = uriPath.split('/')[0].substring(0);
|
||||
if (oauthType.toLowerCase() === 'totp') {
|
||||
return 'totp';
|
||||
} else if (oauthType.toLowerCase() === 'hotp') {
|
||||
return 'hotp';
|
||||
const oauthType = uriPath.split("/")[0].substring(0);
|
||||
if (oauthType.toLowerCase() === "totp") {
|
||||
return "totp";
|
||||
} else if (oauthType.toLowerCase() === "hotp") {
|
||||
return "hotp";
|
||||
}
|
||||
throw new Error(`Unsupported format with host ${oauthType}`);
|
||||
}
|
||||
|
||||
static getSanitizedSecret(uriParams): string {
|
||||
return uriParams['secret'].replace(/ /g, '').toUpperCase();
|
||||
return uriParams["secret"].replace(/ /g, "").toUpperCase();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ module.exports = {
|
|||
// This is required here to ensure desktop picks the right eslint config, where this app is
|
||||
// packaged as a submodule.
|
||||
root: true,
|
||||
extends: ['@ente/eslint-config'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
extends: ["@ente/eslint-config"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json',
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js', 'out'],
|
||||
ignorePatterns: [".eslintrc.js", "out"],
|
||||
};
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
const nextConfigBase = require('@/next/next.config.base.js');
|
||||
const nextConfigBase = require("@/next/next.config.base.js");
|
||||
|
||||
module.exports = nextConfigBase;
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import { initSentry } from '@ente/shared/sentry/config/sentry.config.base';
|
||||
import { initSentry } from "@ente/shared/sentry/config/sentry.config.base";
|
||||
|
||||
initSentry('https://0f7214c7feb9b1dd2fed5db09b42fa1b@sentry.ente.io/5');
|
||||
initSentry("https://0f7214c7feb9b1dd2fed5db09b42fa1b@sentry.ente.io/5");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import styles from './FilledCircleCheck.module.scss'; // Import our CSS module
|
||||
import { useEffect, useState } from "react";
|
||||
import styles from "./FilledCircleCheck.module.scss"; // Import our CSS module
|
||||
|
||||
const FilledCircleCheck = () => {
|
||||
const [animate, setAnimate] = useState(false);
|
||||
|
@ -9,11 +9,12 @@ const FilledCircleCheck = () => {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<div className={`${styles.circle} ${animate ? styles.animate : ''}`}>
|
||||
<div className={`${styles.circle} ${animate ? styles.animate : ""}`}>
|
||||
<svg
|
||||
className={styles.checkmark}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 52 52">
|
||||
viewBox="0 0 52 52"
|
||||
>
|
||||
<circle
|
||||
className={styles.checkmark__circle}
|
||||
cx="26"
|
||||
|
|
|
@ -1,58 +1,62 @@
|
|||
const colourPool = [
|
||||
'#87CEFA', // Light Blue
|
||||
'#90EE90', // Light Green
|
||||
'#F08080', // Light Coral
|
||||
'#FFFFE0', // Light Yellow
|
||||
'#FFB6C1', // Light Pink
|
||||
'#E0FFFF', // Light Cyan
|
||||
'#FAFAD2', // Light Goldenrod
|
||||
'#87CEFA', // Light Sky Blue
|
||||
'#D3D3D3', // Light Gray
|
||||
'#B0C4DE', // Light Steel Blue
|
||||
'#FFA07A', // Light Salmon
|
||||
'#20B2AA', // Light Sea Green
|
||||
'#778899', // Light Slate Gray
|
||||
'#AFEEEE', // Light Turquoise
|
||||
'#7A58C1', // Light Violet
|
||||
'#FFA500', // Light Orange
|
||||
'#A0522D', // Light Brown
|
||||
'#9370DB', // Light Purple
|
||||
'#008080', // Light Teal
|
||||
'#808000', // Light Olive
|
||||
"#87CEFA", // Light Blue
|
||||
"#90EE90", // Light Green
|
||||
"#F08080", // Light Coral
|
||||
"#FFFFE0", // Light Yellow
|
||||
"#FFB6C1", // Light Pink
|
||||
"#E0FFFF", // Light Cyan
|
||||
"#FAFAD2", // Light Goldenrod
|
||||
"#87CEFA", // Light Sky Blue
|
||||
"#D3D3D3", // Light Gray
|
||||
"#B0C4DE", // Light Steel Blue
|
||||
"#FFA07A", // Light Salmon
|
||||
"#20B2AA", // Light Sea Green
|
||||
"#778899", // Light Slate Gray
|
||||
"#AFEEEE", // Light Turquoise
|
||||
"#7A58C1", // Light Violet
|
||||
"#FFA500", // Light Orange
|
||||
"#A0522D", // Light Brown
|
||||
"#9370DB", // Light Purple
|
||||
"#008080", // Light Teal
|
||||
"#808000", // Light Olive
|
||||
];
|
||||
|
||||
export default function LargeType({ chars }: { chars: string[] }) {
|
||||
return (
|
||||
<table
|
||||
style={{
|
||||
fontSize: '4rem',
|
||||
fontWeight: 'bold',
|
||||
fontFamily: 'monospace',
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
}}>
|
||||
fontSize: "4rem",
|
||||
fontWeight: "bold",
|
||||
fontFamily: "monospace",
|
||||
display: "flex",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
{chars.map((char, i) => (
|
||||
<tr
|
||||
key={i}
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: '0.5rem',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
padding: "0.5rem",
|
||||
// alternating background
|
||||
backgroundColor: i % 2 === 0 ? '#2e2e2e' : '#5e5e5e',
|
||||
}}>
|
||||
backgroundColor: i % 2 === 0 ? "#2e2e2e" : "#5e5e5e",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
color: colourPool[i % colourPool.length],
|
||||
lineHeight: 1.2,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{char}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: '1rem',
|
||||
}}>
|
||||
fontSize: "1rem",
|
||||
}}
|
||||
>
|
||||
{i + 1}
|
||||
</span>
|
||||
</tr>
|
||||
|
|
|
@ -1,38 +1,42 @@
|
|||
import FilledCircleCheck from './FilledCircleCheck';
|
||||
import FilledCircleCheck from "./FilledCircleCheck";
|
||||
|
||||
export default function PairedSuccessfullyOverlay() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
right: 0,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
zIndex: 100,
|
||||
backgroundColor: 'black',
|
||||
}}>
|
||||
backgroundColor: "black",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<FilledCircleCheck />
|
||||
<h2
|
||||
style={{
|
||||
marginBottom: 0,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Pairing Complete
|
||||
</h2>
|
||||
<p
|
||||
style={{
|
||||
lineHeight: '1.5rem',
|
||||
}}>
|
||||
lineHeight: "1.5rem",
|
||||
}}
|
||||
>
|
||||
We're preparing your album.
|
||||
<br /> This should only take a few seconds.
|
||||
</p>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { SlideshowContext } from 'pages/slideshow';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { SlideshowContext } from "pages/slideshow";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
|
||||
export default function PhotoAuditorium({
|
||||
url,
|
||||
|
@ -49,38 +49,40 @@ export default function PhotoAuditorium({
|
|||
return (
|
||||
<div
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
backgroundImage: `url(${url})`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundBlendMode: 'multiply',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
}}>
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundBlendMode: "multiply",
|
||||
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backdropFilter: 'blur(10px)',
|
||||
}}>
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backdropFilter: "blur(10px)",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={url}
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '100%',
|
||||
display: showPreloadedNextSlide ? 'none' : 'block',
|
||||
maxWidth: "100%",
|
||||
maxHeight: "100%",
|
||||
display: showPreloadedNextSlide ? "none" : "block",
|
||||
}}
|
||||
/>
|
||||
<img
|
||||
src={nextSlideUrl}
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '100%',
|
||||
display: showPreloadedNextSlide ? 'block' : 'none',
|
||||
maxWidth: "100%",
|
||||
maxHeight: "100%",
|
||||
display: showPreloadedNextSlide ? "block" : "none",
|
||||
}}
|
||||
onLoad={() => {
|
||||
setNextSlidePrerendered(true);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import mime from 'mime-types';
|
||||
import { SlideshowContext } from 'pages/slideshow';
|
||||
import { useContext, useEffect, useRef } from 'react';
|
||||
import mime from "mime-types";
|
||||
import { SlideshowContext } from "pages/slideshow";
|
||||
import { useContext, useEffect, useRef } from "react";
|
||||
|
||||
export default function VideoAuditorium({
|
||||
name,
|
||||
|
@ -30,22 +30,24 @@ export default function VideoAuditorium({
|
|||
return (
|
||||
<div
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<video
|
||||
ref={videoRef}
|
||||
autoPlay
|
||||
controls
|
||||
style={{
|
||||
maxWidth: '100vw',
|
||||
maxHeight: '100vh',
|
||||
maxWidth: "100vw",
|
||||
maxHeight: "100vh",
|
||||
}}
|
||||
onError={showNextSlide}
|
||||
onEnded={showNextSlide}>
|
||||
onEnded={showNextSlide}
|
||||
>
|
||||
<source src={url} type={mime.lookup(name)} />
|
||||
</video>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { FILE_TYPE } from 'constants/file';
|
||||
import PhotoAuditorium from './PhotoAuditorium';
|
||||
import { FILE_TYPE } from "constants/file";
|
||||
import PhotoAuditorium from "./PhotoAuditorium";
|
||||
// import VideoAuditorium from './VideoAuditorium';
|
||||
|
||||
interface fileProp {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function TimerBar({ percentage }: { percentage: number }) {
|
||||
const okColor = '#75C157';
|
||||
const warningColor = '#FFC000';
|
||||
const lateColor = '#FF0000';
|
||||
const okColor = "#75C157";
|
||||
const warningColor = "#FFC000";
|
||||
const lateColor = "#FF0000";
|
||||
|
||||
const [backgroundColor, setBackgroundColor] = useState(okColor);
|
||||
|
||||
|
@ -21,9 +21,9 @@ export default function TimerBar({ percentage }: { percentage: number }) {
|
|||
<div
|
||||
style={{
|
||||
width: `${percentage}%`, // Set the width based on the time left
|
||||
height: '10px', // Same as the border thickness
|
||||
height: "10px", // Same as the border thickness
|
||||
backgroundColor, // The color of the moving border
|
||||
transition: 'width 1s linear', // Smooth transition for the width change
|
||||
transition: "width 1s linear", // Smooth transition for the width change
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { getAlbumsURL } from '@ente/shared/network/api';
|
||||
import { runningInBrowser } from '@ente/shared/platform';
|
||||
import { PAGES } from 'constants/pages';
|
||||
import { getAlbumsURL } from "@ente/shared/network/api";
|
||||
import { runningInBrowser } from "@ente/shared/platform";
|
||||
import { PAGES } from "constants/pages";
|
||||
|
||||
export enum APPS {
|
||||
PHOTOS = 'PHOTOS',
|
||||
AUTH = 'AUTH',
|
||||
ALBUMS = 'ALBUMS',
|
||||
PHOTOS = "PHOTOS",
|
||||
AUTH = "AUTH",
|
||||
ALBUMS = "ALBUMS",
|
||||
}
|
||||
|
||||
export const ALLOWED_APP_PAGES = new Map([
|
||||
|
@ -29,9 +29,9 @@ export const ALLOWED_APP_PAGES = new Map([
|
|||
]);
|
||||
|
||||
export const CLIENT_PACKAGE_NAMES = new Map([
|
||||
[APPS.ALBUMS, 'io.ente.albums.web'],
|
||||
[APPS.PHOTOS, 'io.ente.photos.web'],
|
||||
[APPS.AUTH, 'io.ente.auth.web'],
|
||||
[APPS.ALBUMS, "io.ente.albums.web"],
|
||||
[APPS.PHOTOS, "io.ente.photos.web"],
|
||||
[APPS.AUTH, "io.ente.auth.web"],
|
||||
]);
|
||||
|
||||
export const getAppNameAndTitle = () => {
|
||||
|
@ -41,9 +41,9 @@ export const getAppNameAndTitle = () => {
|
|||
const currentURL = new URL(window.location.href);
|
||||
const albumsURL = new URL(getAlbumsURL());
|
||||
if (currentURL.origin === albumsURL.origin) {
|
||||
return { name: APPS.ALBUMS, title: 'ente Photos' };
|
||||
return { name: APPS.ALBUMS, title: "ente Photos" };
|
||||
} else {
|
||||
return { name: APPS.PHOTOS, title: 'ente Photos' };
|
||||
return { name: APPS.PHOTOS, title: "ente Photos" };
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export enum CACHES {
|
||||
THUMBS = 'thumbs',
|
||||
FACE_CROPS = 'face-crops',
|
||||
FILES = 'files',
|
||||
THUMBS = "thumbs",
|
||||
FACE_CROPS = "face-crops",
|
||||
FILES = "files",
|
||||
}
|
||||
|
|
|
@ -3,31 +3,31 @@ export const TRASH_SECTION = -2;
|
|||
export const DUMMY_UNCATEGORIZED_COLLECTION = -3;
|
||||
export const HIDDEN_ITEMS_SECTION = -4;
|
||||
export const ALL_SECTION = 0;
|
||||
export const DEFAULT_HIDDEN_COLLECTION_USER_FACING_NAME = 'Hidden';
|
||||
export const DEFAULT_HIDDEN_COLLECTION_USER_FACING_NAME = "Hidden";
|
||||
|
||||
export enum CollectionType {
|
||||
folder = 'folder',
|
||||
favorites = 'favorites',
|
||||
album = 'album',
|
||||
uncategorized = 'uncategorized',
|
||||
folder = "folder",
|
||||
favorites = "favorites",
|
||||
album = "album",
|
||||
uncategorized = "uncategorized",
|
||||
}
|
||||
|
||||
export enum CollectionSummaryType {
|
||||
folder = 'folder',
|
||||
favorites = 'favorites',
|
||||
album = 'album',
|
||||
archive = 'archive',
|
||||
trash = 'trash',
|
||||
uncategorized = 'uncategorized',
|
||||
all = 'all',
|
||||
outgoingShare = 'outgoingShare',
|
||||
incomingShareViewer = 'incomingShareViewer',
|
||||
incomingShareCollaborator = 'incomingShareCollaborator',
|
||||
sharedOnlyViaLink = 'sharedOnlyViaLink',
|
||||
archived = 'archived',
|
||||
defaultHidden = 'defaultHidden',
|
||||
hiddenItems = 'hiddenItems',
|
||||
pinned = 'pinned',
|
||||
folder = "folder",
|
||||
favorites = "favorites",
|
||||
album = "album",
|
||||
archive = "archive",
|
||||
trash = "trash",
|
||||
uncategorized = "uncategorized",
|
||||
all = "all",
|
||||
outgoingShare = "outgoingShare",
|
||||
incomingShareViewer = "incomingShareViewer",
|
||||
incomingShareCollaborator = "incomingShareCollaborator",
|
||||
sharedOnlyViaLink = "sharedOnlyViaLink",
|
||||
archived = "archived",
|
||||
defaultHidden = "defaultHidden",
|
||||
hiddenItems = "hiddenItems",
|
||||
pinned = "pinned",
|
||||
}
|
||||
export enum COLLECTION_LIST_SORT_BY {
|
||||
NAME,
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export const INPUT_PATH_PLACEHOLDER = 'INPUT';
|
||||
export const FFMPEG_PLACEHOLDER = 'FFMPEG';
|
||||
export const OUTPUT_PATH_PLACEHOLDER = 'OUTPUT';
|
||||
export const INPUT_PATH_PLACEHOLDER = "INPUT";
|
||||
export const FFMPEG_PLACEHOLDER = "FFMPEG";
|
||||
export const OUTPUT_PATH_PLACEHOLDER = "OUTPUT";
|
||||
|
|
|
@ -4,10 +4,10 @@ export const MAX_EDITED_CREATION_TIME = new Date();
|
|||
export const MAX_EDITED_FILE_NAME_LENGTH = 100;
|
||||
export const MAX_CAPTION_SIZE = 5000;
|
||||
|
||||
export const TYPE_HEIC = 'heic';
|
||||
export const TYPE_HEIF = 'heif';
|
||||
export const TYPE_JPEG = 'jpeg';
|
||||
export const TYPE_JPG = 'jpg';
|
||||
export const TYPE_HEIC = "heic";
|
||||
export const TYPE_HEIF = "heif";
|
||||
export const TYPE_JPEG = "jpeg";
|
||||
export const TYPE_JPG = "jpg";
|
||||
|
||||
export enum FILE_TYPE {
|
||||
IMAGE,
|
||||
|
@ -17,27 +17,27 @@ export enum FILE_TYPE {
|
|||
}
|
||||
|
||||
export const RAW_FORMATS = [
|
||||
'heic',
|
||||
'rw2',
|
||||
'tiff',
|
||||
'arw',
|
||||
'cr3',
|
||||
'cr2',
|
||||
'raf',
|
||||
'nef',
|
||||
'psd',
|
||||
'dng',
|
||||
'tif',
|
||||
"heic",
|
||||
"rw2",
|
||||
"tiff",
|
||||
"arw",
|
||||
"cr3",
|
||||
"cr2",
|
||||
"raf",
|
||||
"nef",
|
||||
"psd",
|
||||
"dng",
|
||||
"tif",
|
||||
];
|
||||
export const SUPPORTED_RAW_FORMATS = [
|
||||
'heic',
|
||||
'rw2',
|
||||
'tiff',
|
||||
'arw',
|
||||
'cr3',
|
||||
'cr2',
|
||||
'nef',
|
||||
'psd',
|
||||
'dng',
|
||||
'tif',
|
||||
"heic",
|
||||
"rw2",
|
||||
"tiff",
|
||||
"arw",
|
||||
"cr3",
|
||||
"cr2",
|
||||
"nef",
|
||||
"psd",
|
||||
"dng",
|
||||
"tif",
|
||||
];
|
||||
|
|
|
@ -8,8 +8,8 @@ export const SPACE_BTW_DATES = 44;
|
|||
export const SPACE_BTW_DATES_TO_IMAGE_CONTAINER_WIDTH_RATIO = 0.244;
|
||||
|
||||
export enum PLAN_PERIOD {
|
||||
MONTH = 'month',
|
||||
YEAR = 'year',
|
||||
MONTH = "month",
|
||||
YEAR = "year",
|
||||
}
|
||||
|
||||
export const SYNC_INTERVAL_IN_MICROSECONDS = 1000 * 60 * 5; // 5 minutes
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
export enum PAGES {
|
||||
CHANGE_EMAIL = '/change-email',
|
||||
CHANGE_PASSWORD = '/change-password',
|
||||
CREDENTIALS = '/credentials',
|
||||
GALLERY = '/gallery',
|
||||
GENERATE = '/generate',
|
||||
LOGIN = '/login',
|
||||
RECOVER = '/recover',
|
||||
SIGNUP = '/signup',
|
||||
TWO_FACTOR_SETUP = '/two-factor/setup',
|
||||
TWO_FACTOR_VERIFY = '/two-factor/verify',
|
||||
TWO_FACTOR_RECOVER = '/two-factor/recover',
|
||||
VERIFY = '/verify',
|
||||
ROOT = '/',
|
||||
SHARED_ALBUMS = '/shared-albums',
|
||||
CHANGE_EMAIL = "/change-email",
|
||||
CHANGE_PASSWORD = "/change-password",
|
||||
CREDENTIALS = "/credentials",
|
||||
GALLERY = "/gallery",
|
||||
GENERATE = "/generate",
|
||||
LOGIN = "/login",
|
||||
RECOVER = "/recover",
|
||||
SIGNUP = "/signup",
|
||||
TWO_FACTOR_SETUP = "/two-factor/setup",
|
||||
TWO_FACTOR_VERIFY = "/two-factor/verify",
|
||||
TWO_FACTOR_RECOVER = "/two-factor/recover",
|
||||
VERIFY = "/verify",
|
||||
ROOT = "/",
|
||||
SHARED_ALBUMS = "/shared-albums",
|
||||
// ML_DEBUG = '/ml-debug',
|
||||
DEDUPLICATE = '/deduplicate',
|
||||
DEDUPLICATE = "/deduplicate",
|
||||
// AUTH page is used to show (auth)enticator codes
|
||||
AUTH = '/auth',
|
||||
AUTH = "/auth",
|
||||
}
|
||||
|
|
|
@ -1,52 +1,52 @@
|
|||
import { ENCRYPTION_CHUNK_SIZE } from '@ente/shared/crypto/constants';
|
||||
import { FILE_TYPE } from 'constants/file';
|
||||
import { ENCRYPTION_CHUNK_SIZE } from "@ente/shared/crypto/constants";
|
||||
import { FILE_TYPE } from "constants/file";
|
||||
import {
|
||||
FileTypeInfo,
|
||||
ImportSuggestion,
|
||||
Location,
|
||||
ParsedExtractedMetadata,
|
||||
} from 'types/upload';
|
||||
} from "types/upload";
|
||||
|
||||
// list of format that were missed by type-detection for some files.
|
||||
export const WHITELISTED_FILE_FORMATS: FileTypeInfo[] = [
|
||||
{ fileType: FILE_TYPE.IMAGE, exactType: 'jpeg', mimeType: 'image/jpeg' },
|
||||
{ fileType: FILE_TYPE.IMAGE, exactType: 'jpg', mimeType: 'image/jpeg' },
|
||||
{ fileType: FILE_TYPE.VIDEO, exactType: 'webm', mimeType: 'video/webm' },
|
||||
{ fileType: FILE_TYPE.VIDEO, exactType: 'mod', mimeType: 'video/mpeg' },
|
||||
{ fileType: FILE_TYPE.VIDEO, exactType: 'mp4', mimeType: 'video/mp4' },
|
||||
{ fileType: FILE_TYPE.IMAGE, exactType: 'gif', mimeType: 'image/gif' },
|
||||
{ fileType: FILE_TYPE.VIDEO, exactType: 'dv', mimeType: 'video/x-dv' },
|
||||
{ fileType: FILE_TYPE.IMAGE, exactType: "jpeg", mimeType: "image/jpeg" },
|
||||
{ fileType: FILE_TYPE.IMAGE, exactType: "jpg", mimeType: "image/jpeg" },
|
||||
{ fileType: FILE_TYPE.VIDEO, exactType: "webm", mimeType: "video/webm" },
|
||||
{ fileType: FILE_TYPE.VIDEO, exactType: "mod", mimeType: "video/mpeg" },
|
||||
{ fileType: FILE_TYPE.VIDEO, exactType: "mp4", mimeType: "video/mp4" },
|
||||
{ fileType: FILE_TYPE.IMAGE, exactType: "gif", mimeType: "image/gif" },
|
||||
{ fileType: FILE_TYPE.VIDEO, exactType: "dv", mimeType: "video/x-dv" },
|
||||
{
|
||||
fileType: FILE_TYPE.VIDEO,
|
||||
exactType: 'wmv',
|
||||
mimeType: 'video/x-ms-asf',
|
||||
exactType: "wmv",
|
||||
mimeType: "video/x-ms-asf",
|
||||
},
|
||||
{
|
||||
fileType: FILE_TYPE.VIDEO,
|
||||
exactType: 'hevc',
|
||||
mimeType: 'video/hevc',
|
||||
exactType: "hevc",
|
||||
mimeType: "video/hevc",
|
||||
},
|
||||
{
|
||||
fileType: FILE_TYPE.IMAGE,
|
||||
exactType: 'raf',
|
||||
mimeType: 'image/x-fuji-raf',
|
||||
exactType: "raf",
|
||||
mimeType: "image/x-fuji-raf",
|
||||
},
|
||||
{
|
||||
fileType: FILE_TYPE.IMAGE,
|
||||
exactType: 'orf',
|
||||
mimeType: 'image/x-olympus-orf',
|
||||
exactType: "orf",
|
||||
mimeType: "image/x-olympus-orf",
|
||||
},
|
||||
|
||||
{
|
||||
fileType: FILE_TYPE.IMAGE,
|
||||
exactType: 'crw',
|
||||
mimeType: 'image/x-canon-crw',
|
||||
exactType: "crw",
|
||||
mimeType: "image/x-canon-crw",
|
||||
},
|
||||
];
|
||||
|
||||
export const KNOWN_NON_MEDIA_FORMATS = ['xmp', 'html', 'txt'];
|
||||
export const KNOWN_NON_MEDIA_FORMATS = ["xmp", "html", "txt"];
|
||||
|
||||
export const EXIFLESS_FORMATS = ['gif', 'bmp'];
|
||||
export const EXIFLESS_FORMATS = ["gif", "bmp"];
|
||||
|
||||
// this is the chunk size of the un-encrypted file which is read and encrypted before uploading it as a single part.
|
||||
export const MULTIPART_PART_SIZE = 20 * 1024 * 1024;
|
||||
|
@ -54,7 +54,7 @@ export const MULTIPART_PART_SIZE = 20 * 1024 * 1024;
|
|||
export const FILE_READER_CHUNK_SIZE = ENCRYPTION_CHUNK_SIZE;
|
||||
|
||||
export const FILE_CHUNKS_COMBINED_FOR_A_UPLOAD_PART = Math.floor(
|
||||
MULTIPART_PART_SIZE / FILE_READER_CHUNK_SIZE
|
||||
MULTIPART_PART_SIZE / FILE_READER_CHUNK_SIZE,
|
||||
);
|
||||
|
||||
export const RANDOM_PERCENTAGE_PROGRESS_FOR_PUT = () => 90 + 10 * Math.random();
|
||||
|
@ -88,9 +88,9 @@ export enum UPLOAD_RESULT {
|
|||
}
|
||||
|
||||
export enum PICKED_UPLOAD_TYPE {
|
||||
FILES = 'files',
|
||||
FOLDERS = 'folders',
|
||||
ZIPS = 'zips',
|
||||
FILES = "files",
|
||||
FOLDERS = "folders",
|
||||
ZIPS = "zips",
|
||||
}
|
||||
|
||||
export const MAX_FILE_SIZE_SUPPORTED = 4 * 1024 * 1024 * 1024; // 4 GB
|
||||
|
@ -107,36 +107,36 @@ export const NULL_EXTRACTED_METADATA: ParsedExtractedMetadata = {
|
|||
export const A_SEC_IN_MICROSECONDS = 1e6;
|
||||
|
||||
export const DEFAULT_IMPORT_SUGGESTION: ImportSuggestion = {
|
||||
rootFolderName: '',
|
||||
rootFolderName: "",
|
||||
hasNestedFolders: false,
|
||||
hasRootLevelFileWithFolder: false,
|
||||
};
|
||||
|
||||
export const BLACK_THUMBNAIL_BASE64 =
|
||||
'/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEB' +
|
||||
'AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQ' +
|
||||
'EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARC' +
|
||||
'ACWASwDAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUF' +
|
||||
'BAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk' +
|
||||
'6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztL' +
|
||||
'W2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAA' +
|
||||
'AAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVY' +
|
||||
'nLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImK' +
|
||||
'kpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oAD' +
|
||||
'AMBAAIRAxEAPwD/AD/6ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA' +
|
||||
'CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg' +
|
||||
'AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAC' +
|
||||
'gAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo' +
|
||||
'AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg' +
|
||||
'AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg' +
|
||||
'AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA' +
|
||||
'CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA' +
|
||||
'CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA' +
|
||||
'KACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg' +
|
||||
'AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo' +
|
||||
'AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA' +
|
||||
'CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAK' +
|
||||
'ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA' +
|
||||
'KACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo' +
|
||||
'AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo' +
|
||||
'AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD/9k=';
|
||||
"/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEB" +
|
||||
"AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQ" +
|
||||
"EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARC" +
|
||||
"ACWASwDAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUF" +
|
||||
"BAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk" +
|
||||
"6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztL" +
|
||||
"W2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAA" +
|
||||
"AAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVY" +
|
||||
"nLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImK" +
|
||||
"kpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oAD" +
|
||||
"AMBAAIRAxEAPwD/AD/6ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA" +
|
||||
"CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg" +
|
||||
"AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAC" +
|
||||
"gAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo" +
|
||||
"AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg" +
|
||||
"AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg" +
|
||||
"AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA" +
|
||||
"CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA" +
|
||||
"CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA" +
|
||||
"KACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg" +
|
||||
"AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo" +
|
||||
"AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA" +
|
||||
"CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAK" +
|
||||
"ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA" +
|
||||
"KACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo" +
|
||||
"AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo" +
|
||||
"AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD/9k=";
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
export const ENTE_WEBSITE_LINK = 'https://ente.io';
|
||||
export const ENTE_WEBSITE_LINK = "https://ente.io";
|
||||
|
||||
export const ML_BLOG_LINK = 'https://ente.io/blog/desktop-ml-beta';
|
||||
export const ML_BLOG_LINK = "https://ente.io/blog/desktop-ml-beta";
|
||||
|
||||
export const FACE_SEARCH_PRIVACY_POLICY_LINK =
|
||||
'https://ente.io/privacy#8-biometric-information-privacy-policy';
|
||||
"https://ente.io/privacy#8-biometric-information-privacy-policy";
|
||||
|
||||
export const SUPPORT_EMAIL = 'support@ente.io';
|
||||
export const SUPPORT_EMAIL = "support@ente.io";
|
||||
|
||||
export const APP_DOWNLOAD_URL = 'https://ente.io/download/desktop';
|
||||
export const APP_DOWNLOAD_URL = "https://ente.io/download/desktop";
|
||||
|
||||
export const FEEDBACK_EMAIL = 'feedback@ente.io';
|
||||
export const FEEDBACK_EMAIL = "feedback@ente.io";
|
||||
|
||||
export const DELETE_ACCOUNT_EMAIL = 'account-deletion@ente.io';
|
||||
export const DELETE_ACCOUNT_EMAIL = "account-deletion@ente.io";
|
||||
|
||||
export const WEB_ROADMAP_URL = 'https://github.com/ente-io/photos-web/issues';
|
||||
export const WEB_ROADMAP_URL = "https://github.com/ente-io/photos-web/issues";
|
||||
|
||||
export const DESKTOP_ROADMAP_URL =
|
||||
'https://github.com/ente-io/photos-desktop/issues';
|
||||
"https://github.com/ente-io/photos-desktop/issues";
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import type { AppProps } from 'next/app';
|
||||
import 'styles/global.css';
|
||||
import { ThemeProvider, CssBaseline } from '@mui/material';
|
||||
import { getTheme } from '@ente/shared/themes';
|
||||
import { THEME_COLOR } from '@ente/shared/themes/constants';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import { APPS } from "@ente/shared/apps/constants";
|
||||
import { getTheme } from "@ente/shared/themes";
|
||||
import { THEME_COLOR } from "@ente/shared/themes/constants";
|
||||
import { CssBaseline, ThemeProvider } from "@mui/material";
|
||||
import type { AppProps } from "next/app";
|
||||
import "styles/global.css";
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
|
@ -12,8 +12,9 @@ export default function App({ Component, pageProps }: AppProps) {
|
|||
|
||||
<main
|
||||
style={{
|
||||
display: 'contents',
|
||||
}}>
|
||||
display: "contents",
|
||||
}}
|
||||
>
|
||||
<Component {...pageProps} />
|
||||
</main>
|
||||
</ThemeProvider>
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
import { Html, Head, Main, NextScript } from 'next/document';
|
||||
import { Head, Html, Main, NextScript } from "next/document";
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html
|
||||
lang="en"
|
||||
style={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}>
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Head />
|
||||
<body
|
||||
style={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
margin: 0,
|
||||
backgroundColor: 'black',
|
||||
color: 'white',
|
||||
}}>
|
||||
backgroundColor: "black",
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import EnteSpinner from '@ente/shared/components/EnteSpinner';
|
||||
import { boxSealOpen, toB64 } from '@ente/shared/crypto/internal/libsodium';
|
||||
import { useCastReceiver } from '@ente/shared/hooks/useCastReceiver';
|
||||
import { addLogLine } from '@ente/shared/logging';
|
||||
import castGateway from '@ente/shared/network/cast';
|
||||
import LargeType from 'components/LargeType';
|
||||
import _sodium from 'libsodium-wrappers';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { storeCastData } from 'services/cast/castService';
|
||||
import EnteSpinner from "@ente/shared/components/EnteSpinner";
|
||||
import { boxSealOpen, toB64 } from "@ente/shared/crypto/internal/libsodium";
|
||||
import { useCastReceiver } from "@ente/shared/hooks/useCastReceiver";
|
||||
import { addLogLine } from "@ente/shared/logging";
|
||||
import castGateway from "@ente/shared/network/cast";
|
||||
import LargeType from "components/LargeType";
|
||||
import _sodium from "libsodium-wrappers";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { storeCastData } from "services/cast/castService";
|
||||
|
||||
// Function to generate cryptographically secure digits
|
||||
const generateSecureData = (length: number): Uint8Array => {
|
||||
|
@ -21,7 +21,7 @@ const generateSecureData = (length: number): Uint8Array => {
|
|||
};
|
||||
|
||||
const convertDataToDecimalString = (data: Uint8Array): string => {
|
||||
let decimalString = '';
|
||||
let decimalString = "";
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
decimalString += data[i].toString(); // No need to pad, as each value is a single digit
|
||||
}
|
||||
|
@ -30,8 +30,8 @@ const convertDataToDecimalString = (data: Uint8Array): string => {
|
|||
|
||||
export default function PairingMode() {
|
||||
const [digits, setDigits] = useState<string[]>([]);
|
||||
const [publicKeyB64, setPublicKeyB64] = useState('');
|
||||
const [privateKeyB64, setPrivateKeyB64] = useState('');
|
||||
const [publicKeyB64, setPublicKeyB64] = useState("");
|
||||
const [privateKeyB64, setPrivateKeyB64] = useState("");
|
||||
const [codePending, setCodePending] = useState(true);
|
||||
const [isCastReady, setIsCastReady] = useState(false);
|
||||
|
||||
|
@ -49,18 +49,18 @@ export default function PairingMode() {
|
|||
try {
|
||||
const options = new cast.framework.CastReceiverOptions();
|
||||
options.customNamespaces = Object.assign({});
|
||||
options.customNamespaces['urn:x-cast:pair-request'] =
|
||||
options.customNamespaces["urn:x-cast:pair-request"] =
|
||||
cast.framework.system.MessageType.JSON;
|
||||
|
||||
options.disableIdleTimeout = true;
|
||||
|
||||
context.addCustomMessageListener(
|
||||
'urn:x-cast:pair-request',
|
||||
messageReceiveHandler
|
||||
"urn:x-cast:pair-request",
|
||||
messageReceiveHandler,
|
||||
);
|
||||
context.start(options);
|
||||
} catch (e) {
|
||||
addLogLine(e, 'failed to create cast context');
|
||||
addLogLine(e, "failed to create cast context");
|
||||
}
|
||||
setIsCastReady(true);
|
||||
return () => {
|
||||
|
@ -74,17 +74,17 @@ export default function PairingMode() {
|
|||
data: any;
|
||||
}) => {
|
||||
cast.framework.CastReceiverContext.getInstance().sendCustomMessage(
|
||||
'urn:x-cast:pair-request',
|
||||
"urn:x-cast:pair-request",
|
||||
message.senderId,
|
||||
{
|
||||
code: digits.join(''),
|
||||
}
|
||||
code: digits.join(""),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const init = async () => {
|
||||
const data = generateSecureData(6);
|
||||
setDigits(convertDataToDecimalString(data).split(''));
|
||||
setDigits(convertDataToDecimalString(data).split(""));
|
||||
const keypair = await generateKeyPair();
|
||||
setPublicKeyB64(await toB64(keypair.publicKey));
|
||||
setPrivateKeyB64(await toB64(keypair.privateKey));
|
||||
|
@ -105,10 +105,10 @@ export default function PairingMode() {
|
|||
// see if we were acknowledged on the client.
|
||||
// the client will send us the encrypted payload using our public key that we advertised.
|
||||
// then, we can decrypt this and store all the necessary info locally so we can play the collection slideshow.
|
||||
let devicePayload = '';
|
||||
let devicePayload = "";
|
||||
try {
|
||||
const encDastData = await castGateway.getCastData(
|
||||
`${digits.join('')}`
|
||||
`${digits.join("")}`,
|
||||
);
|
||||
if (!encDastData) return;
|
||||
devicePayload = encDastData;
|
||||
|
@ -121,7 +121,7 @@ export default function PairingMode() {
|
|||
const decryptedPayload = await boxSealOpen(
|
||||
devicePayload,
|
||||
publicKeyB64,
|
||||
privateKeyB64
|
||||
privateKeyB64,
|
||||
);
|
||||
|
||||
const decryptedPayloadObj = JSON.parse(atob(decryptedPayload));
|
||||
|
@ -133,8 +133,8 @@ export default function PairingMode() {
|
|||
// hey client, we exist!
|
||||
try {
|
||||
await castGateway.registerDevice(
|
||||
`${digits.join('')}`,
|
||||
publicKeyB64
|
||||
`${digits.join("")}`,
|
||||
publicKeyB64,
|
||||
);
|
||||
setCodePending(false);
|
||||
} catch (e) {
|
||||
|
@ -155,7 +155,7 @@ export default function PairingMode() {
|
|||
const data = await pollForCastData();
|
||||
if (!data) return;
|
||||
storeCastData(data);
|
||||
await router.push('/slideshow');
|
||||
await router.push("/slideshow");
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
|
@ -172,30 +172,34 @@ export default function PairingMode() {
|
|||
<>
|
||||
<div
|
||||
style={{
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
textAlign: "center",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img width={150} src="/images/ente.svg" />
|
||||
<h1
|
||||
style={{
|
||||
fontWeight: 'normal',
|
||||
}}>
|
||||
fontWeight: "normal",
|
||||
}}
|
||||
>
|
||||
Enter this code on <b>ente</b> to pair this TV
|
||||
</h1>
|
||||
<div
|
||||
style={{
|
||||
borderRadius: '10px',
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
borderRadius: "10px",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{codePending ? (
|
||||
<EnteSpinner />
|
||||
) : (
|
||||
|
@ -206,33 +210,36 @@ export default function PairingMode() {
|
|||
</div>
|
||||
<p
|
||||
style={{
|
||||
fontSize: '1.2rem',
|
||||
}}>
|
||||
Visit{' '}
|
||||
fontSize: "1.2rem",
|
||||
}}
|
||||
>
|
||||
Visit{" "}
|
||||
<a
|
||||
style={{
|
||||
textDecoration: 'none',
|
||||
color: '#87CEFA',
|
||||
fontWeight: 'bold',
|
||||
textDecoration: "none",
|
||||
color: "#87CEFA",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
href="https://ente.io/cast"
|
||||
target="_blank">
|
||||
target="_blank"
|
||||
>
|
||||
ente.io/cast
|
||||
</a>{' '}
|
||||
</a>{" "}
|
||||
for help
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
bottom: '20px',
|
||||
right: '20px',
|
||||
backgroundColor: 'white',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: '10px',
|
||||
borderRadius: '10px',
|
||||
}}>
|
||||
position: "fixed",
|
||||
bottom: "20px",
|
||||
right: "20px",
|
||||
backgroundColor: "white",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
padding: "10px",
|
||||
borderRadius: "10px",
|
||||
}}
|
||||
>
|
||||
<img src="/images/help-qrcode.webp" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import { logError } from '@ente/shared/sentry';
|
||||
import PairedSuccessfullyOverlay from 'components/PairedSuccessfullyOverlay';
|
||||
import Theatre from 'components/Theatre';
|
||||
import { FILE_TYPE } from 'constants/file';
|
||||
import { useRouter } from 'next/router';
|
||||
import { createContext, useEffect, useState } from 'react';
|
||||
import { logError } from "@ente/shared/sentry";
|
||||
import PairedSuccessfullyOverlay from "components/PairedSuccessfullyOverlay";
|
||||
import Theatre from "components/Theatre";
|
||||
import { FILE_TYPE } from "constants/file";
|
||||
import { useRouter } from "next/router";
|
||||
import { createContext, useEffect, useState } from "react";
|
||||
import {
|
||||
getCastCollection,
|
||||
getLocalFiles,
|
||||
syncPublicFiles,
|
||||
} from 'services/cast/castService';
|
||||
import { Collection } from 'types/collection';
|
||||
import { EnteFile } from 'types/file';
|
||||
import { getPreviewableImage, isRawFileFromFileName } from 'utils/file';
|
||||
} from "services/cast/castService";
|
||||
import { Collection } from "types/collection";
|
||||
import { EnteFile } from "types/file";
|
||||
import { getPreviewableImage, isRawFileFromFileName } from "utils/file";
|
||||
|
||||
export const SlideshowContext = createContext<{
|
||||
showNextSlide: () => void;
|
||||
|
@ -23,24 +23,24 @@ export default function Slideshow() {
|
|||
const [collectionFiles, setCollectionFiles] = useState<EnteFile[]>([]);
|
||||
|
||||
const [currentFile, setCurrentFile] = useState<EnteFile | undefined>(
|
||||
undefined
|
||||
undefined,
|
||||
);
|
||||
const [nextFile, setNextFile] = useState<EnteFile | undefined>(undefined);
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [castToken, setCastToken] = useState<string>('');
|
||||
const [castToken, setCastToken] = useState<string>("");
|
||||
const [castCollection, setCastCollection] = useState<
|
||||
Collection | undefined
|
||||
>(undefined);
|
||||
|
||||
const syncCastFiles = async (token: string) => {
|
||||
try {
|
||||
const castToken = window.localStorage.getItem('castToken');
|
||||
const castToken = window.localStorage.getItem("castToken");
|
||||
const requestedCollectionKey =
|
||||
window.localStorage.getItem('collectionKey');
|
||||
window.localStorage.getItem("collectionKey");
|
||||
const collection = await getCastCollection(
|
||||
castToken,
|
||||
requestedCollectionKey
|
||||
requestedCollectionKey,
|
||||
);
|
||||
if (
|
||||
castCollection === undefined ||
|
||||
|
@ -50,22 +50,22 @@ export default function Slideshow() {
|
|||
await syncPublicFiles(token, collection, () => {});
|
||||
const files = await getLocalFiles(String(collection.id));
|
||||
setCollectionFiles(
|
||||
files.filter((file) => isFileEligibleForCast(file))
|
||||
files.filter((file) => isFileEligibleForCast(file)),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
logError(e, 'error during sync');
|
||||
router.push('/');
|
||||
logError(e, "error during sync");
|
||||
router.push("/");
|
||||
}
|
||||
};
|
||||
|
||||
const init = async () => {
|
||||
try {
|
||||
const castToken = window.localStorage.getItem('castToken');
|
||||
const castToken = window.localStorage.getItem("castToken");
|
||||
setCastToken(castToken);
|
||||
} catch (e) {
|
||||
logError(e, 'error during sync');
|
||||
router.push('/');
|
||||
logError(e, "error during sync");
|
||||
router.push("/");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -115,7 +115,7 @@ export default function Slideshow() {
|
|||
|
||||
const showNextSlide = () => {
|
||||
const currentIndex = collectionFiles.findIndex(
|
||||
(file) => file.id === currentFile?.id
|
||||
(file) => file.id === currentFile?.id,
|
||||
);
|
||||
|
||||
const nextIndex = (currentIndex + 1) % collectionFiles.length;
|
||||
|
@ -128,7 +128,7 @@ export default function Slideshow() {
|
|||
setNextFile(nextNextFile);
|
||||
};
|
||||
|
||||
const [renderableFileURL, setRenderableFileURL] = useState<string>('');
|
||||
const [renderableFileURL, setRenderableFileURL] = useState<string>("");
|
||||
|
||||
const getRenderableFileURL = async () => {
|
||||
if (!currentFile) return;
|
||||
|
@ -143,7 +143,7 @@ export default function Slideshow() {
|
|||
try {
|
||||
const blob = await getPreviewableImage(
|
||||
currentFile as EnteFile,
|
||||
castToken
|
||||
castToken,
|
||||
);
|
||||
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export enum MS_KEYS {
|
||||
OPT_OUT_OF_CRASH_REPORTS = 'optOutOfCrashReports',
|
||||
SRP_CONFIGURE_IN_PROGRESS = 'srpConfigureInProgress',
|
||||
REDIRECT_URL = 'redirectUrl',
|
||||
OPT_OUT_OF_CRASH_REPORTS = "optOutOfCrashReports",
|
||||
SRP_CONFIGURE_IN_PROGRESS = "srpConfigureInProgress",
|
||||
REDIRECT_URL = "redirectUrl",
|
||||
}
|
||||
|
||||
type StoreType = Map<Partial<MS_KEYS>, any>;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { LimitedCacheStorage } from 'types/cache/index';
|
||||
import { LimitedCacheStorage } from "types/cache/index";
|
||||
// import { ElectronCacheStorage } from 'services/electron/cache';
|
||||
// import { runningInElectron, runningInWorker } from 'utils/common';
|
||||
// import { WorkerElectronCacheStorageService } from 'services/workerElectronCache/service';
|
||||
|
@ -25,7 +25,7 @@ class cacheStorageFactory {
|
|||
export const CacheStorageFactory = new cacheStorageFactory();
|
||||
|
||||
function transformBrowserCacheStorageToLimitedCacheStorage(
|
||||
caches: CacheStorage
|
||||
caches: CacheStorage,
|
||||
): LimitedCacheStorage {
|
||||
return {
|
||||
async open(cacheName) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { logError } from '@ente/shared/sentry';
|
||||
import { CacheStorageFactory } from './cacheStorageFactory';
|
||||
import { logError } from "@ente/shared/sentry";
|
||||
import { CacheStorageFactory } from "./cacheStorageFactory";
|
||||
|
||||
const SecurityError = 'SecurityError';
|
||||
const INSECURE_OPERATION = 'The operation is insecure.';
|
||||
const SecurityError = "SecurityError";
|
||||
const INSECURE_OPERATION = "The operation is insecure.";
|
||||
async function openCache(cacheName: string) {
|
||||
try {
|
||||
return await CacheStorageFactory.getCacheStorage().open(cacheName);
|
||||
|
@ -12,7 +12,7 @@ async function openCache(cacheName: string) {
|
|||
// no-op
|
||||
} else {
|
||||
// log and ignore, we don't want to break the caller flow, when cache is not available
|
||||
logError(e, 'openCache failed');
|
||||
logError(e, "openCache failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ async function deleteCache(cacheName: string) {
|
|||
// no-op
|
||||
} else {
|
||||
// log and ignore, we don't want to break the caller flow, when cache is not available
|
||||
logError(e, 'deleteCache failed');
|
||||
logError(e, "deleteCache failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +1,51 @@
|
|||
import { getEndpoint } from '@ente/shared/network/api';
|
||||
import localForage from '@ente/shared/storage/localForage';
|
||||
import HTTPService from '@ente/shared/network/HTTPService';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
import { CustomError, parseSharingErrorCodes } from '@ente/shared/error';
|
||||
import ComlinkCryptoWorker from '@ente/shared/crypto';
|
||||
import ComlinkCryptoWorker from "@ente/shared/crypto";
|
||||
import { CustomError, parseSharingErrorCodes } from "@ente/shared/error";
|
||||
import HTTPService from "@ente/shared/network/HTTPService";
|
||||
import { getEndpoint } from "@ente/shared/network/api";
|
||||
import { logError } from "@ente/shared/sentry";
|
||||
import localForage from "@ente/shared/storage/localForage";
|
||||
|
||||
import { Collection, CollectionPublicMagicMetadata } from 'types/collection';
|
||||
import { EncryptedEnteFile, EnteFile } from 'types/file';
|
||||
import { decryptFile, mergeMetadata, sortFiles } from 'utils/file';
|
||||
import { Collection, CollectionPublicMagicMetadata } from "types/collection";
|
||||
import { EncryptedEnteFile, EnteFile } from "types/file";
|
||||
import { decryptFile, mergeMetadata, sortFiles } from "utils/file";
|
||||
|
||||
export interface SavedCollectionFiles {
|
||||
collectionLocalID: string;
|
||||
files: EnteFile[];
|
||||
}
|
||||
const ENDPOINT = getEndpoint();
|
||||
const COLLECTION_FILES_TABLE = 'collection-files';
|
||||
const COLLECTIONS_TABLE = 'collections';
|
||||
const COLLECTION_FILES_TABLE = "collection-files";
|
||||
const COLLECTIONS_TABLE = "collections";
|
||||
|
||||
const getLastSyncKey = (collectionUID: string) => `${collectionUID}-time`;
|
||||
|
||||
export const getLocalFiles = async (
|
||||
collectionUID: string
|
||||
collectionUID: string,
|
||||
): Promise<EnteFile[]> => {
|
||||
const localSavedcollectionFiles =
|
||||
(await localForage.getItem<SavedCollectionFiles[]>(
|
||||
COLLECTION_FILES_TABLE
|
||||
COLLECTION_FILES_TABLE,
|
||||
)) || [];
|
||||
const matchedCollection = localSavedcollectionFiles.find(
|
||||
(item) => item.collectionLocalID === collectionUID
|
||||
(item) => item.collectionLocalID === collectionUID,
|
||||
);
|
||||
return matchedCollection?.files || [];
|
||||
};
|
||||
|
||||
const savecollectionFiles = async (
|
||||
collectionUID: string,
|
||||
files: EnteFile[]
|
||||
files: EnteFile[],
|
||||
) => {
|
||||
const collectionFiles =
|
||||
(await localForage.getItem<SavedCollectionFiles[]>(
|
||||
COLLECTION_FILES_TABLE
|
||||
COLLECTION_FILES_TABLE,
|
||||
)) || [];
|
||||
await localForage.setItem(
|
||||
COLLECTION_FILES_TABLE,
|
||||
dedupeCollectionFiles([
|
||||
{ collectionLocalID: collectionUID, files },
|
||||
...collectionFiles,
|
||||
])
|
||||
]),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -55,7 +55,7 @@ export const getLocalCollections = async (collectionKey: string) => {
|
|||
const collection =
|
||||
localCollections.find(
|
||||
(localSavedPublicCollection) =>
|
||||
localSavedPublicCollection.key === collectionKey
|
||||
localSavedPublicCollection.key === collectionKey,
|
||||
) || null;
|
||||
return collection;
|
||||
};
|
||||
|
@ -65,7 +65,7 @@ const saveCollection = async (collection: Collection) => {
|
|||
(await localForage.getItem<Collection[]>(COLLECTIONS_TABLE)) ?? [];
|
||||
await localForage.setItem(
|
||||
COLLECTIONS_TABLE,
|
||||
dedupeCollections([collection, ...collections])
|
||||
dedupeCollections([collection, ...collections]),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -105,7 +105,7 @@ const updateSyncTime = async (collectionUID: string, time: number) =>
|
|||
export const syncPublicFiles = async (
|
||||
token: string,
|
||||
collection: Collection,
|
||||
setPublicFiles: (files: EnteFile[]) => void
|
||||
setPublicFiles: (files: EnteFile[]) => void,
|
||||
) => {
|
||||
try {
|
||||
let files: EnteFile[] = [];
|
||||
|
@ -123,7 +123,7 @@ export const syncPublicFiles = async (
|
|||
collection,
|
||||
lastSyncTime,
|
||||
files,
|
||||
setPublicFiles
|
||||
setPublicFiles,
|
||||
);
|
||||
|
||||
files = [...files, ...fetchedFiles];
|
||||
|
@ -150,14 +150,14 @@ export const syncPublicFiles = async (
|
|||
setPublicFiles([...sortFiles(mergeMetadata(files), sortAsc)]);
|
||||
} catch (e) {
|
||||
const parsedError = parseSharingErrorCodes(e);
|
||||
logError(e, 'failed to sync shared collection files');
|
||||
logError(e, "failed to sync shared collection files");
|
||||
if (parsedError.message === CustomError.TOKEN_EXPIRED) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return [...sortFiles(mergeMetadata(files), sortAsc)];
|
||||
} catch (e) {
|
||||
logError(e, 'failed to get local or sync shared collection files');
|
||||
logError(e, "failed to get local or sync shared collection files");
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
@ -167,7 +167,7 @@ const fetchFiles = async (
|
|||
collection: Collection,
|
||||
sinceTime: number,
|
||||
files: EnteFile[],
|
||||
setPublicFiles: (files: EnteFile[]) => void
|
||||
setPublicFiles: (files: EnteFile[]) => void,
|
||||
): Promise<EnteFile[]> => {
|
||||
try {
|
||||
let decryptedFiles: EnteFile[] = [];
|
||||
|
@ -184,9 +184,9 @@ const fetchFiles = async (
|
|||
sinceTime: time,
|
||||
},
|
||||
{
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Cast-Access-Token': castToken,
|
||||
}
|
||||
"Cache-Control": "no-cache",
|
||||
"X-Cast-Access-Token": castToken,
|
||||
},
|
||||
);
|
||||
decryptedFiles = [
|
||||
...decryptedFiles,
|
||||
|
@ -197,7 +197,7 @@ const fetchFiles = async (
|
|||
} else {
|
||||
return file;
|
||||
}
|
||||
}) as Promise<EnteFile>[]
|
||||
}) as Promise<EnteFile>[],
|
||||
)),
|
||||
];
|
||||
|
||||
|
@ -208,28 +208,28 @@ const fetchFiles = async (
|
|||
sortFiles(
|
||||
mergeMetadata(
|
||||
[...(files || []), ...decryptedFiles].filter(
|
||||
(item) => !item.isDeleted
|
||||
)
|
||||
(item) => !item.isDeleted,
|
||||
),
|
||||
),
|
||||
sortAsc
|
||||
)
|
||||
sortAsc,
|
||||
),
|
||||
);
|
||||
} while (resp.data.hasMore);
|
||||
return decryptedFiles;
|
||||
} catch (e) {
|
||||
logError(e, 'Get cast files failed');
|
||||
logError(e, "Get cast files failed");
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const getCastCollection = async (
|
||||
castToken: string,
|
||||
collectionKey: string
|
||||
collectionKey: string,
|
||||
): Promise<Collection> => {
|
||||
try {
|
||||
const resp = await HTTPService.get(`${ENDPOINT}/cast/info`, null, {
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Cast-Access-Token': castToken,
|
||||
"Cache-Control": "no-cache",
|
||||
"X-Cast-Access-Token": castToken,
|
||||
});
|
||||
const fetchedCollection = resp.data.collection;
|
||||
|
||||
|
@ -240,7 +240,7 @@ export const getCastCollection = async (
|
|||
(await cryptoWorker.decryptToUTF8(
|
||||
fetchedCollection.encryptedName,
|
||||
fetchedCollection.nameDecryptionNonce,
|
||||
collectionKey
|
||||
collectionKey,
|
||||
)));
|
||||
|
||||
let collectionPublicMagicMetadata: CollectionPublicMagicMetadata;
|
||||
|
@ -250,7 +250,7 @@ export const getCastCollection = async (
|
|||
data: await cryptoWorker.decryptMetadata(
|
||||
fetchedCollection.pubMagicMetadata.data,
|
||||
fetchedCollection.pubMagicMetadata.header,
|
||||
collectionKey
|
||||
collectionKey,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
@ -264,20 +264,20 @@ export const getCastCollection = async (
|
|||
await saveCollection(collection);
|
||||
return collection;
|
||||
} catch (e) {
|
||||
logError(e, 'failed to get cast collection');
|
||||
logError(e, "failed to get cast collection");
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const removeCollection = async (
|
||||
collectionUID: string,
|
||||
collectionKey: string
|
||||
collectionKey: string,
|
||||
) => {
|
||||
const collections =
|
||||
(await localForage.getItem<Collection[]>(COLLECTIONS_TABLE)) || [];
|
||||
await localForage.setItem(
|
||||
COLLECTIONS_TABLE,
|
||||
collections.filter((collection) => collection.key !== collectionKey)
|
||||
collections.filter((collection) => collection.key !== collectionKey),
|
||||
);
|
||||
await removeCollectionFiles(collectionUID);
|
||||
};
|
||||
|
@ -286,14 +286,14 @@ export const removeCollectionFiles = async (collectionUID: string) => {
|
|||
await localForage.removeItem(getLastSyncKey(collectionUID));
|
||||
const collectionFiles =
|
||||
(await localForage.getItem<SavedCollectionFiles[]>(
|
||||
COLLECTION_FILES_TABLE
|
||||
COLLECTION_FILES_TABLE,
|
||||
)) ?? [];
|
||||
await localForage.setItem(
|
||||
COLLECTION_FILES_TABLE,
|
||||
collectionFiles.filter(
|
||||
(collectionFiles) =>
|
||||
collectionFiles.collectionLocalID !== collectionUID
|
||||
)
|
||||
collectionFiles.collectionLocalID !== collectionUID,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
import { EnteFile } from "types/file";
|
||||
import {
|
||||
createTypedObjectURL,
|
||||
generateStreamFromArrayBuffer,
|
||||
getRenderableFileURL,
|
||||
createTypedObjectURL,
|
||||
} from 'utils/file';
|
||||
import { EnteFile } from 'types/file';
|
||||
} from "utils/file";
|
||||
|
||||
import { FILE_TYPE } from 'constants/file';
|
||||
import { CustomError } from '@ente/shared/error';
|
||||
import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
|
||||
import { CACHES } from 'constants/cache';
|
||||
import { CacheStorageService } from './cache/cacheStorageService';
|
||||
import { LimitedCache } from 'types/cache';
|
||||
import { getCastFileURL, getCastThumbnailURL } from '@ente/shared/network/api';
|
||||
import HTTPService from '@ente/shared/network/HTTPService';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
import { CustomError } from "@ente/shared/error";
|
||||
import HTTPService from "@ente/shared/network/HTTPService";
|
||||
import { getCastFileURL, getCastThumbnailURL } from "@ente/shared/network/api";
|
||||
import { logError } from "@ente/shared/sentry";
|
||||
import { CACHES } from "constants/cache";
|
||||
import { FILE_TYPE } from "constants/file";
|
||||
import { LimitedCache } from "types/cache";
|
||||
import ComlinkCryptoWorker from "utils/comlink/ComlinkCryptoWorker";
|
||||
import { CacheStorageService } from "./cache/cacheStorageService";
|
||||
|
||||
class CastDownloadManager {
|
||||
private fileObjectURLPromise = new Map<
|
||||
|
@ -33,7 +33,7 @@ class CastDownloadManager {
|
|||
private async getThumbnailCache() {
|
||||
try {
|
||||
const thumbnailCache = await CacheStorageService.open(
|
||||
CACHES.THUMBS
|
||||
CACHES.THUMBS,
|
||||
);
|
||||
return thumbnailCache;
|
||||
} catch (e) {
|
||||
|
@ -44,14 +44,14 @@ class CastDownloadManager {
|
|||
|
||||
public async getCachedThumbnail(
|
||||
file: EnteFile,
|
||||
thumbnailCache?: LimitedCache
|
||||
thumbnailCache?: LimitedCache,
|
||||
) {
|
||||
try {
|
||||
if (!thumbnailCache) {
|
||||
thumbnailCache = await this.getThumbnailCache();
|
||||
}
|
||||
const cacheResp: Response = await thumbnailCache?.match(
|
||||
file.id.toString()
|
||||
file.id.toString(),
|
||||
);
|
||||
|
||||
if (cacheResp) {
|
||||
|
@ -59,7 +59,7 @@ class CastDownloadManager {
|
|||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
logError(e, 'failed to get cached thumbnail');
|
||||
logError(e, "failed to get cached thumbnail");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ class CastDownloadManager {
|
|||
const thumbnailCache = await this.getThumbnailCache();
|
||||
const cachedThumb = await this.getCachedThumbnail(
|
||||
file,
|
||||
thumbnailCache
|
||||
thumbnailCache,
|
||||
);
|
||||
if (cachedThumb) {
|
||||
return cachedThumb;
|
||||
|
@ -82,7 +82,7 @@ class CastDownloadManager {
|
|||
try {
|
||||
await thumbnailCache?.put(
|
||||
file.id.toString(),
|
||||
new Response(thumbBlob)
|
||||
new Response(thumbBlob),
|
||||
);
|
||||
} catch (e) {
|
||||
// TODO: handle storage full exception.
|
||||
|
@ -95,7 +95,7 @@ class CastDownloadManager {
|
|||
return await this.thumbnailObjectURLPromise.get(file.id);
|
||||
} catch (e) {
|
||||
this.thumbnailObjectURLPromise.delete(file.id);
|
||||
logError(e, 'get castDownloadManager preview Failed');
|
||||
logError(e, "get castDownloadManager preview Failed");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -105,18 +105,18 @@ class CastDownloadManager {
|
|||
getCastThumbnailURL(file.id),
|
||||
null,
|
||||
{
|
||||
'X-Cast-Access-Token': castToken,
|
||||
"X-Cast-Access-Token": castToken,
|
||||
},
|
||||
{ responseType: 'arraybuffer' }
|
||||
{ responseType: "arraybuffer" },
|
||||
);
|
||||
if (typeof resp.data === 'undefined') {
|
||||
if (typeof resp.data === "undefined") {
|
||||
throw Error(CustomError.REQUEST_FAILED);
|
||||
}
|
||||
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
|
||||
const decrypted = await cryptoWorker.decryptThumbnail(
|
||||
new Uint8Array(resp.data),
|
||||
await cryptoWorker.fromB64(file.thumbnail.decryptionHeader),
|
||||
file.key
|
||||
file.key,
|
||||
);
|
||||
return decrypted;
|
||||
};
|
||||
|
@ -132,7 +132,7 @@ class CastDownloadManager {
|
|||
} else {
|
||||
const fileURL = await createTypedObjectURL(
|
||||
fileBlob,
|
||||
file.metadata.title
|
||||
file.metadata.title,
|
||||
);
|
||||
return { converted: [fileURL], original: [fileURL] };
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ class CastDownloadManager {
|
|||
return fileURLs;
|
||||
} catch (e) {
|
||||
this.fileObjectURLPromise.delete(fileKey);
|
||||
logError(e, 'castDownloadManager failed to get file');
|
||||
logError(e, "castDownloadManager failed to get file");
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
@ -166,40 +166,40 @@ class CastDownloadManager {
|
|||
getCastFileURL(file.id),
|
||||
null,
|
||||
{
|
||||
'X-Cast-Access-Token': castToken,
|
||||
"X-Cast-Access-Token": castToken,
|
||||
},
|
||||
{ responseType: 'arraybuffer' }
|
||||
{ responseType: "arraybuffer" },
|
||||
);
|
||||
if (typeof resp.data === 'undefined') {
|
||||
if (typeof resp.data === "undefined") {
|
||||
throw Error(CustomError.REQUEST_FAILED);
|
||||
}
|
||||
const decrypted = await cryptoWorker.decryptFile(
|
||||
new Uint8Array(resp.data),
|
||||
await cryptoWorker.fromB64(file.file.decryptionHeader),
|
||||
file.key
|
||||
file.key,
|
||||
);
|
||||
return generateStreamFromArrayBuffer(decrypted);
|
||||
}
|
||||
const resp = await fetch(getCastFileURL(file.id), {
|
||||
headers: {
|
||||
'X-Cast-Access-Token': castToken,
|
||||
"X-Cast-Access-Token": castToken,
|
||||
},
|
||||
});
|
||||
const reader = resp.body.getReader();
|
||||
|
||||
const contentLength = +resp.headers.get('Content-Length');
|
||||
const contentLength = +resp.headers.get("Content-Length");
|
||||
let downloadedBytes = 0;
|
||||
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
const decryptionHeader = await cryptoWorker.fromB64(
|
||||
file.file.decryptionHeader
|
||||
file.file.decryptionHeader,
|
||||
);
|
||||
const fileKey = await cryptoWorker.fromB64(file.key);
|
||||
const { pullState, decryptionChunkSize } =
|
||||
await cryptoWorker.initChunkDecryption(
|
||||
decryptionHeader,
|
||||
fileKey
|
||||
fileKey,
|
||||
);
|
||||
let data = new Uint8Array();
|
||||
// The following function handles each data chunk
|
||||
|
@ -214,19 +214,19 @@ class CastDownloadManager {
|
|||
total: contentLength,
|
||||
});
|
||||
const buffer = new Uint8Array(
|
||||
data.byteLength + value.byteLength
|
||||
data.byteLength + value.byteLength,
|
||||
);
|
||||
buffer.set(new Uint8Array(data), 0);
|
||||
buffer.set(new Uint8Array(value), data.byteLength);
|
||||
if (buffer.length > decryptionChunkSize) {
|
||||
const fileData = buffer.slice(
|
||||
0,
|
||||
decryptionChunkSize
|
||||
decryptionChunkSize,
|
||||
);
|
||||
const { decryptedData } =
|
||||
await cryptoWorker.decryptFileChunk(
|
||||
fileData,
|
||||
pullState
|
||||
pullState,
|
||||
);
|
||||
controller.enqueue(decryptedData);
|
||||
data = buffer.slice(decryptionChunkSize);
|
||||
|
@ -239,7 +239,7 @@ class CastDownloadManager {
|
|||
const { decryptedData } =
|
||||
await cryptoWorker.decryptFileChunk(
|
||||
data,
|
||||
pullState
|
||||
pullState,
|
||||
);
|
||||
controller.enqueue(decryptedData);
|
||||
data = null;
|
||||
|
@ -262,7 +262,7 @@ class CastDownloadManager {
|
|||
} else {
|
||||
this.fileDownloadProgress.set(
|
||||
fileID,
|
||||
Math.round((event.loaded * 100) / event.total)
|
||||
Math.round((event.loaded * 100) / event.total),
|
||||
);
|
||||
}
|
||||
this.progressUpdater(new Map(this.fileDownloadProgress));
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { EventEmitter } from 'eventemitter3';
|
||||
import { EventEmitter } from "eventemitter3";
|
||||
|
||||
// When registering event handlers,
|
||||
// handle errors to avoid unhandled rejection or propagation to emit call
|
||||
|
||||
export enum Events {
|
||||
LOGOUT = 'logout',
|
||||
FILE_UPLOADED = 'fileUploaded',
|
||||
LOCAL_FILES_UPDATED = 'localFilesUpdated',
|
||||
LOGOUT = "logout",
|
||||
FILE_UPLOADED = "fileUploaded",
|
||||
LOCAL_FILES_UPDATED = "localFilesUpdated",
|
||||
}
|
||||
|
||||
export const eventBus = new EventEmitter<Events>();
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
// import isElectron from 'is-electron';
|
||||
// import { ElectronFFmpeg } from 'services/electron/ffmpeg';
|
||||
import { ElectronFile } from 'types/upload';
|
||||
import ComlinkFFmpegWorker from 'utils/comlink/ComlinkFFmpegWorker';
|
||||
import { ElectronFile } from "types/upload";
|
||||
import ComlinkFFmpegWorker from "utils/comlink/ComlinkFFmpegWorker";
|
||||
|
||||
export interface IFFmpeg {
|
||||
run: (
|
||||
cmd: string[],
|
||||
inputFile: File | ElectronFile,
|
||||
outputFilename: string,
|
||||
dontTimeout?: boolean
|
||||
dontTimeout?: boolean,
|
||||
) => Promise<File | ElectronFile>;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { logError } from "@ente/shared/sentry";
|
||||
import {
|
||||
FFMPEG_PLACEHOLDER,
|
||||
INPUT_PATH_PLACEHOLDER,
|
||||
OUTPUT_PATH_PLACEHOLDER,
|
||||
} from 'constants/ffmpeg';
|
||||
import { ElectronFile } from 'types/upload';
|
||||
import ffmpegFactory from './ffmpegFactory';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
} from "constants/ffmpeg";
|
||||
import { ElectronFile } from "types/upload";
|
||||
import ffmpegFactory from "./ffmpegFactory";
|
||||
|
||||
export async function convertToMP4(file: File | ElectronFile) {
|
||||
try {
|
||||
|
@ -13,18 +13,18 @@ export async function convertToMP4(file: File | ElectronFile) {
|
|||
return await ffmpegClient.run(
|
||||
[
|
||||
FFMPEG_PLACEHOLDER,
|
||||
'-i',
|
||||
"-i",
|
||||
INPUT_PATH_PLACEHOLDER,
|
||||
'-preset',
|
||||
'ultrafast',
|
||||
"-preset",
|
||||
"ultrafast",
|
||||
OUTPUT_PATH_PLACEHOLDER,
|
||||
],
|
||||
file,
|
||||
'output.mp4',
|
||||
true
|
||||
"output.mp4",
|
||||
true,
|
||||
);
|
||||
} catch (e) {
|
||||
logError(e, 'ffmpeg convertToMP4 failed');
|
||||
logError(e, "ffmpeg convertToMP4 failed");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { logError } from '@ente/shared/sentry';
|
||||
import WasmHEICConverterService from './wasmHeicConverter/wasmHEICConverterService';
|
||||
import { logError } from "@ente/shared/sentry";
|
||||
import WasmHEICConverterService from "./wasmHeicConverter/wasmHEICConverterService";
|
||||
|
||||
class HeicConversionService {
|
||||
async convert(heicFileData: Blob): Promise<Blob> {
|
||||
try {
|
||||
return await WasmHEICConverterService.convert(heicFileData);
|
||||
} catch (e) {
|
||||
logError(e, 'failed to convert heic file');
|
||||
logError(e, "failed to convert heic file");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import JSZip from 'jszip';
|
||||
import { EnteFile } from 'types/file';
|
||||
import JSZip from "jszip";
|
||||
import { EnteFile } from "types/file";
|
||||
import {
|
||||
getFileExtensionWithDot,
|
||||
getFileNameWithoutExtension,
|
||||
} from 'utils/file';
|
||||
} from "utils/file";
|
||||
|
||||
class LivePhoto {
|
||||
image: Uint8Array;
|
||||
|
@ -18,14 +18,14 @@ export const decodeLivePhoto = async (file: EnteFile, zipBlob: Blob) => {
|
|||
|
||||
const livePhoto = new LivePhoto();
|
||||
for (const zipFilename in zip.files) {
|
||||
if (zipFilename.startsWith('image')) {
|
||||
if (zipFilename.startsWith("image")) {
|
||||
livePhoto.imageNameTitle =
|
||||
originalName + getFileExtensionWithDot(zipFilename);
|
||||
livePhoto.image = await zip.files[zipFilename].async('uint8array');
|
||||
} else if (zipFilename.startsWith('video')) {
|
||||
livePhoto.image = await zip.files[zipFilename].async("uint8array");
|
||||
} else if (zipFilename.startsWith("video")) {
|
||||
livePhoto.videoNameTitle =
|
||||
originalName + getFileExtensionWithDot(zipFilename);
|
||||
livePhoto.video = await zip.files[zipFilename].async('uint8array');
|
||||
livePhoto.video = await zip.files[zipFilename].async("uint8array");
|
||||
}
|
||||
}
|
||||
return livePhoto;
|
||||
|
@ -34,12 +34,12 @@ export const decodeLivePhoto = async (file: EnteFile, zipBlob: Blob) => {
|
|||
export const encodeLivePhoto = async (livePhoto: LivePhoto) => {
|
||||
const zip = new JSZip();
|
||||
zip.file(
|
||||
'image' + getFileExtensionWithDot(livePhoto.imageNameTitle),
|
||||
livePhoto.image
|
||||
"image" + getFileExtensionWithDot(livePhoto.imageNameTitle),
|
||||
livePhoto.image,
|
||||
);
|
||||
zip.file(
|
||||
'video' + getFileExtensionWithDot(livePhoto.videoNameTitle),
|
||||
livePhoto.video
|
||||
"video" + getFileExtensionWithDot(livePhoto.videoNameTitle),
|
||||
livePhoto.video,
|
||||
);
|
||||
return await zip.generateAsync({ type: 'uint8array' });
|
||||
return await zip.generateAsync({ type: "uint8array" });
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { CustomError } from '@ente/shared/error';
|
||||
import { CustomError } from "@ente/shared/error";
|
||||
|
||||
interface RequestQueueItem {
|
||||
request: (canceller?: RequestCanceller) => Promise<any>;
|
||||
|
@ -28,11 +28,11 @@ export default class QueueProcessor<T> {
|
|||
|
||||
constructor(
|
||||
private maxParallelProcesses: number,
|
||||
private processingStrategy = PROCESSING_STRATEGY.FIFO
|
||||
private processingStrategy = PROCESSING_STRATEGY.FIFO,
|
||||
) {}
|
||||
|
||||
public queueUpRequest(
|
||||
request: (canceller?: RequestCanceller) => Promise<T>
|
||||
request: (canceller?: RequestCanceller) => Promise<T>,
|
||||
) {
|
||||
const isCanceled: CancellationStatus = { status: false };
|
||||
const canceller: RequestCanceller = {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { logError } from '@ente/shared/sentry';
|
||||
import { convertBytesToHumanReadable } from '@ente/shared/utils/size';
|
||||
import { ElectronFile } from 'types/upload';
|
||||
import { logError } from "@ente/shared/sentry";
|
||||
import { convertBytesToHumanReadable } from "@ente/shared/utils/size";
|
||||
import { ElectronFile } from "types/upload";
|
||||
|
||||
export async function getUint8ArrayView(
|
||||
file: Blob | ElectronFile
|
||||
file: Blob | ElectronFile,
|
||||
): Promise<Uint8Array> {
|
||||
try {
|
||||
return new Uint8Array(await file.arrayBuffer());
|
||||
} catch (e) {
|
||||
logError(e, 'reading file blob failed', {
|
||||
logError(e, "reading file blob failed", {
|
||||
fileSize: convertBytesToHumanReadable(file.size),
|
||||
});
|
||||
throw e;
|
||||
|
@ -37,7 +37,7 @@ export function getFileStream(file: File, chunkSize: number) {
|
|||
|
||||
export async function getElectronFileStream(
|
||||
file: ElectronFile,
|
||||
chunkSize: number
|
||||
chunkSize: number,
|
||||
) {
|
||||
const chunkCount = Math.ceil(file.size / chunkSize);
|
||||
return {
|
||||
|
@ -61,29 +61,29 @@ async function* fileChunkReaderMaker(file: File, chunkSize: number) {
|
|||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async function getUint8ArrayViewOld(
|
||||
reader: FileReader,
|
||||
file: Blob
|
||||
file: Blob,
|
||||
): Promise<Uint8Array> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
reader.onabort = () =>
|
||||
reject(
|
||||
Error(
|
||||
`file reading was aborted, file size= ${convertBytesToHumanReadable(
|
||||
file.size
|
||||
)}`
|
||||
)
|
||||
file.size,
|
||||
)}`,
|
||||
),
|
||||
);
|
||||
reader.onerror = () =>
|
||||
reject(
|
||||
Error(
|
||||
`file reading has failed, file size= ${convertBytesToHumanReadable(
|
||||
file.size
|
||||
)} , reason= ${reader.error}`
|
||||
)
|
||||
file.size,
|
||||
)} , reason= ${reader.error}`,
|
||||
),
|
||||
);
|
||||
reader.onload = () => {
|
||||
// Do whatever you want with the file contents
|
||||
const result =
|
||||
typeof reader.result === 'string'
|
||||
typeof reader.result === "string"
|
||||
? new TextEncoder().encode(reader.result)
|
||||
: new Uint8Array(reader.result);
|
||||
resolve(result);
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
import { FILE_TYPE } from 'constants/file';
|
||||
import { ElectronFile, FileTypeInfo } from 'types/upload';
|
||||
import { CustomError } from "@ente/shared/error";
|
||||
import { logError } from "@ente/shared/sentry";
|
||||
import { convertBytesToHumanReadable } from "@ente/shared/utils/size";
|
||||
import { FILE_TYPE } from "constants/file";
|
||||
import {
|
||||
WHITELISTED_FILE_FORMATS,
|
||||
KNOWN_NON_MEDIA_FORMATS,
|
||||
} from 'constants/upload';
|
||||
import { CustomError } from '@ente/shared/error';
|
||||
import { getFileExtension } from 'utils/file';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
import { getUint8ArrayView } from './readerService';
|
||||
import FileType, { FileTypeResult } from 'file-type';
|
||||
import { convertBytesToHumanReadable } from '@ente/shared/utils/size';
|
||||
WHITELISTED_FILE_FORMATS,
|
||||
} from "constants/upload";
|
||||
import FileType, { FileTypeResult } from "file-type";
|
||||
import { ElectronFile, FileTypeInfo } from "types/upload";
|
||||
import { getFileExtension } from "utils/file";
|
||||
import { getUint8ArrayView } from "./readerService";
|
||||
|
||||
function getFileSize(file: File | ElectronFile) {
|
||||
return file.size;
|
||||
}
|
||||
|
||||
const TYPE_VIDEO = 'video';
|
||||
const TYPE_IMAGE = 'image';
|
||||
const TYPE_VIDEO = "video";
|
||||
const TYPE_IMAGE = "image";
|
||||
const CHUNK_SIZE_FOR_TYPE_DETECTION = 4100;
|
||||
|
||||
export async function getFileType(
|
||||
receivedFile: File | ElectronFile
|
||||
receivedFile: File | ElectronFile,
|
||||
): Promise<FileTypeInfo> {
|
||||
try {
|
||||
let fileType: FILE_TYPE;
|
||||
|
@ -32,7 +32,7 @@ export async function getFileType(
|
|||
typeResult = await extractElectronFileType(receivedFile);
|
||||
}
|
||||
|
||||
const mimTypeParts: string[] = typeResult.mime?.split('/');
|
||||
const mimTypeParts: string[] = typeResult.mime?.split("/");
|
||||
|
||||
if (mimTypeParts?.length !== 2) {
|
||||
throw Error(CustomError.INVALID_MIME_TYPE(typeResult.mime));
|
||||
|
@ -56,7 +56,7 @@ export async function getFileType(
|
|||
const fileFormat = getFileExtension(receivedFile.name);
|
||||
const fileSize = convertBytesToHumanReadable(getFileSize(receivedFile));
|
||||
const whiteListedFormat = WHITELISTED_FILE_FORMATS.find(
|
||||
(a) => a.exactType === fileFormat
|
||||
(a) => a.exactType === fileFormat,
|
||||
);
|
||||
if (whiteListedFormat) {
|
||||
return whiteListedFormat;
|
||||
|
@ -65,13 +65,13 @@ export async function getFileType(
|
|||
throw Error(CustomError.UNSUPPORTED_FILE_FORMAT);
|
||||
}
|
||||
if (e.message === CustomError.NON_MEDIA_FILE) {
|
||||
logError(e, 'unsupported file format', {
|
||||
logError(e, "unsupported file format", {
|
||||
fileFormat,
|
||||
fileSize,
|
||||
});
|
||||
throw Error(CustomError.UNSUPPORTED_FILE_FORMAT);
|
||||
}
|
||||
logError(e, 'type detection failed', {
|
||||
logError(e, "type detection failed", {
|
||||
fileFormat,
|
||||
fileSize,
|
||||
});
|
||||
|
@ -96,11 +96,11 @@ async function extractElectronFileType(file: ElectronFile) {
|
|||
async function getFileTypeFromBuffer(buffer: Uint8Array) {
|
||||
const result = await FileType.fromBuffer(buffer);
|
||||
if (!result?.mime) {
|
||||
let logableInfo = '';
|
||||
let logableInfo = "";
|
||||
try {
|
||||
logableInfo = `result: ${JSON.stringify(result)}`;
|
||||
} catch (e) {
|
||||
logableInfo = 'failed to stringify result';
|
||||
logableInfo = "failed to stringify result";
|
||||
}
|
||||
throw Error(`mimetype missing from file type result - ${logableInfo}`);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { promiseWithTimeout } from '@ente/shared/promise';
|
||||
import { createFFmpeg, FFmpeg } from 'ffmpeg-wasm';
|
||||
import QueueProcessor from 'services/queueProcessor';
|
||||
import { getUint8ArrayView } from 'services/readerService';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
import { generateTempName } from 'utils/temp';
|
||||
import { addLogLine } from '@ente/shared/logging';
|
||||
import { addLogLine } from "@ente/shared/logging";
|
||||
import { promiseWithTimeout } from "@ente/shared/promise";
|
||||
import { logError } from "@ente/shared/sentry";
|
||||
import { createFFmpeg, FFmpeg } from "ffmpeg-wasm";
|
||||
import QueueProcessor from "services/queueProcessor";
|
||||
import { getUint8ArrayView } from "services/readerService";
|
||||
import { generateTempName } from "utils/temp";
|
||||
|
||||
const INPUT_PATH_PLACEHOLDER = 'INPUT';
|
||||
const FFMPEG_PLACEHOLDER = 'FFMPEG';
|
||||
const OUTPUT_PATH_PLACEHOLDER = 'OUTPUT';
|
||||
const INPUT_PATH_PLACEHOLDER = "INPUT";
|
||||
const FFMPEG_PLACEHOLDER = "FFMPEG";
|
||||
const OUTPUT_PATH_PLACEHOLDER = "OUTPUT";
|
||||
|
||||
const FFMPEG_EXECUTION_WAIT_TIME = 30 * 1000;
|
||||
|
||||
|
@ -19,7 +19,7 @@ export class WasmFFmpeg {
|
|||
|
||||
constructor() {
|
||||
this.ffmpeg = createFFmpeg({
|
||||
corePath: '/js/ffmpeg/ffmpeg-core.js',
|
||||
corePath: "/js/ffmpeg/ffmpeg-core.js",
|
||||
mt: false,
|
||||
});
|
||||
|
||||
|
@ -36,7 +36,7 @@ export class WasmFFmpeg {
|
|||
cmd: string[],
|
||||
inputFile: File,
|
||||
outputFileName: string,
|
||||
dontTimeout = false
|
||||
dontTimeout = false,
|
||||
) {
|
||||
const response = this.ffmpegTaskQueue.queueUpRequest(() => {
|
||||
if (dontTimeout) {
|
||||
|
@ -44,14 +44,14 @@ export class WasmFFmpeg {
|
|||
} else {
|
||||
return promiseWithTimeout<File>(
|
||||
this.execute(cmd, inputFile, outputFileName),
|
||||
FFMPEG_EXECUTION_WAIT_TIME
|
||||
FFMPEG_EXECUTION_WAIT_TIME,
|
||||
);
|
||||
}
|
||||
});
|
||||
try {
|
||||
return await response.promise;
|
||||
} catch (e) {
|
||||
logError(e, 'ffmpeg run failed');
|
||||
logError(e, "ffmpeg run failed");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -59,25 +59,25 @@ export class WasmFFmpeg {
|
|||
private async execute(
|
||||
cmd: string[],
|
||||
inputFile: File,
|
||||
outputFileName: string
|
||||
outputFileName: string,
|
||||
) {
|
||||
let tempInputFilePath: string;
|
||||
let tempOutputFilePath: string;
|
||||
try {
|
||||
await this.ready;
|
||||
const extension = getFileExtension(inputFile.name);
|
||||
const tempNameSuffix = extension ? `input.${extension}` : 'input';
|
||||
const tempNameSuffix = extension ? `input.${extension}` : "input";
|
||||
tempInputFilePath = `${generateTempName(10, tempNameSuffix)}`;
|
||||
this.ffmpeg.FS(
|
||||
'writeFile',
|
||||
"writeFile",
|
||||
tempInputFilePath,
|
||||
await getUint8ArrayView(inputFile)
|
||||
await getUint8ArrayView(inputFile),
|
||||
);
|
||||
tempOutputFilePath = `${generateTempName(10, outputFileName)}`;
|
||||
|
||||
cmd = cmd.map((cmdPart) => {
|
||||
if (cmdPart === FFMPEG_PLACEHOLDER) {
|
||||
return '';
|
||||
return "";
|
||||
} else if (cmdPart === INPUT_PATH_PLACEHOLDER) {
|
||||
return tempInputFilePath;
|
||||
} else if (cmdPart === OUTPUT_PATH_PLACEHOLDER) {
|
||||
|
@ -89,26 +89,26 @@ export class WasmFFmpeg {
|
|||
addLogLine(`${cmd}`);
|
||||
await this.ffmpeg.run(...cmd);
|
||||
return new File(
|
||||
[this.ffmpeg.FS('readFile', tempOutputFilePath)],
|
||||
outputFileName
|
||||
[this.ffmpeg.FS("readFile", tempOutputFilePath)],
|
||||
outputFileName,
|
||||
);
|
||||
} finally {
|
||||
try {
|
||||
this.ffmpeg.FS('unlink', tempInputFilePath);
|
||||
this.ffmpeg.FS("unlink", tempInputFilePath);
|
||||
} catch (e) {
|
||||
logError(e, 'unlink input file failed');
|
||||
logError(e, "unlink input file failed");
|
||||
}
|
||||
try {
|
||||
this.ffmpeg.FS('unlink', tempOutputFilePath);
|
||||
this.ffmpeg.FS("unlink", tempOutputFilePath);
|
||||
} catch (e) {
|
||||
logError(e, 'unlink output file failed');
|
||||
logError(e, "unlink output file failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getFileExtension(filename: string) {
|
||||
const lastDotPosition = filename.lastIndexOf('.');
|
||||
const lastDotPosition = filename.lastIndexOf(".");
|
||||
if (lastDotPosition === -1) return null;
|
||||
else {
|
||||
return filename.slice(lastDotPosition + 1);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import * as HeicConvert from 'heic-convert';
|
||||
import { getUint8ArrayView } from 'services/readerService';
|
||||
import * as HeicConvert from "heic-convert";
|
||||
import { getUint8ArrayView } from "services/readerService";
|
||||
|
||||
export async function convertHEIC(
|
||||
fileBlob: Blob,
|
||||
format: string
|
||||
format: string,
|
||||
): Promise<Blob> {
|
||||
const filedata = await getUint8ArrayView(fileBlob);
|
||||
const result = await HeicConvert({ buffer: filedata, format });
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
import QueueProcessor from 'services/queueProcessor';
|
||||
import { retryAsyncFunction } from 'utils/network';
|
||||
import { DedicatedConvertWorker } from 'worker/convert.worker';
|
||||
import { ComlinkWorker } from 'utils/comlink/comlinkWorker';
|
||||
import { getDedicatedConvertWorker } from 'utils/comlink/ComlinkConvertWorker';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
import { addLogLine } from '@ente/shared/logging';
|
||||
import { convertBytesToHumanReadable } from '@ente/shared/utils/size';
|
||||
import { CustomError } from '@ente/shared/error';
|
||||
import { CustomError } from "@ente/shared/error";
|
||||
import { addLogLine } from "@ente/shared/logging";
|
||||
import { logError } from "@ente/shared/sentry";
|
||||
import { convertBytesToHumanReadable } from "@ente/shared/utils/size";
|
||||
import QueueProcessor from "services/queueProcessor";
|
||||
import { getDedicatedConvertWorker } from "utils/comlink/ComlinkConvertWorker";
|
||||
import { ComlinkWorker } from "utils/comlink/comlinkWorker";
|
||||
import { retryAsyncFunction } from "utils/network";
|
||||
import { DedicatedConvertWorker } from "worker/convert.worker";
|
||||
|
||||
const WORKER_POOL_SIZE = 2;
|
||||
const MAX_CONVERSION_IN_PARALLEL = 1;
|
||||
const WAIT_TIME_BEFORE_NEXT_ATTEMPT_IN_MICROSECONDS = [100, 100];
|
||||
const WAIT_TIME_IN_MICROSECONDS = 30 * 1000;
|
||||
const BREATH_TIME_IN_MICROSECONDS = 1000;
|
||||
const CONVERT_FORMAT = 'JPEG';
|
||||
const CONVERT_FORMAT = "JPEG";
|
||||
|
||||
class HEICConverter {
|
||||
private convertProcessor = new QueueProcessor<Blob>(
|
||||
MAX_CONVERSION_IN_PARALLEL
|
||||
MAX_CONVERSION_IN_PARALLEL,
|
||||
);
|
||||
private workerPool: ComlinkWorker<typeof DedicatedConvertWorker>[] = [];
|
||||
private ready: Promise<void>;
|
||||
|
@ -43,22 +43,22 @@ class HEICConverter {
|
|||
const main = async () => {
|
||||
try {
|
||||
const timeout = setTimeout(() => {
|
||||
reject(Error('wait time exceeded'));
|
||||
reject(Error("wait time exceeded"));
|
||||
}, WAIT_TIME_IN_MICROSECONDS);
|
||||
const startTime = Date.now();
|
||||
const convertedHEIC =
|
||||
await worker.convertHEIC(
|
||||
fileBlob,
|
||||
CONVERT_FORMAT
|
||||
CONVERT_FORMAT,
|
||||
);
|
||||
addLogLine(
|
||||
`originalFileSize:${convertBytesToHumanReadable(
|
||||
fileBlob?.size
|
||||
fileBlob?.size,
|
||||
)},convertedFileSize:${convertBytesToHumanReadable(
|
||||
convertedHEIC?.size
|
||||
convertedHEIC?.size,
|
||||
)}, heic conversion time: ${
|
||||
Date.now() - startTime
|
||||
}ms `
|
||||
}ms `,
|
||||
);
|
||||
clearTimeout(timeout);
|
||||
resolve(convertedHEIC);
|
||||
|
@ -67,37 +67,37 @@ class HEICConverter {
|
|||
}
|
||||
};
|
||||
main();
|
||||
}
|
||||
},
|
||||
);
|
||||
if (!convertedHEIC || convertedHEIC?.size === 0) {
|
||||
logError(
|
||||
Error(`converted heic fileSize is Zero`),
|
||||
'converted heic fileSize is Zero',
|
||||
"converted heic fileSize is Zero",
|
||||
{
|
||||
originalFileSize: convertBytesToHumanReadable(
|
||||
fileBlob?.size ?? 0
|
||||
fileBlob?.size ?? 0,
|
||||
),
|
||||
convertedFileSize: convertBytesToHumanReadable(
|
||||
convertedHEIC?.size ?? 0
|
||||
convertedHEIC?.size ?? 0,
|
||||
),
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(
|
||||
() => resolve(null),
|
||||
BREATH_TIME_IN_MICROSECONDS
|
||||
BREATH_TIME_IN_MICROSECONDS,
|
||||
);
|
||||
});
|
||||
this.workerPool.push(convertWorker);
|
||||
return convertedHEIC;
|
||||
} catch (e) {
|
||||
logError(e, 'heic conversion failed');
|
||||
logError(e, "heic conversion failed");
|
||||
convertWorker.terminate();
|
||||
this.workerPool.push(getDedicatedConvertWorker());
|
||||
throw e;
|
||||
}
|
||||
}, WAIT_TIME_BEFORE_NEXT_ATTEMPT_IN_MICROSECONDS)
|
||||
}, WAIT_TIME_BEFORE_NEXT_ATTEMPT_IN_MICROSECONDS),
|
||||
);
|
||||
try {
|
||||
return await response.promise;
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import { EnteFile } from 'types/file';
|
||||
import { CollectionSummaryType, CollectionType } from 'constants/collection';
|
||||
import { CollectionSummaryType, CollectionType } from "constants/collection";
|
||||
import { EnteFile } from "types/file";
|
||||
import {
|
||||
EncryptedMagicMetadata,
|
||||
MagicMetadataCore,
|
||||
SUB_TYPE,
|
||||
VISIBILITY_STATE,
|
||||
} from 'types/magicMetadata';
|
||||
} from "types/magicMetadata";
|
||||
|
||||
export enum COLLECTION_ROLE {
|
||||
VIEWER = 'VIEWER',
|
||||
OWNER = 'OWNER',
|
||||
COLLABORATOR = 'COLLABORATOR',
|
||||
UNKNOWN = 'UNKNOWN',
|
||||
VIEWER = "VIEWER",
|
||||
OWNER = "OWNER",
|
||||
COLLABORATOR = "COLLABORATOR",
|
||||
UNKNOWN = "UNKNOWN",
|
||||
}
|
||||
|
||||
export interface CollectionUser {
|
||||
|
@ -43,13 +43,13 @@ export interface EncryptedCollection {
|
|||
export interface Collection
|
||||
extends Omit<
|
||||
EncryptedCollection,
|
||||
| 'encryptedKey'
|
||||
| 'keyDecryptionNonce'
|
||||
| 'encryptedName'
|
||||
| 'nameDecryptionNonce'
|
||||
| 'magicMetadata'
|
||||
| 'pubMagicMetadata'
|
||||
| 'sharedMagicMetadata'
|
||||
| "encryptedKey"
|
||||
| "keyDecryptionNonce"
|
||||
| "encryptedName"
|
||||
| "nameDecryptionNonce"
|
||||
| "magicMetadata"
|
||||
| "pubMagicMetadata"
|
||||
| "sharedMagicMetadata"
|
||||
> {
|
||||
key: string;
|
||||
name: string;
|
||||
|
|
|
@ -2,8 +2,8 @@ import {
|
|||
EncryptedMagicMetadata,
|
||||
MagicMetadataCore,
|
||||
VISIBILITY_STATE,
|
||||
} from 'types/magicMetadata';
|
||||
import { Metadata } from 'types/upload';
|
||||
} from "types/magicMetadata";
|
||||
import { Metadata } from "types/upload";
|
||||
|
||||
export interface MetadataFileAttributes {
|
||||
encryptedData: string;
|
||||
|
@ -38,11 +38,11 @@ export interface EncryptedEnteFile {
|
|||
export interface EnteFile
|
||||
extends Omit<
|
||||
EncryptedEnteFile,
|
||||
| 'metadata'
|
||||
| 'pubMagicMetadata'
|
||||
| 'magicMetadata'
|
||||
| 'encryptedKey'
|
||||
| 'keyDecryptionNonce'
|
||||
| "metadata"
|
||||
| "pubMagicMetadata"
|
||||
| "magicMetadata"
|
||||
| "encryptedKey"
|
||||
| "keyDecryptionNonce"
|
||||
> {
|
||||
metadata: Metadata;
|
||||
magicMetadata: FileMagicMetadata;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// import { CollectionDownloadProgressAttributes } from 'components/Collections/CollectionDownloadProgress';
|
||||
// import { CollectionSelectorAttributes } from 'components/Collections/CollectionSelector';
|
||||
// import { TimeStampListItem } from 'components/PhotoList';
|
||||
import { User } from '@ente/shared/user/types';
|
||||
import { Collection } from 'types/collection';
|
||||
import { EnteFile } from 'types/file';
|
||||
import { User } from "@ente/shared/user/types";
|
||||
import { Collection } from "types/collection";
|
||||
import { EnteFile } from "types/file";
|
||||
|
||||
export type SelectedState = {
|
||||
[k: number]: boolean;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import {
|
||||
B64EncryptionResult,
|
||||
LocalFileAttributes,
|
||||
} from '@ente/shared/crypto/types';
|
||||
import { FILE_TYPE } from 'constants/file';
|
||||
import { Collection } from 'types/collection';
|
||||
} from "@ente/shared/crypto/types";
|
||||
import { FILE_TYPE } from "constants/file";
|
||||
import { Collection } from "types/collection";
|
||||
import {
|
||||
MetadataFileAttributes,
|
||||
S3FileAttributes,
|
||||
FilePublicMagicMetadata,
|
||||
FilePublicMagicMetadataProps,
|
||||
} from 'types/file';
|
||||
import { EncryptedMagicMetadata } from 'types/magicMetadata';
|
||||
MetadataFileAttributes,
|
||||
S3FileAttributes,
|
||||
} from "types/file";
|
||||
import { EncryptedMagicMetadata } from "types/magicMetadata";
|
||||
|
||||
export interface DataStream {
|
||||
stream: ReadableStream<Uint8Array>;
|
||||
|
@ -18,7 +18,7 @@ export interface DataStream {
|
|||
}
|
||||
|
||||
export function isDataStream(object: any): object is DataStream {
|
||||
return 'stream' in object;
|
||||
return "stream" in object;
|
||||
}
|
||||
|
||||
export type Logger = (message: string) => void;
|
||||
|
@ -114,7 +114,7 @@ export interface FileInMemory {
|
|||
}
|
||||
|
||||
export interface FileWithMetadata
|
||||
extends Omit<FileInMemory, 'hasStaticThumbnail'> {
|
||||
extends Omit<FileInMemory, "hasStaticThumbnail"> {
|
||||
metadata: Metadata;
|
||||
localID: number;
|
||||
pubMagicMetadata: FilePublicMagicMetadata;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { UPLOAD_RESULT, UPLOAD_STAGES } from 'constants/upload';
|
||||
import { UPLOAD_RESULT, UPLOAD_STAGES } from "constants/upload";
|
||||
|
||||
export type FileID = number;
|
||||
export type FileName = string;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { COLLECTION_ROLE, Collection } from 'types/collection';
|
||||
import { LS_KEYS, getData } from "@ente/shared/storage/localStorage";
|
||||
import { User } from "@ente/shared/user/types";
|
||||
import {
|
||||
CollectionSummaryType,
|
||||
CollectionType,
|
||||
HIDE_FROM_COLLECTION_BAR_TYPES,
|
||||
OPTIONS_NOT_HAVING_COLLECTION_TYPES,
|
||||
} from 'constants/collection';
|
||||
import { SUB_TYPE, VISIBILITY_STATE } from 'types/magicMetadata';
|
||||
import { User } from '@ente/shared/user/types';
|
||||
import { getData, LS_KEYS } from '@ente/shared/storage/localStorage';
|
||||
} from "constants/collection";
|
||||
import { COLLECTION_ROLE, Collection } from "types/collection";
|
||||
import { SUB_TYPE, VISIBILITY_STATE } from "types/magicMetadata";
|
||||
|
||||
export enum COLLECTION_OPS_TYPE {
|
||||
ADD,
|
||||
|
@ -19,7 +19,7 @@ export enum COLLECTION_OPS_TYPE {
|
|||
|
||||
export function getSelectedCollection(
|
||||
collectionID: number,
|
||||
collections: Collection[]
|
||||
collections: Collection[],
|
||||
) {
|
||||
return collections.find((collection) => collection.id === collectionID);
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ export const shouldBeShownOnCollectionBar = (type: CollectionSummaryType) => {
|
|||
export const getUserOwnedCollections = (collections: Collection[]) => {
|
||||
const user: User = getData(LS_KEYS.USER);
|
||||
if (!user?.id) {
|
||||
throw Error('user missing');
|
||||
throw Error("user missing");
|
||||
}
|
||||
return collections.filter((collection) => collection.owner.id === user.id);
|
||||
};
|
||||
|
@ -103,7 +103,7 @@ export function isSharedOnlyViaLink(collection: Collection) {
|
|||
export function isValidMoveTarget(
|
||||
sourceCollectionID: number,
|
||||
targetCollection: Collection,
|
||||
user: User
|
||||
user: User,
|
||||
) {
|
||||
return (
|
||||
sourceCollectionID !== targetCollection.id &&
|
||||
|
@ -116,7 +116,7 @@ export function isValidMoveTarget(
|
|||
export function isValidReplacementAlbum(
|
||||
collection: Collection,
|
||||
user: User,
|
||||
wantedCollectionName: string
|
||||
wantedCollectionName: string,
|
||||
) {
|
||||
return (
|
||||
collection.name === wantedCollectionName &&
|
||||
|
@ -129,15 +129,15 @@ export function isValidReplacementAlbum(
|
|||
}
|
||||
|
||||
export function getCollectionNameMap(
|
||||
collections: Collection[]
|
||||
collections: Collection[],
|
||||
): Map<number, string> {
|
||||
return new Map<number, string>(
|
||||
collections.map((collection) => [collection.id, collection.name])
|
||||
collections.map((collection) => [collection.id, collection.name]),
|
||||
);
|
||||
}
|
||||
|
||||
export function getNonHiddenCollections(
|
||||
collections: Collection[]
|
||||
collections: Collection[],
|
||||
): Collection[] {
|
||||
return collections.filter((collection) => !isHiddenCollection(collection));
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Remote } from 'comlink';
|
||||
import { DedicatedConvertWorker } from 'worker/convert.worker';
|
||||
import { ComlinkWorker } from './comlinkWorker';
|
||||
import { runningInBrowser } from '@ente/shared/platform';
|
||||
import { runningInBrowser } from "@ente/shared/platform";
|
||||
import { Remote } from "comlink";
|
||||
import { DedicatedConvertWorker } from "worker/convert.worker";
|
||||
import { ComlinkWorker } from "./comlinkWorker";
|
||||
|
||||
class ComlinkConvertWorker {
|
||||
private comlinkWorkerInstance: Remote<DedicatedConvertWorker>;
|
||||
|
@ -20,8 +20,8 @@ export const getDedicatedConvertWorker = () => {
|
|||
const cryptoComlinkWorker = new ComlinkWorker<
|
||||
typeof DedicatedConvertWorker
|
||||
>(
|
||||
'ente-convert-worker',
|
||||
new Worker(new URL('worker/convert.worker.ts', import.meta.url))
|
||||
"ente-convert-worker",
|
||||
new Worker(new URL("worker/convert.worker.ts", import.meta.url)),
|
||||
);
|
||||
return cryptoComlinkWorker;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue