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 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;
|
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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue