Switch to prettier defaults (#1656)

This commit is contained in:
Manav Rathi 2024-02-24 14:16:19 +05:30 committed by GitHub
commit 8b1d33e83d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
669 changed files with 12321 additions and 12054 deletions

View file

@ -1,3 +0,0 @@
{
"tabWidth": 4
}

View file

@ -1,6 +1,4 @@
{
"tabWidth": 4,
"trailingComma": "es5",
"singleQuote": true,
"bracketSameLine": true
"plugins": ["prettier-plugin-organize-imports"]
}

View file

@ -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"],
};

View file

@ -1,4 +1,4 @@
const nextConfigBase = require('@/next/next.config.base.js');
const nextConfigBase = require("@/next/next.config.base.js");
module.exports = {
...nextConfigBase,

View file

@ -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");

View file

@ -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>
)}

View file

@ -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} />;

View file

@ -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) || {};

View file

@ -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);

View file

@ -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);

View file

@ -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 <></>;
};

View file

@ -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);

View file

@ -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>

View file

@ -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>

View file

@ -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;

View file

@ -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[];

View file

@ -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 } }}

View file

@ -1,4 +1,4 @@
import PasskeysFinishPage from '@ente/accounts/pages/passkeys/finish';
import PasskeysFinishPage from "@ente/accounts/pages/passkeys/finish";
const PasskeysFinish = () => {
return <PasskeysFinishPage />;
};

View file

@ -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

View file

@ -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: {

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;
}
};

View file

@ -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;
}

View file

@ -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"],
};

View file

@ -1,3 +1,3 @@
const nextConfigBase = require('@/next/next.config.base.js');
const nextConfigBase = require("@/next/next.config.base.js");
module.exports = nextConfigBase;

View file

@ -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");

View file

@ -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>
);

View file

@ -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>

View file

@ -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}

View file

@ -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,
}}
/>

View file

@ -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);

View file

@ -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>
)}

View file

@ -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} />;

View file

@ -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);

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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();

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;
}
};

View file

@ -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();
}
}

View file

@ -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"],
};

View file

@ -1,3 +1,3 @@
const nextConfigBase = require('@/next/next.config.base.js');
const nextConfigBase = require("@/next/next.config.base.js");
module.exports = nextConfigBase;

View file

@ -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");

View file

@ -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"

View file

@ -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>

View file

@ -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>

View file

@ -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);

View file

@ -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>

View file

@ -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 {

View file

@ -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
}}
/>
);

View file

@ -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" };
}
};

View file

@ -1,5 +1,5 @@
export enum CACHES {
THUMBS = 'thumbs',
FACE_CROPS = 'face-crops',
FILES = 'files',
THUMBS = "thumbs",
FACE_CROPS = "face-crops",
FILES = "files",
}

View file

@ -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,

View file

@ -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";

View file

@ -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",
];

View file

@ -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

View file

@ -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",
}

View file

@ -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=";

View file

@ -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";

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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);

View file

@ -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>;

View file

@ -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) {

View file

@ -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");
}
}
}

View file

@ -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,
),
);
};

View file

@ -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));

View file

@ -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>();

View file

@ -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>;
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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" });
};

View file

@ -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 = {

View file

@ -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);

View file

@ -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}`);
}

View file

@ -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);

View file

@ -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 });

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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));
}

View file

@ -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