feat(ui): move model manager in-place install state to redux

- persists across sessions/refreshes
- shared state for all installers (local path, scan folder)
This commit is contained in:
Riku
2024-10-20 12:30:35 +02:00
committed by psychedelicious
parent 61d26cffe6
commit 6a24594140
3 changed files with 45 additions and 12 deletions

View File

@@ -12,6 +12,7 @@ type ModelManagerState = {
searchTerm: string;
filteredModelType: FilterableModelType | null;
scanPath: string | undefined;
shouldInstallInPlace: boolean;
};
const initialModelManagerState: ModelManagerState = {
@@ -21,6 +22,7 @@ const initialModelManagerState: ModelManagerState = {
filteredModelType: null,
searchTerm: '',
scanPath: undefined,
shouldInstallInPlace: true,
};
export const modelManagerV2Slice = createSlice({
@@ -37,18 +39,26 @@ export const modelManagerV2Slice = createSlice({
setSearchTerm: (state, action: PayloadAction<string>) => {
state.searchTerm = action.payload;
},
setFilteredModelType: (state, action: PayloadAction<FilterableModelType | null>) => {
state.filteredModelType = action.payload;
},
setScanPath: (state, action: PayloadAction<string | undefined>) => {
state.scanPath = action.payload;
},
shouldInstallInPlaceChanged: (state, action: PayloadAction<boolean>) => {
state.shouldInstallInPlace = action.payload;
},
},
});
export const { setSelectedModelKey, setSearchTerm, setFilteredModelType, setSelectedModelMode, setScanPath } =
modelManagerV2Slice.actions;
export const {
setSelectedModelKey,
setSearchTerm,
setFilteredModelType,
setSelectedModelMode,
setScanPath,
shouldInstallInPlaceChanged,
} = modelManagerV2Slice.actions;
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const migrateModelManagerState = (state: any): any => {
@@ -74,3 +84,4 @@ export const selectSelectedModelKey = createModelManagerSelector((modelManager)
export const selectSelectedModelMode = createModelManagerSelector((modelManager) => modelManager.selectedModelMode);
export const selectSearchTerm = createModelManagerSelector((mm) => mm.searchTerm);
export const selectFilteredModelType = createModelManagerSelector((mm) => mm.filteredModelType);
export const selectShouldInstallInPlace = createModelManagerSelector((mm) => mm.shouldInstallInPlace);

View File

@@ -1,22 +1,28 @@
import { Button, Checkbox, Flex, FormControl, FormHelperText, FormLabel, Input } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
import {
selectShouldInstallInPlace,
shouldInstallInPlaceChanged,
} from 'features/modelManagerV2/store/modelManagerV2Slice';
import { t } from 'i18next';
import type { ChangeEvent } from 'react';
import { memo, useCallback } from 'react';
import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form';
type SimpleImportModelConfig = {
location: string;
inplace: boolean;
};
export const InstallModelForm = memo(() => {
const inplace = useAppSelector(selectShouldInstallInPlace);
const dispatch = useAppDispatch();
const [installModel, { isLoading }] = useInstallModel();
const { register, handleSubmit, formState, reset } = useForm<SimpleImportModelConfig>({
defaultValues: {
location: '',
inplace: true,
},
mode: 'onChange',
});
@@ -31,12 +37,19 @@ export const InstallModelForm = memo(() => {
installModel({
source: values.location,
inplace: values.inplace,
inplace: inplace,
onSuccess: resetForm,
onError: resetForm,
});
},
[installModel, resetForm]
[installModel, resetForm, inplace]
);
const onChangeInplace = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
dispatch(shouldInstallInPlaceChanged(e.target.checked));
},
[dispatch]
);
return (
@@ -63,7 +76,7 @@ export const InstallModelForm = memo(() => {
<FormControl>
<Flex flexDir="column" gap={2}>
<Flex gap={4}>
<Checkbox {...register('inplace')} />
<Checkbox isChecked={inplace} onChange={onChangeInplace} />
<FormLabel>
{t('modelManager.inplaceInstall')} ({t('modelManager.localOnly')})
</FormLabel>

View File

@@ -11,8 +11,13 @@ import {
InputGroup,
InputRightElement,
} from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
import {
selectShouldInstallInPlace,
shouldInstallInPlaceChanged,
} from 'features/modelManagerV2/store/modelManagerV2Slice';
import type { ChangeEvent, ChangeEventHandler } from 'react';
import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -26,9 +31,10 @@ type ScanModelResultsProps = {
};
export const ScanModelsResults = memo(({ results }: ScanModelResultsProps) => {
const inplace = useAppSelector(selectShouldInstallInPlace);
const dispatch = useAppDispatch();
const { t } = useTranslation();
const [searchTerm, setSearchTerm] = useState('');
const [inplace, setInplace] = useState(true);
const [installModel] = useInstallModel();
const filteredResults = useMemo(() => {
@@ -42,9 +48,12 @@ export const ScanModelsResults = memo(({ results }: ScanModelResultsProps) => {
setSearchTerm(e.target.value.trim());
}, []);
const onChangeInplace = useCallback((e: ChangeEvent<HTMLInputElement>) => {
setInplace(e.target.checked);
}, []);
const onChangeInplace = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
dispatch(shouldInstallInPlaceChanged(e.target.checked));
},
[dispatch]
);
const clearSearch = useCallback(() => {
setSearchTerm('');