add ML settings components
This commit is contained in:
parent
83ebce22a9
commit
13712cf8ce
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
118
src/components/MachineLearning/MLSearchSettings/index.tsx
Normal file
118
src/components/MachineLearning/MLSearchSettings/index.tsx
Normal 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;
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -1 +1,4 @@
|
|||
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';
|
||||
|
|
|
@ -76,7 +76,7 @@ type AppContextType = {
|
|||
redirectURL: string;
|
||||
setRedirectURL: (url: string) => void;
|
||||
mlSearchEnabled: boolean;
|
||||
updateMlSearchEnabled: (enabled: boolean) => void;
|
||||
updateMlSearchEnabled: (enabled: boolean) => Promise<void>;
|
||||
startLoading: () => void;
|
||||
finishLoading: () => void;
|
||||
closeMessageDialog: () => void;
|
||||
|
@ -91,6 +91,7 @@ type AppContextType = {
|
|||
isMobile: boolean;
|
||||
theme: THEME_COLOR;
|
||||
setTheme: SetTheme;
|
||||
somethingWentWrong: () => void;
|
||||
};
|
||||
|
||||
export enum FLASH_MESSAGE_TYPE {
|
||||
|
@ -328,6 +329,13 @@ export default function App({ Component, err }) {
|
|||
|
||||
const closeMessageDialog = () => setMessageDialogView(false);
|
||||
|
||||
const somethingWentWrong = () =>
|
||||
setDialogMessage({
|
||||
title: constants.ERROR,
|
||||
close: { variant: 'danger' },
|
||||
content: constants.UNKNOWN_ERROR,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
|
@ -406,6 +414,7 @@ export default function App({ Component, err }) {
|
|||
setNotificationAttributes,
|
||||
theme,
|
||||
setTheme,
|
||||
somethingWentWrong,
|
||||
}}>
|
||||
{loading ? (
|
||||
<VerticallyCentered>
|
||||
|
|
|
@ -920,6 +920,61 @@ const englishConstants = {
|
|||
),
|
||||
ADD_X_PHOTOS: (x: number) => `Add ${x} ${x > 1 ? 'photos' : 'photo'}`,
|
||||
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;
|
||||
|
|
Loading…
Reference in a new issue