add ML settings components

This commit is contained in:
Abhinav 2023-02-08 20:07:12 +05:30
parent 83ebce22a9
commit 13712cf8ce
7 changed files with 364 additions and 1 deletions

View file

@ -0,0 +1,79 @@
import {
Stack,
Box,
Button,
FormGroup,
Checkbox,
FormControlLabel,
} from '@mui/material';
import { EnteDrawer } from 'components/EnteDrawer';
import Titlebar from 'components/Titlebar';
import { useState } from 'react';
import constants from 'utils/strings/constants';
export default function EnableFaceSearch({
open,
onClose,
enableFaceSearch,
onRootClose,
}) {
const [acceptTerms, setAcceptTerms] = useState(false);
return (
<EnteDrawer
transitionDuration={0}
open={open}
onClose={onClose}
BackdropProps={{
onClick: onRootClose,
sx: { '&&&': { backgroundColor: 'transparent' } },
}}>
<Stack spacing={'4px'} py={'12px'}>
<Titlebar
onClose={onClose}
title={constants.ENABLE_FACE_SEARCH_TITLE}
onRootClose={onRootClose}
/>
<Stack py={'20px'} px={'8px'} spacing={'32px'}>
<Box px={'8px'}>
{constants.ENABLE_FACE_SEARCH_DESCRIPTION()}
</Box>
<FormGroup sx={{ width: '100%' }}>
<FormControlLabel
sx={{
color: 'text.secondary',
ml: 0,
mt: 2,
}}
control={
<Checkbox
size="small"
checked={acceptTerms}
onChange={(e) =>
setAcceptTerms(e.target.checked)
}
color="accent"
/>
}
label={constants.FACE_SEARCH_CONFIRMATION}
/>
</FormGroup>
<Stack px={'8px'} spacing={'8px'}>
<Button
color={'accent'}
size="large"
disabled={!acceptTerms}
onClick={enableFaceSearch}>
{constants.ENABLE_FACE_SEARCH}
</Button>
<Button
color={'secondary'}
size="large"
onClick={onClose}>
{constants.CANCEL}
</Button>
</Stack>
</Stack>
</Stack>
</EnteDrawer>
);
}

View file

@ -0,0 +1,46 @@
import { Stack, Box, Button } from '@mui/material';
import { EnteDrawer } from 'components/EnteDrawer';
import Titlebar from 'components/Titlebar';
import { ML_BLOG_LINK } from 'constants/urls';
import { openLink } from 'utils/common';
import constants from 'utils/strings/constants';
export default function EnableMLSearch({
open,
onClose,
enableMlSearch,
onRootClose,
}) {
return (
<EnteDrawer
hideBackdrop
open={open}
onClose={onClose}
transitionDuration={0}>
<Stack spacing={'4px'} py={'12px'}>
<Titlebar
onClose={onClose}
title={constants.ML_SEARCH}
onRootClose={onRootClose}
/>
<Stack py={'20px'} px={'8px'} spacing={'32px'}>
<Box px={'8px'}>{constants.ML_SEARCH_DESCRIPTION()}</Box>
<Stack px={'8px'} spacing={'8px'}>
<Button
color={'accent'}
size="large"
onClick={enableMlSearch}>
{constants.ENABLE}
</Button>
<Button
color={'secondary'}
size="large"
onClick={() => openLink(ML_BLOG_LINK, true)}>
{constants.ML_MORE_DETAILS}
</Button>
</Stack>
</Stack>
</Stack>
</EnteDrawer>
);
}

View file

@ -0,0 +1,118 @@
import { Box } from '@mui/material';
import { AppContext } from 'pages/_app';
import { useContext, useState } from 'react';
import {
getFaceSearchStatus,
updateFaceSearchStatus,
} from 'services/userService';
import { logError } from 'utils/sentry';
import constants from 'utils/strings/constants';
import EnableFaceSearch from './enableFaceSearch';
import EnableMLSearch from './enableMLSearch';
import ManageMLSearch from './manageMLSearch';
const MLSearchSettings = ({ open, onClose, OnRootClose }) => {
const {
updateMlSearchEnabled,
mlSearchEnabled,
setDialogMessage,
somethingWentWrong,
} = useContext(AppContext);
const [enableFaceSearchView, setEnableFaceSearchView] = useState(false);
const openEnableFaceSearch = () => {
setEnableFaceSearchView(true);
};
const closeEnableFaceSearch = () => {
setEnableFaceSearchView(false);
};
const enableMlSearch = async () => {
try {
const hasEnabledFaceSearch = await getFaceSearchStatus();
if (!hasEnabledFaceSearch) {
openEnableFaceSearch();
} else {
updateMlSearchEnabled(true);
}
} catch (e) {
logError(e, 'Enable ML search failed');
somethingWentWrong();
}
};
const enableFaceSearch = async () => {
try {
await updateFaceSearchStatus(true);
updateMlSearchEnabled(true);
closeEnableFaceSearch();
} catch (e) {
logError(e, 'Enable face search failed');
somethingWentWrong();
}
};
const disableMlSearch = async () => {
try {
await updateMlSearchEnabled(false);
onClose();
} catch (e) {
logError(e, 'Disable ML search failed');
somethingWentWrong();
}
};
const disableFaceSearch = async () => {
try {
await updateFaceSearchStatus(false);
await disableMlSearch();
} catch (e) {
logError(e, 'Disable face search failed');
somethingWentWrong();
}
};
const confirmDisableFaceSearch = () => {
setDialogMessage({
title: constants.DISABLE_FACE_SEARCH_TITLE,
content: constants.DISABLE_FACE_SEARCH_DESCRIPTION(),
close: { text: constants.CANCEL },
proceed: {
variant: 'primary',
text: constants.DISABLE_FACE_SEARCH,
action: disableFaceSearch,
},
});
};
return (
<Box>
{mlSearchEnabled ? (
<ManageMLSearch
open={open}
onClose={onClose}
disableMlSearch={disableMlSearch}
handleDisableFaceSearch={confirmDisableFaceSearch}
onRootClose={OnRootClose}
/>
) : (
<EnableMLSearch
open={open}
onClose={onClose}
enableMlSearch={enableMlSearch}
onRootClose={OnRootClose}
/>
)}
<EnableFaceSearch
open={enableFaceSearchView}
onClose={closeEnableFaceSearch}
enableFaceSearch={enableFaceSearch}
onRootClose={OnRootClose}
/>
</Box>
);
};
export default MLSearchSettings;

View file

@ -0,0 +1,53 @@
import { Stack, Box, ButtonProps, TypographyVariant } from '@mui/material';
import { EnteDrawer } from 'components/EnteDrawer';
import SidebarButton from 'components/Sidebar/Button';
import Titlebar from 'components/Titlebar';
import constants from 'utils/strings/constants';
type Iprops = ButtonProps<'button', { typographyVariant?: TypographyVariant }>;
const ManageOptions = (props: Iprops) => {
return (
<SidebarButton
variant="contained"
color="secondary"
{...props}></SidebarButton>
);
};
export default function ManageMLSearch({
open,
onClose,
disableMlSearch,
handleDisableFaceSearch,
onRootClose,
}) {
return (
<EnteDrawer
anchor="left"
transitionDuration={0}
open={open}
onClose={onClose}
BackdropProps={{
sx: { '&&&': { backgroundColor: 'transparent' } },
}}>
<Stack spacing={'4px'} py={'12px'}>
<Titlebar
onClose={onClose}
title={constants.ML_SEARCH}
onRootClose={onRootClose}
/>
<Box px={'16px'}>
<Stack py={'20px'} spacing={'24px'}>
<ManageOptions onClick={disableMlSearch}>
{constants.DISABLE_BETA}
</ManageOptions>
<ManageOptions onClick={handleDisableFaceSearch}>
{constants.DISABLE_FACE_SEARCH}
</ManageOptions>
</Stack>
</Box>
</Stack>
</EnteDrawer>
);
}

View file

@ -1 +1,4 @@
export const ENTE_WEBSITE_LINK = 'https://ente.io'; export const ENTE_WEBSITE_LINK = 'https://ente.io';
export const ML_BLOG_LINK =
'https://on-device-ml.ente-website.pages.dev/blog/desktop-ml-beta';

View file

@ -76,7 +76,7 @@ type AppContextType = {
redirectURL: string; redirectURL: string;
setRedirectURL: (url: string) => void; setRedirectURL: (url: string) => void;
mlSearchEnabled: boolean; mlSearchEnabled: boolean;
updateMlSearchEnabled: (enabled: boolean) => void; updateMlSearchEnabled: (enabled: boolean) => Promise<void>;
startLoading: () => void; startLoading: () => void;
finishLoading: () => void; finishLoading: () => void;
closeMessageDialog: () => void; closeMessageDialog: () => void;
@ -91,6 +91,7 @@ type AppContextType = {
isMobile: boolean; isMobile: boolean;
theme: THEME_COLOR; theme: THEME_COLOR;
setTheme: SetTheme; setTheme: SetTheme;
somethingWentWrong: () => void;
}; };
export enum FLASH_MESSAGE_TYPE { export enum FLASH_MESSAGE_TYPE {
@ -328,6 +329,13 @@ export default function App({ Component, err }) {
const closeMessageDialog = () => setMessageDialogView(false); const closeMessageDialog = () => setMessageDialogView(false);
const somethingWentWrong = () =>
setDialogMessage({
title: constants.ERROR,
close: { variant: 'danger' },
content: constants.UNKNOWN_ERROR,
});
return ( return (
<> <>
<Head> <Head>
@ -406,6 +414,7 @@ export default function App({ Component, err }) {
setNotificationAttributes, setNotificationAttributes,
theme, theme,
setTheme, setTheme,
somethingWentWrong,
}}> }}>
{loading ? ( {loading ? (
<VerticallyCentered> <VerticallyCentered>

View file

@ -920,6 +920,61 @@ const englishConstants = {
), ),
ADD_X_PHOTOS: (x: number) => `Add ${x} ${x > 1 ? 'photos' : 'photo'}`, ADD_X_PHOTOS: (x: number) => `Add ${x} ${x > 1 ? 'photos' : 'photo'}`,
CHOSE_THEME: 'Choose theme', CHOSE_THEME: 'Choose theme',
ML_SEARCH: 'ML search (beta)',
ML_SEARCH_DESCRIPTION: () => (
<>
<Typography color="text.secondary">
This will enable on-device machine learning and face search
which will start analyzing your uploaded photos locally.
<br />
<br />
For the first run after login or enabling this feature, it will
download all images on local device to analyze them. So please
only enable this if you are ok with bandwidth and local
processing of all images in your photo library.
<br />
<br />
If this is the first time you're enabling this, we'll also ask
your permission to process face data.
</Typography>
</>
),
ML_MORE_DETAILS: 'More details',
ENABLE_FACE_SEARCH: 'Enable face search',
ENABLE_FACE_SEARCH_TITLE: 'Enable face search?',
ENABLE_FACE_SEARCH_DESCRIPTION: () => (
<>
<Typography color="text.secondary">
If you enable face search, ente will extract face geometry from
your photos. This will happen on your device, and any generated
biometric data will be end-to-encrypted.
<br />
<br />
<Link href="#">
Please click here for more details about this feature in our
privacy policy
</Link>
</Typography>
</>
),
DISABLE_BETA: 'Disable beta',
DISABLE_FACE_SEARCH: 'Disable face search',
DISABLE_FACE_SEARCH_TITLE: 'Disable face search?',
DISABLE_FACE_SEARCH_DESCRIPTION: () => (
<Typography>
ente will stop processing face geometry, and will also disable ML
search (beta)
<br />
<br />
You can reenable face search again if you wish, so this operation is
safe
</Typography>
),
ADVANCED: 'Advanced',
FACE_SEARCH_CONFIRMATION:
'I understand, and wish to allow ente to process face geometry',
LABS: 'Labs',
}; };
export default englishConstants; export default englishConstants;