mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): add inplace
option to scan folder install ui
This commit is contained in:
parent
2383fb93c7
commit
25bbaa73b9
@ -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>
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user