Switch to prettier defaults

This is unfortunately a huge diff, but now is as good as ever to do this pending chore.

- Move to prettier (v3) defaults. The only thing we tweak is the tabWidth (the
  default is 2, we change it to 4).

- Use the prettier plugin to sort imports.

We were not changing the defaults of prettier much anyway, the main thing that
has changed is the switching from the overridden single quotes to the default
double quotes.
This commit is contained in:
Manav Rathi 2024-02-24 13:48:57 +05:30
parent 0b8d9fafee
commit c1bf193211
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