feat(ui): add inplace option to scan folder install ui

This commit is contained in:
psychedelicious 2024-04-03 19:00:15 +11:00
parent 2383fb93c7
commit 25bbaa73b9
2 changed files with 52 additions and 40 deletions

View File

@ -1,48 +1,19 @@
import { Badge, Box, Flex, IconButton, Text } from '@invoke-ai/ui-library'; import { Badge, Box, Flex, IconButton, Text } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import { addToast } from 'features/system/store/systemSlice';
import { makeToast } from 'features/system/util/makeToast';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi'; import { PiPlusBold } from 'react-icons/pi';
import type { ScanFolderResponse } from 'services/api/endpoints/models'; import type { ScanFolderResponse } from 'services/api/endpoints/models';
import { useInstallModelMutation } from 'services/api/endpoints/models';
type Props = { type Props = {
result: ScanFolderResponse[number]; result: ScanFolderResponse[number];
installModel: (source: string) => void;
}; };
export const ScanModelResultItem = ({ result }: Props) => { export const ScanModelResultItem = ({ result, installModel }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch();
const [installModel] = useInstallModelMutation(); const handleInstall = useCallback(() => {
installModel(result.path);
const handleQuickAdd = useCallback(() => { }, [installModel, result]);
installModel({ source: result.path })
.unwrap()
.then((_) => {
dispatch(
addToast(
makeToast({
title: t('toast.modelAddedSimple'),
status: 'success',
})
)
);
})
.catch((error) => {
if (error) {
dispatch(
addToast(
makeToast({
title: `${error.data.detail} `,
status: 'error',
})
)
);
}
});
}, [installModel, result, dispatch, t]);
return ( return (
<Flex alignItems="center" justifyContent="space-between" w="100%" gap={3}> <Flex alignItems="center" justifyContent="space-between" w="100%" gap={3}>
@ -54,7 +25,7 @@ export const ScanModelResultItem = ({ result }: Props) => {
{result.is_installed ? ( {result.is_installed ? (
<Badge>{t('common.installed')}</Badge> <Badge>{t('common.installed')}</Badge>
) : ( ) : (
<IconButton aria-label={t('modelManager.install')} icon={<PiPlusBold />} onClick={handleQuickAdd} size="sm" /> <IconButton aria-label={t('modelManager.install')} icon={<PiPlusBold />} onClick={handleInstall} size="sm" />
)} )}
</Box> </Box>
</Flex> </Flex>

View File

@ -1,7 +1,10 @@
import { import {
Button, Button,
Checkbox,
Divider, Divider,
Flex, Flex,
FormControl,
FormLabel,
Heading, Heading,
IconButton, IconButton,
Input, Input,
@ -12,7 +15,7 @@ import { useAppDispatch } from 'app/store/storeHooks';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
import { makeToast } from 'features/system/util/makeToast'; import { makeToast } from 'features/system/util/makeToast';
import type { ChangeEventHandler } from 'react'; import type { ChangeEvent, ChangeEventHandler } from 'react';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiXBold } from 'react-icons/pi'; import { PiXBold } from 'react-icons/pi';
@ -28,7 +31,7 @@ export const ScanModelsResults = ({ results }: ScanModelResultsProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const [inplace, setInplace] = useState(true);
const [installModel] = useInstallModelMutation(); const [installModel] = useInstallModelMutation();
const filteredResults = useMemo(() => { const filteredResults = useMemo(() => {
@ -42,6 +45,10 @@ export const ScanModelsResults = ({ results }: ScanModelResultsProps) => {
setSearchTerm(e.target.value.trim()); setSearchTerm(e.target.value.trim());
}, []); }, []);
const onChangeInplace = useCallback((e: ChangeEvent<HTMLInputElement>) => {
setInplace(e.target.checked);
}, []);
const clearSearch = useCallback(() => { const clearSearch = useCallback(() => {
setSearchTerm(''); setSearchTerm('');
}, []); }, []);
@ -51,7 +58,7 @@ export const ScanModelsResults = ({ results }: ScanModelResultsProps) => {
if (result.is_installed) { if (result.is_installed) {
continue; continue;
} }
installModel({ source: result.path }) installModel({ source: result.path, inplace })
.unwrap() .unwrap()
.then((_) => { .then((_) => {
dispatch( dispatch(
@ -76,7 +83,37 @@ export const ScanModelsResults = ({ results }: ScanModelResultsProps) => {
} }
}); });
} }
}, [installModel, filteredResults, dispatch, t]); }, [filteredResults, installModel, inplace, dispatch, t]);
const handleInstallOne = useCallback(
(source: string) => {
installModel({ source, inplace })
.unwrap()
.then((_) => {
dispatch(
addToast(
makeToast({
title: t('toast.modelAddedSimple'),
status: 'success',
})
)
);
})
.catch((error) => {
if (error) {
dispatch(
addToast(
makeToast({
title: `${error.data.detail} `,
status: 'error',
})
)
);
}
});
},
[installModel, inplace, dispatch, t]
);
return ( return (
<> <>
@ -85,6 +122,10 @@ export const ScanModelsResults = ({ results }: ScanModelResultsProps) => {
<Flex justifyContent="space-between" alignItems="center"> <Flex justifyContent="space-between" alignItems="center">
<Heading size="sm">{t('modelManager.scanResults')}</Heading> <Heading size="sm">{t('modelManager.scanResults')}</Heading>
<Flex alignItems="center" gap={3}> <Flex alignItems="center" gap={3}>
<FormControl w="min-content">
<FormLabel m={0}>{t('modelManager.inplaceInstall')}</FormLabel>
<Checkbox isChecked={inplace} onChange={onChangeInplace} size="md" />
</FormControl>
<Button size="sm" onClick={handleAddAll} isDisabled={filteredResults.length === 0}> <Button size="sm" onClick={handleAddAll} isDisabled={filteredResults.length === 0}>
{t('modelManager.installAll')} {t('modelManager.installAll')}
</Button> </Button>
@ -116,7 +157,7 @@ export const ScanModelsResults = ({ results }: ScanModelResultsProps) => {
<ScrollableContent> <ScrollableContent>
<Flex flexDir="column" gap={3}> <Flex flexDir="column" gap={3}>
{filteredResults.map((result) => ( {filteredResults.map((result) => (
<ScanModelResultItem key={result.path} result={result} /> <ScanModelResultItem key={result.path} result={result} installModel={handleInstallOne} />
))} ))}
</Flex> </Flex>
</ScrollableContent> </ScrollableContent>