mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
added add all button to scan models (#5811)
## What type of PR is this? (check all applicable) - [ ] Refactor - [X] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update - [ ] Community Node Submission ## Have you discussed this change with the InvokeAI team? - [X] Yes - [ ] No, because: ## Have you updated all relevant documentation? - [ ] Yes - [ ] No ## Description ## Related Tickets & Documents <!-- For pull requests that relate or close an issue, please include them below. For example having the text: "closes #1234" would connect the current pull request to issue 1234. And when we merge the pull request, Github will automatically close the issue. --> - Related Issue # - Closes # ## QA Instructions, Screenshots, Recordings <!-- Please provide steps on how to test changes, any hardware or software specifications as well as any other pertinent information. --> ## Merge Plan <!-- A merge plan describes how this PR should be handled after it is approved. Example merge plans: - "This PR can be merged when approved" - "This must be squash-merged when approved" - "DO NOT MERGE - I will rebase and tidy commits before merging" - "#dev-chat on discord needs to be advised of this change when it is merged" A merge plan is particularly important for large PRs or PRs that touch the database in any way. --> ## Added/updated tests? - [ ] Yes - [ ] No : _please replace this line with details on why tests have not been included_ ## [optional] Are there any post deployment tasks we need to perform?
This commit is contained in:
commit
4418c118db
@ -692,6 +692,7 @@
|
||||
},
|
||||
"modelManager": {
|
||||
"active": "active",
|
||||
"addAll": "Add All",
|
||||
"addCheckpointModel": "Add Checkpoint / Safetensor Model",
|
||||
"addDifference": "Add Difference",
|
||||
"addDiffuserModel": "Add Diffusers",
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Box, Button, Flex, Text } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { makeToast } from 'features/system/util/makeToast';
|
||||
import { t } from 'i18next';
|
||||
@ -57,9 +58,11 @@ export const ImportQueue = () => {
|
||||
</Button>
|
||||
</Flex>
|
||||
<Box mt={3} layerStyle="first" p={3} borderRadius="base" w="full" h="full">
|
||||
<Flex flexDir="column-reverse" gap="2">
|
||||
{data?.map((model) => <ImportQueueItem key={model.id} model={model} />)}
|
||||
</Flex>
|
||||
<ScrollableContent>
|
||||
<Flex flexDir="column-reverse" gap="2">
|
||||
{data?.map((model) => <ImportQueueItem key={model.id} model={model} />)}
|
||||
</Flex>
|
||||
</ScrollableContent>
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
|
@ -121,7 +121,7 @@ export const ImportQueueItem = (props: ModelListItemProps) => {
|
||||
</Box>
|
||||
|
||||
<Box minW="20px">
|
||||
{(model.status === 'downloading' || model.status === 'waiting') && (
|
||||
{(model.status === 'downloading' || model.status === 'waiting' || model.status === 'running') && (
|
||||
<IconButton
|
||||
isRound={true}
|
||||
size="xs"
|
||||
|
@ -1,10 +1,22 @@
|
||||
import { Divider, Flex, Heading, IconButton, Input, InputGroup, InputRightElement } from '@invoke-ai/ui-library';
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
Flex,
|
||||
Heading,
|
||||
IconButton,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputRightElement,
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { makeToast } from 'features/system/util/makeToast';
|
||||
import type { ChangeEventHandler } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiXBold } from 'react-icons/pi';
|
||||
import type { ScanFolderResponse } from 'services/api/endpoints/models';
|
||||
import { type ScanFolderResponse, useInstallModelMutation } from 'services/api/endpoints/models';
|
||||
|
||||
import { ScanModelResultItem } from './ScanModelResultItem';
|
||||
|
||||
@ -15,6 +27,9 @@ type ScanModelResultsProps = {
|
||||
export const ScanModelsResults = ({ results }: ScanModelResultsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const [installModel] = useInstallModelMutation();
|
||||
|
||||
const filteredResults = useMemo(() => {
|
||||
return results.filter((result) => {
|
||||
@ -31,6 +46,38 @@ export const ScanModelsResults = ({ results }: ScanModelResultsProps) => {
|
||||
setSearchTerm('');
|
||||
}, []);
|
||||
|
||||
const handleAddAll = useCallback(() => {
|
||||
for (const result of filteredResults) {
|
||||
if (result.is_installed) {
|
||||
continue;
|
||||
}
|
||||
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, filteredResults, dispatch, t]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Divider mt={4} />
|
||||
@ -39,27 +86,32 @@ export const ScanModelsResults = ({ results }: ScanModelResultsProps) => {
|
||||
<Heading fontSize="md" as="h4">
|
||||
{t('modelManager.scanResults')}
|
||||
</Heading>
|
||||
<InputGroup maxW="300px" size="xs">
|
||||
<Input
|
||||
placeholder={t('modelManager.search')}
|
||||
value={searchTerm}
|
||||
data-testid="board-search-input"
|
||||
onChange={handleSearch}
|
||||
size="xs"
|
||||
/>
|
||||
<Flex alignItems="center" gap="4">
|
||||
<Button onClick={handleAddAll} isDisabled={filteredResults.length === 0}>
|
||||
{t('modelManager.addAll')}
|
||||
</Button>
|
||||
<InputGroup maxW="300px" size="xs">
|
||||
<Input
|
||||
placeholder={t('modelManager.search')}
|
||||
value={searchTerm}
|
||||
data-testid="board-search-input"
|
||||
onChange={handleSearch}
|
||||
size="xs"
|
||||
/>
|
||||
|
||||
{searchTerm && (
|
||||
<InputRightElement h="full" pe={2}>
|
||||
<IconButton
|
||||
size="sm"
|
||||
variant="link"
|
||||
aria-label={t('boards.clearSearch')}
|
||||
icon={<PiXBold />}
|
||||
onClick={clearSearch}
|
||||
/>
|
||||
</InputRightElement>
|
||||
)}
|
||||
</InputGroup>
|
||||
{searchTerm && (
|
||||
<InputRightElement h="full" pe={2}>
|
||||
<IconButton
|
||||
size="sm"
|
||||
variant="link"
|
||||
aria-label={t('boards.clearSearch')}
|
||||
icon={<PiXBold />}
|
||||
onClick={clearSearch}
|
||||
/>
|
||||
</InputRightElement>
|
||||
)}
|
||||
</InputGroup>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex height="100%" layerStyle="third" borderRadius="base" p={4} mt={4} mb={4}>
|
||||
<ScrollableContent>
|
||||
|
Loading…
Reference in New Issue
Block a user