mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): wip canvas migration, createListenerMiddleware
This commit is contained in:
parent
a75148cb16
commit
6ab5d28cf3
@ -118,6 +118,7 @@
|
|||||||
"@types/node": "^18.16.2",
|
"@types/node": "^18.16.2",
|
||||||
"@types/react": "^18.2.0",
|
"@types/react": "^18.2.0",
|
||||||
"@types/react-dom": "^18.2.1",
|
"@types/react-dom": "^18.2.1",
|
||||||
|
"@types/react-redux": "^7.1.25",
|
||||||
"@types/react-transition-group": "^4.4.5",
|
"@types/react-transition-group": "^4.4.5",
|
||||||
"@types/uuid": "^9.0.0",
|
"@types/uuid": "^9.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.1",
|
"@typescript-eslint/eslint-plugin": "^5.59.1",
|
||||||
|
@ -550,7 +550,7 @@
|
|||||||
"imageCopied": "Image Copied",
|
"imageCopied": "Image Copied",
|
||||||
"imageLinkCopied": "Image Link Copied",
|
"imageLinkCopied": "Image Link Copied",
|
||||||
"imageNotLoaded": "No Image Loaded",
|
"imageNotLoaded": "No Image Loaded",
|
||||||
"imageNotLoadedDesc": "No image found to send to image to image module",
|
"imageNotLoadedDesc": "Could not find image",
|
||||||
"imageSavedToGallery": "Image Saved to Gallery",
|
"imageSavedToGallery": "Image Saved to Gallery",
|
||||||
"canvasMerged": "Canvas Merged",
|
"canvasMerged": "Canvas Merged",
|
||||||
"sentToImageToImage": "Sent To Image To Image",
|
"sentToImageToImage": "Sent To Image To Image",
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
import {
|
||||||
|
createListenerMiddleware,
|
||||||
|
addListener,
|
||||||
|
ListenerEffect,
|
||||||
|
AnyAction,
|
||||||
|
} from '@reduxjs/toolkit';
|
||||||
|
import type { TypedStartListening, TypedAddListener } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
import type { RootState, AppDispatch } from '../../store';
|
||||||
|
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||||
|
import { initialImageListener } from './listeners/initialImageListener';
|
||||||
|
import {
|
||||||
|
imageResultReceivedListener,
|
||||||
|
imageResultReceivedPrediate,
|
||||||
|
} from './listeners/invocationCompleteListener';
|
||||||
|
import { imageUploaded } from 'services/thunks/image';
|
||||||
|
import { imageUploadedListener } from './listeners/imageUploadedListener';
|
||||||
|
|
||||||
|
export const listenerMiddleware = createListenerMiddleware();
|
||||||
|
|
||||||
|
export type AppStartListening = TypedStartListening<RootState, AppDispatch>;
|
||||||
|
|
||||||
|
export const startAppListening =
|
||||||
|
listenerMiddleware.startListening as AppStartListening;
|
||||||
|
|
||||||
|
export const addAppListener = addListener as TypedAddListener<
|
||||||
|
RootState,
|
||||||
|
AppDispatch
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type AppListenerEffect = ListenerEffect<
|
||||||
|
AnyAction,
|
||||||
|
RootState,
|
||||||
|
AppDispatch
|
||||||
|
>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initial image selected
|
||||||
|
*/
|
||||||
|
startAppListening({
|
||||||
|
actionCreator: initialImageSelected,
|
||||||
|
effect: initialImageListener,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image Result received
|
||||||
|
*/
|
||||||
|
startAppListening({
|
||||||
|
predicate: imageResultReceivedPrediate,
|
||||||
|
effect: imageResultReceivedListener,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image Uploaded
|
||||||
|
*/
|
||||||
|
startAppListening({
|
||||||
|
actionCreator: imageUploaded.fulfilled,
|
||||||
|
effect: imageUploadedListener,
|
||||||
|
});
|
@ -0,0 +1,19 @@
|
|||||||
|
import { deserializeImageResponse } from 'services/util/deserializeImageResponse';
|
||||||
|
import { AppListenerEffect } from '..';
|
||||||
|
import { uploadAdded } from 'features/gallery/store/uploadsSlice';
|
||||||
|
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||||
|
|
||||||
|
export const imageUploadedListener: AppListenerEffect = (
|
||||||
|
action,
|
||||||
|
{ dispatch, getState }
|
||||||
|
) => {
|
||||||
|
const { response } = action.payload;
|
||||||
|
const state = getState();
|
||||||
|
const image = deserializeImageResponse(response);
|
||||||
|
|
||||||
|
dispatch(uploadAdded(image));
|
||||||
|
|
||||||
|
if (state.gallery.shouldAutoSwitchToNewImages) {
|
||||||
|
dispatch(imageSelected(image));
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,53 @@
|
|||||||
|
import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
||||||
|
import { Image, isInvokeAIImage } from 'app/types/invokeai';
|
||||||
|
import { selectResultsById } from 'features/gallery/store/resultsSlice';
|
||||||
|
import { selectUploadsById } from 'features/gallery/store/uploadsSlice';
|
||||||
|
import { makeToast } from 'features/system/hooks/useToastWatcher';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
|
import { AnyAction, ListenerEffect } from '@reduxjs/toolkit';
|
||||||
|
import { AppDispatch, RootState } from 'app/store/store';
|
||||||
|
|
||||||
|
export const initialImageListener: ListenerEffect<
|
||||||
|
AnyAction,
|
||||||
|
RootState,
|
||||||
|
AppDispatch
|
||||||
|
> = (action, { getState, dispatch }) => {
|
||||||
|
if (!action.payload) {
|
||||||
|
dispatch(
|
||||||
|
addToast(
|
||||||
|
makeToast({ title: t('toast.imageNotLoadedDesc'), status: 'error' })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInvokeAIImage(action.payload)) {
|
||||||
|
dispatch(initialImageChanged(action.payload));
|
||||||
|
dispatch(addToast(makeToast(t('toast.sentToImageToImage'))));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name, type } = action.payload;
|
||||||
|
|
||||||
|
let image: Image | undefined;
|
||||||
|
const state = getState();
|
||||||
|
|
||||||
|
if (type === 'results') {
|
||||||
|
image = selectResultsById(state, name);
|
||||||
|
} else if (type === 'uploads') {
|
||||||
|
image = selectUploadsById(state, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!image) {
|
||||||
|
dispatch(
|
||||||
|
addToast(
|
||||||
|
makeToast({ title: t('toast.imageNotLoadedDesc'), status: 'error' })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(initialImageChanged(image));
|
||||||
|
dispatch(addToast(makeToast(t('toast.sentToImageToImage'))));
|
||||||
|
};
|
@ -0,0 +1,75 @@
|
|||||||
|
import { AnyListenerPredicate } from '@reduxjs/toolkit';
|
||||||
|
import { invocationComplete } from 'services/events/actions';
|
||||||
|
import { isImageOutput } from 'services/types/guards';
|
||||||
|
import { RootState } from 'app/store/store';
|
||||||
|
import {
|
||||||
|
buildImageUrls,
|
||||||
|
extractTimestampFromImageName,
|
||||||
|
} from 'services/util/deserializeImageField';
|
||||||
|
import { Image } from 'app/types/invokeai';
|
||||||
|
import { resultAdded } from 'features/gallery/store/resultsSlice';
|
||||||
|
import { imageReceived, thumbnailReceived } from 'services/thunks/image';
|
||||||
|
import { AppListenerEffect } from '..';
|
||||||
|
|
||||||
|
export const imageResultReceivedPrediate: AnyListenerPredicate<RootState> = (
|
||||||
|
action,
|
||||||
|
_currentState,
|
||||||
|
_originalState
|
||||||
|
) => {
|
||||||
|
if (
|
||||||
|
invocationComplete.match(action) &&
|
||||||
|
isImageOutput(action.payload.data.result)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const imageResultReceivedListener: AppListenerEffect = (
|
||||||
|
action,
|
||||||
|
{ getState, dispatch }
|
||||||
|
) => {
|
||||||
|
const { data, shouldFetchImages } = action.payload;
|
||||||
|
const { result, node, graph_execution_state_id } = data;
|
||||||
|
|
||||||
|
if (isImageOutput(result)) {
|
||||||
|
const name = result.image.image_name;
|
||||||
|
const type = result.image.image_type;
|
||||||
|
const state = getState();
|
||||||
|
|
||||||
|
// if we need to refetch, set URLs to placeholder for now
|
||||||
|
const { url, thumbnail } = shouldFetchImages
|
||||||
|
? { url: '', thumbnail: '' }
|
||||||
|
: buildImageUrls(type, name);
|
||||||
|
|
||||||
|
const timestamp = extractTimestampFromImageName(name);
|
||||||
|
|
||||||
|
const image: Image = {
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
url,
|
||||||
|
thumbnail,
|
||||||
|
metadata: {
|
||||||
|
created: timestamp,
|
||||||
|
width: result.width,
|
||||||
|
height: result.height,
|
||||||
|
invokeai: {
|
||||||
|
session_id: graph_execution_state_id,
|
||||||
|
...(node ? { node } : {}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch(resultAdded(image));
|
||||||
|
|
||||||
|
if (state.config.shouldFetchImages) {
|
||||||
|
dispatch(imageReceived({ imageName: name, imageType: type }));
|
||||||
|
dispatch(
|
||||||
|
thumbnailReceived({
|
||||||
|
thumbnailName: name,
|
||||||
|
thumbnailType: type,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -1,4 +1,9 @@
|
|||||||
import { combineReducers, configureStore } from '@reduxjs/toolkit';
|
import {
|
||||||
|
AnyAction,
|
||||||
|
ThunkDispatch,
|
||||||
|
combineReducers,
|
||||||
|
configureStore,
|
||||||
|
} from '@reduxjs/toolkit';
|
||||||
|
|
||||||
import { persistReducer } from 'redux-persist';
|
import { persistReducer } from 'redux-persist';
|
||||||
import storage from 'redux-persist/lib/storage'; // defaults to localStorage for web
|
import storage from 'redux-persist/lib/storage'; // defaults to localStorage for web
|
||||||
@ -30,6 +35,7 @@ import { systemDenylist } from 'features/system/store/systemPersistDenylist';
|
|||||||
import { uiDenylist } from 'features/ui/store/uiPersistDenylist';
|
import { uiDenylist } from 'features/ui/store/uiPersistDenylist';
|
||||||
import { resultsDenylist } from 'features/gallery/store/resultsPersistDenylist';
|
import { resultsDenylist } from 'features/gallery/store/resultsPersistDenylist';
|
||||||
import { uploadsDenylist } from 'features/gallery/store/uploadsPersistDenylist';
|
import { uploadsDenylist } from 'features/gallery/store/uploadsPersistDenylist';
|
||||||
|
import { listenerMiddleware } from './middleware/listenerMiddleware';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* redux-persist provides an easy and reliable way to persist state across reloads.
|
* redux-persist provides an easy and reliable way to persist state across reloads.
|
||||||
@ -101,7 +107,9 @@ export const store = configureStore({
|
|||||||
getDefaultMiddleware({
|
getDefaultMiddleware({
|
||||||
immutableCheck: false,
|
immutableCheck: false,
|
||||||
serializableCheck: false,
|
serializableCheck: false,
|
||||||
}).concat(dynamicMiddlewares),
|
})
|
||||||
|
.concat(dynamicMiddlewares)
|
||||||
|
.prepend(listenerMiddleware.middleware),
|
||||||
devTools: {
|
devTools: {
|
||||||
// Uncommenting these very rapidly called actions makes the redux dev tools output much more readable
|
// Uncommenting these very rapidly called actions makes the redux dev tools output much more readable
|
||||||
actionsDenylist: [
|
actionsDenylist: [
|
||||||
@ -120,4 +128,5 @@ export const store = configureStore({
|
|||||||
|
|
||||||
export type AppGetState = typeof store.getState;
|
export type AppGetState = typeof store.getState;
|
||||||
export type RootState = ReturnType<typeof store.getState>;
|
export type RootState = ReturnType<typeof store.getState>;
|
||||||
|
export type AppThunkDispatch = ThunkDispatch<RootState, any, AnyAction>;
|
||||||
export type AppDispatch = typeof store.dispatch;
|
export type AppDispatch = typeof store.dispatch;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
||||||
import { AppDispatch, RootState } from 'app/store/store';
|
import { AppDispatch, AppThunkDispatch, RootState } from 'app/store/store';
|
||||||
|
|
||||||
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
||||||
export const useAppDispatch: () => AppDispatch = useDispatch;
|
export const useAppDispatch = () => useDispatch<AppThunkDispatch>();
|
||||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { GalleryCategory } from 'features/gallery/store/gallerySlice';
|
import { GalleryCategory } from 'features/gallery/store/gallerySlice';
|
||||||
|
import { SelectedImage } from 'features/parameters/store/actions';
|
||||||
import { FacetoolType } from 'features/parameters/store/postprocessingSlice';
|
import { FacetoolType } from 'features/parameters/store/postprocessingSlice';
|
||||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||||
import { IRect } from 'konva/lib/types';
|
import { IRect } from 'konva/lib/types';
|
||||||
@ -126,6 +127,14 @@ export type Image = {
|
|||||||
metadata: ImageResponseMetadata;
|
metadata: ImageResponseMetadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isInvokeAIImage = (obj: Image | SelectedImage): obj is Image => {
|
||||||
|
if ('url' in obj && 'thumbnail' in obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types related to the system status.
|
* Types related to the system status.
|
||||||
*/
|
*/
|
||||||
|
@ -29,7 +29,6 @@ import {
|
|||||||
isCanvasBaseImage,
|
isCanvasBaseImage,
|
||||||
isCanvasMaskLine,
|
isCanvasMaskLine,
|
||||||
} from './canvasTypes';
|
} from './canvasTypes';
|
||||||
import { invocationComplete } from 'services/events/actions';
|
|
||||||
|
|
||||||
export const initialLayerState: CanvasLayerState = {
|
export const initialLayerState: CanvasLayerState = {
|
||||||
objects: [],
|
objects: [],
|
||||||
@ -816,11 +815,6 @@ export const canvasSlice = createSlice({
|
|||||||
state.isTransformingBoundingBox = false;
|
state.isTransformingBoundingBox = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
extraReducers(builder) {
|
|
||||||
builder.addCase(invocationComplete, (state, action) => {
|
|
||||||
//
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
|
@ -22,7 +22,7 @@ import { setIsLightboxOpen } from 'features/lightbox/store/lightboxSlice';
|
|||||||
import FaceRestoreSettings from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreSettings';
|
import FaceRestoreSettings from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreSettings';
|
||||||
import UpscaleSettings from 'features/parameters/components/AdvancedParameters/Upscale/UpscaleSettings';
|
import UpscaleSettings from 'features/parameters/components/AdvancedParameters/Upscale/UpscaleSettings';
|
||||||
import {
|
import {
|
||||||
initialImageSelected,
|
initialImageChanged,
|
||||||
setAllParameters,
|
setAllParameters,
|
||||||
// setInitialImage,
|
// setInitialImage,
|
||||||
setSeed,
|
setSeed,
|
||||||
@ -68,6 +68,7 @@ import { useGetUrl } from 'common/util/getUrl';
|
|||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
import { imageDeleted } from 'services/thunks/image';
|
import { imageDeleted } from 'services/thunks/image';
|
||||||
import { useParameters } from 'features/parameters/hooks/useParameters';
|
import { useParameters } from 'features/parameters/hooks/useParameters';
|
||||||
|
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||||
|
|
||||||
const currentImageButtonsSelector = createSelector(
|
const currentImageButtonsSelector = createSelector(
|
||||||
[
|
[
|
||||||
@ -264,8 +265,8 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
useHotkeys('p', handleUsePrompt, [image]);
|
useHotkeys('p', handleUsePrompt, [image]);
|
||||||
|
|
||||||
const handleSendToImageToImage = useCallback(() => {
|
const handleSendToImageToImage = useCallback(() => {
|
||||||
sendToImageToImage(image);
|
dispatch(initialImageSelected(image));
|
||||||
}, [image, sendToImageToImage]);
|
}, [dispatch, image]);
|
||||||
|
|
||||||
useHotkeys('shift+i', handleSendToImageToImage, [image]);
|
useHotkeys('shift+i', handleSendToImageToImage, [image]);
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import CurrentImageFallback from './CurrentImageFallback';
|
|||||||
import ImageMetadataViewer from './ImageMetaDataViewer/ImageMetadataViewer';
|
import ImageMetadataViewer from './ImageMetaDataViewer/ImageMetadataViewer';
|
||||||
import NextPrevImageButtons from './NextPrevImageButtons';
|
import NextPrevImageButtons from './NextPrevImageButtons';
|
||||||
import CurrentImageHidden from './CurrentImageHidden';
|
import CurrentImageHidden from './CurrentImageHidden';
|
||||||
import { memo } from 'react';
|
import { DragEvent, memo, useCallback } from 'react';
|
||||||
|
|
||||||
export const imagesSelector = createSelector(
|
export const imagesSelector = createSelector(
|
||||||
[uiSelector, selectedImageSelector, systemSelector],
|
[uiSelector, selectedImageSelector, systemSelector],
|
||||||
@ -36,6 +36,18 @@ const CurrentImagePreview = () => {
|
|||||||
useAppSelector(imagesSelector);
|
useAppSelector(imagesSelector);
|
||||||
const { getUrl } = useGetUrl();
|
const { getUrl } = useGetUrl();
|
||||||
|
|
||||||
|
const handleDragStart = useCallback(
|
||||||
|
(e: DragEvent<HTMLDivElement>) => {
|
||||||
|
if (!image) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.dataTransfer.setData('invokeai/imageName', image.name);
|
||||||
|
e.dataTransfer.setData('invokeai/imageType', image.type);
|
||||||
|
e.dataTransfer.effectAllowed = 'move';
|
||||||
|
},
|
||||||
|
[image]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
||||||
@ -48,6 +60,7 @@ const CurrentImagePreview = () => {
|
|||||||
>
|
>
|
||||||
{image && (
|
{image && (
|
||||||
<Image
|
<Image
|
||||||
|
onDragStart={handleDragStart}
|
||||||
src={shouldHidePreview ? undefined : getUrl(image.url)}
|
src={shouldHidePreview ? undefined : getUrl(image.url)}
|
||||||
width={image.metadata.width}
|
width={image.metadata.width}
|
||||||
height={image.metadata.height}
|
height={image.metadata.height}
|
||||||
|
@ -134,7 +134,6 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
|
|
||||||
const handleDragStart = useCallback(
|
const handleDragStart = useCallback(
|
||||||
(e: DragEvent<HTMLDivElement>) => {
|
(e: DragEvent<HTMLDivElement>) => {
|
||||||
console.log('dragging');
|
|
||||||
e.dataTransfer.setData('invokeai/imageName', image.name);
|
e.dataTransfer.setData('invokeai/imageName', image.name);
|
||||||
e.dataTransfer.setData('invokeai/imageType', image.type);
|
e.dataTransfer.setData('invokeai/imageType', image.type);
|
||||||
e.dataTransfer.effectAllowed = 'move';
|
e.dataTransfer.effectAllowed = 'move';
|
||||||
|
@ -4,7 +4,7 @@ import { invocationComplete } from 'services/events/actions';
|
|||||||
import { isImageOutput } from 'services/types/guards';
|
import { isImageOutput } from 'services/types/guards';
|
||||||
import { deserializeImageResponse } from 'services/util/deserializeImageResponse';
|
import { deserializeImageResponse } from 'services/util/deserializeImageResponse';
|
||||||
import { imageUploaded } from 'services/thunks/image';
|
import { imageUploaded } from 'services/thunks/image';
|
||||||
import { SelectedImage } from 'features/parameters/store/generationSlice';
|
import { Image } from 'app/types/invokeai';
|
||||||
|
|
||||||
type GalleryImageObjectFitType = 'contain' | 'cover';
|
type GalleryImageObjectFitType = 'contain' | 'cover';
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ export interface GalleryState {
|
|||||||
/**
|
/**
|
||||||
* The selected image
|
* The selected image
|
||||||
*/
|
*/
|
||||||
selectedImage?: SelectedImage;
|
selectedImage?: Image;
|
||||||
galleryImageMinimumWidth: number;
|
galleryImageMinimumWidth: number;
|
||||||
galleryImageObjectFit: GalleryImageObjectFitType;
|
galleryImageObjectFit: GalleryImageObjectFitType;
|
||||||
shouldAutoSwitchToNewImages: boolean;
|
shouldAutoSwitchToNewImages: boolean;
|
||||||
@ -22,7 +22,6 @@ export interface GalleryState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const initialState: GalleryState = {
|
const initialState: GalleryState = {
|
||||||
selectedImage: undefined,
|
|
||||||
galleryImageMinimumWidth: 64,
|
galleryImageMinimumWidth: 64,
|
||||||
galleryImageObjectFit: 'cover',
|
galleryImageObjectFit: 'cover',
|
||||||
shouldAutoSwitchToNewImages: true,
|
shouldAutoSwitchToNewImages: true,
|
||||||
@ -35,10 +34,7 @@ export const gallerySlice = createSlice({
|
|||||||
name: 'gallery',
|
name: 'gallery',
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
imageSelected: (
|
imageSelected: (state, action: PayloadAction<Image | undefined>) => {
|
||||||
state,
|
|
||||||
action: PayloadAction<SelectedImage | undefined>
|
|
||||||
) => {
|
|
||||||
state.selectedImage = action.payload;
|
state.selectedImage = action.payload;
|
||||||
// TODO: if the user selects an image, disable the auto switch?
|
// TODO: if the user selects an image, disable the auto switch?
|
||||||
// state.shouldAutoSwitchToNewImages = false;
|
// state.shouldAutoSwitchToNewImages = false;
|
||||||
@ -84,16 +80,6 @@ export const gallerySlice = createSlice({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload Image - FULFILLED
|
|
||||||
*/
|
|
||||||
builder.addCase(imageUploaded.fulfilled, (state, action) => {
|
|
||||||
const { response } = action.payload;
|
|
||||||
|
|
||||||
const uploadedImage = deserializeImageResponse(response);
|
|
||||||
state.selectedImage = { name: uploadedImage.name, type: 'uploads' };
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -73,43 +73,43 @@ const resultsSlice = createSlice({
|
|||||||
state.isLoading = false;
|
state.isLoading = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Invocation Complete
|
// * Invocation Complete
|
||||||
*/
|
// */
|
||||||
builder.addCase(invocationComplete, (state, action) => {
|
// builder.addCase(invocationComplete, (state, action) => {
|
||||||
const { data, shouldFetchImages } = action.payload;
|
// const { data, shouldFetchImages } = action.payload;
|
||||||
const { result, node, graph_execution_state_id } = data;
|
// const { result, node, graph_execution_state_id } = data;
|
||||||
|
|
||||||
if (isImageOutput(result)) {
|
// if (isImageOutput(result)) {
|
||||||
const name = result.image.image_name;
|
// const name = result.image.image_name;
|
||||||
const type = result.image.image_type;
|
// const type = result.image.image_type;
|
||||||
|
|
||||||
// if we need to refetch, set URLs to placeholder for now
|
// // if we need to refetch, set URLs to placeholder for now
|
||||||
const { url, thumbnail } = shouldFetchImages
|
// const { url, thumbnail } = shouldFetchImages
|
||||||
? { url: '', thumbnail: '' }
|
// ? { url: '', thumbnail: '' }
|
||||||
: buildImageUrls(type, name);
|
// : buildImageUrls(type, name);
|
||||||
|
|
||||||
const timestamp = extractTimestampFromImageName(name);
|
// const timestamp = extractTimestampFromImageName(name);
|
||||||
|
|
||||||
const image: Image = {
|
// const image: Image = {
|
||||||
name,
|
// name,
|
||||||
type,
|
// type,
|
||||||
url,
|
// url,
|
||||||
thumbnail,
|
// thumbnail,
|
||||||
metadata: {
|
// metadata: {
|
||||||
created: timestamp,
|
// created: timestamp,
|
||||||
width: result.width,
|
// width: result.width,
|
||||||
height: result.height,
|
// height: result.height,
|
||||||
invokeai: {
|
// invokeai: {
|
||||||
session_id: graph_execution_state_id,
|
// session_id: graph_execution_state_id,
|
||||||
...(node ? { node } : {}),
|
// ...(node ? { node } : {}),
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
|
|
||||||
resultsAdapter.setOne(state, image);
|
// resultsAdapter.setOne(state, image);
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image Received - FULFILLED
|
* Image Received - FULFILLED
|
||||||
|
@ -35,7 +35,7 @@ const uploadsSlice = createSlice({
|
|||||||
name: 'uploads',
|
name: 'uploads',
|
||||||
initialState: initialUploadsState,
|
initialState: initialUploadsState,
|
||||||
reducers: {
|
reducers: {
|
||||||
uploadAdded: uploadsAdapter.addOne,
|
uploadAdded: uploadsAdapter.upsertOne,
|
||||||
},
|
},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
/**
|
/**
|
||||||
@ -61,17 +61,6 @@ const uploadsSlice = createSlice({
|
|||||||
state.isLoading = false;
|
state.isLoading = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload Image - FULFILLED
|
|
||||||
*/
|
|
||||||
builder.addCase(imageUploaded.fulfilled, (state, action) => {
|
|
||||||
const { location, response } = action.payload;
|
|
||||||
|
|
||||||
const uploadedImage = deserializeImageResponse(response);
|
|
||||||
|
|
||||||
uploadsAdapter.setOne(state, uploadedImage);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete Image - FULFILLED
|
* Delete Image - FULFILLED
|
||||||
*/
|
*/
|
||||||
|
@ -5,9 +5,9 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|||||||
import SelectImagePlaceholder from 'common/components/SelectImagePlaceholder';
|
import SelectImagePlaceholder from 'common/components/SelectImagePlaceholder';
|
||||||
import { useGetUrl } from 'common/util/getUrl';
|
import { useGetUrl } from 'common/util/getUrl';
|
||||||
import useGetImageByNameAndType from 'features/gallery/hooks/useGetImageByName';
|
import useGetImageByNameAndType from 'features/gallery/hooks/useGetImageByName';
|
||||||
import {
|
import generationSlice, {
|
||||||
clearInitialImage,
|
clearInitialImage,
|
||||||
initialImageSelected,
|
initialImageChanged,
|
||||||
} from 'features/parameters/store/generationSlice';
|
} from 'features/parameters/store/generationSlice';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
@ -15,23 +15,26 @@ import { DragEvent, useCallback, useState } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ImageType } from 'services/api';
|
import { ImageType } from 'services/api';
|
||||||
import ImageToImageOverlay from 'common/components/ImageToImageOverlay';
|
import ImageToImageOverlay from 'common/components/ImageToImageOverlay';
|
||||||
import { initialImageSelector } from 'features/parameters/store/generationSelectors';
|
import {
|
||||||
|
generationSelector,
|
||||||
|
initialImageSelector,
|
||||||
|
} from 'features/parameters/store/generationSelectors';
|
||||||
|
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
[initialImageSelector],
|
[generationSelector],
|
||||||
(initialImage) => {
|
(generation) => {
|
||||||
|
const { initialImage, isImageToImageEnabled } = generation;
|
||||||
return {
|
return {
|
||||||
initialImage,
|
initialImage,
|
||||||
|
isImageToImageEnabled,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{ memoizeOptions: { resultEqualityCheck: isEqual } }
|
{ memoizeOptions: { resultEqualityCheck: isEqual } }
|
||||||
);
|
);
|
||||||
|
|
||||||
const InitialImagePreview = () => {
|
const InitialImagePreview = () => {
|
||||||
const isImageToImageEnabled = useAppSelector(
|
const { initialImage, isImageToImageEnabled } = useAppSelector(selector);
|
||||||
(state: RootState) => state.generation.isImageToImageEnabled
|
|
||||||
);
|
|
||||||
const { initialImage } = useAppSelector(selector);
|
|
||||||
const { getUrl } = useGetUrl();
|
const { getUrl } = useGetUrl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -55,22 +58,13 @@ const InitialImagePreview = () => {
|
|||||||
const handleDrop = useCallback(
|
const handleDrop = useCallback(
|
||||||
(e: DragEvent<HTMLDivElement>) => {
|
(e: DragEvent<HTMLDivElement>) => {
|
||||||
setIsLoaded(false);
|
setIsLoaded(false);
|
||||||
|
|
||||||
const name = e.dataTransfer.getData('invokeai/imageName');
|
const name = e.dataTransfer.getData('invokeai/imageName');
|
||||||
const type = e.dataTransfer.getData('invokeai/imageType') as ImageType;
|
const type = e.dataTransfer.getData('invokeai/imageType') as ImageType;
|
||||||
|
|
||||||
if (!name || !type) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const image = getImageByNameAndType(name, type);
|
|
||||||
|
|
||||||
if (!image) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(initialImageSelected({ name, type }));
|
dispatch(initialImageSelected({ name, type }));
|
||||||
},
|
},
|
||||||
[getImageByNameAndType, dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -4,9 +4,11 @@ import { isFinite, isString } from 'lodash-es';
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import useSetBothPrompts from './usePrompt';
|
import useSetBothPrompts from './usePrompt';
|
||||||
import { initialImageSelected, setSeed } from '../store/generationSlice';
|
import { initialImageChanged, setSeed } from '../store/generationSlice';
|
||||||
import { isImage, isImageField } from 'services/types/guards';
|
import { isImage, isImageField } from 'services/types/guards';
|
||||||
import { NUMPY_RAND_MAX } from 'app/constants';
|
import { NUMPY_RAND_MAX } from 'app/constants';
|
||||||
|
import { initialImageSelected } from '../store/actions';
|
||||||
|
import { Image } from 'app/types/invokeai';
|
||||||
|
|
||||||
export const useParameters = () => {
|
export const useParameters = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -86,7 +88,7 @@ export const useParameters = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
initialImageSelected({ name: image.image_name, type: image.image_type })
|
initialImageChanged({ name: image.image_name, type: image.image_type })
|
||||||
);
|
);
|
||||||
toast({
|
toast({
|
||||||
title: t('toast.initialImageSet'),
|
title: t('toast.initialImageSet'),
|
||||||
@ -102,27 +104,10 @@ export const useParameters = () => {
|
|||||||
* Sets image as initial image with toast
|
* Sets image as initial image with toast
|
||||||
*/
|
*/
|
||||||
const sendToImageToImage = useCallback(
|
const sendToImageToImage = useCallback(
|
||||||
(image: unknown) => {
|
(image: Image) => {
|
||||||
if (!isImage(image)) {
|
|
||||||
toast({
|
|
||||||
title: t('toast.imageNotLoaded'),
|
|
||||||
description: t('toast.imageNotLoadedDesc'),
|
|
||||||
status: 'warning',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(initialImageSelected({ name: image.name, type: image.type }));
|
dispatch(initialImageSelected({ name: image.name, type: image.type }));
|
||||||
toast({
|
|
||||||
title: t('toast.sentToImageToImage'),
|
|
||||||
status: 'info',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[t, toast, dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
return { recallPrompt, recallSeed, recallInitialImage, sendToImageToImage };
|
return { recallPrompt, recallSeed, recallInitialImage, sendToImageToImage };
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
import { createAction } from '@reduxjs/toolkit';
|
||||||
|
import { Image } from 'app/types/invokeai';
|
||||||
|
import { ImageType } from 'services/api';
|
||||||
|
|
||||||
|
export type SelectedImage = {
|
||||||
|
name: string;
|
||||||
|
type: ImageType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initialImageSelected = createAction<
|
||||||
|
Image | SelectedImage | undefined
|
||||||
|
>('generation/initialImageSelected');
|
@ -7,17 +7,12 @@ import { seedWeightsToString } from 'common/util/seedWeightPairs';
|
|||||||
import { clamp } from 'lodash-es';
|
import { clamp } from 'lodash-es';
|
||||||
import { ImageField, ImageType } from 'services/api';
|
import { ImageField, ImageType } from 'services/api';
|
||||||
|
|
||||||
export type SelectedImage = {
|
|
||||||
name: string;
|
|
||||||
type: ImageType;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface GenerationState {
|
export interface GenerationState {
|
||||||
cfgScale: number;
|
cfgScale: number;
|
||||||
height: number;
|
height: number;
|
||||||
img2imgStrength: number;
|
img2imgStrength: number;
|
||||||
infillMethod: string;
|
infillMethod: string;
|
||||||
initialImage?: SelectedImage; // can be an Image or url
|
initialImage?: InvokeAI.Image; // can be an Image or url
|
||||||
iterations: number;
|
iterations: number;
|
||||||
maskPath: string;
|
maskPath: string;
|
||||||
perlin: number;
|
perlin: number;
|
||||||
@ -351,7 +346,7 @@ export const generationSlice = createSlice({
|
|||||||
setVerticalSymmetrySteps: (state, action: PayloadAction<number>) => {
|
setVerticalSymmetrySteps: (state, action: PayloadAction<number>) => {
|
||||||
state.verticalSymmetrySteps = action.payload;
|
state.verticalSymmetrySteps = action.payload;
|
||||||
},
|
},
|
||||||
initialImageSelected: (state, action: PayloadAction<SelectedImage>) => {
|
initialImageChanged: (state, action: PayloadAction<InvokeAI.Image>) => {
|
||||||
state.initialImage = action.payload;
|
state.initialImage = action.payload;
|
||||||
state.isImageToImageEnabled = true;
|
state.isImageToImageEnabled = true;
|
||||||
},
|
},
|
||||||
@ -399,7 +394,7 @@ export const {
|
|||||||
setShouldUseSymmetry,
|
setShouldUseSymmetry,
|
||||||
setHorizontalSymmetrySteps,
|
setHorizontalSymmetrySteps,
|
||||||
setVerticalSymmetrySteps,
|
setVerticalSymmetrySteps,
|
||||||
initialImageSelected,
|
initialImageChanged,
|
||||||
isImageToImageEnabledChanged,
|
isImageToImageEnabledChanged,
|
||||||
} = generationSlice.actions;
|
} = generationSlice.actions;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
} from 'services/events/actions';
|
} from 'services/events/actions';
|
||||||
|
|
||||||
import { ProgressImage } from 'services/events/types';
|
import { ProgressImage } from 'services/events/types';
|
||||||
import { initialImageSelected } from 'features/parameters/store/generationSlice';
|
import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
||||||
import { makeToast } from '../hooks/useToastWatcher';
|
import { makeToast } from '../hooks/useToastWatcher';
|
||||||
import { sessionCanceled, sessionInvoked } from 'services/thunks/session';
|
import { sessionCanceled, sessionInvoked } from 'services/thunks/session';
|
||||||
import { receivedModels } from 'services/thunks/model';
|
import { receivedModels } from 'services/thunks/model';
|
||||||
@ -434,13 +434,6 @@ export const systemSlice = createSlice({
|
|||||||
state.statusTranslationKey = 'common.statusConnected';
|
state.statusTranslationKey = 'common.statusConnected';
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Initial Image Selected
|
|
||||||
*/
|
|
||||||
builder.addCase(initialImageSelected, (state) => {
|
|
||||||
state.toastQueue.push(makeToast(t('toast.sentToImageToImage')));
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Received available models from the backend
|
* Received available models from the backend
|
||||||
*/
|
*/
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Box, BoxProps, Grid, GridItem } from '@chakra-ui/react';
|
import { Box, BoxProps, Grid, GridItem } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { initialImageSelected } from 'features/parameters/store/generationSlice';
|
import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
||||||
import {
|
import {
|
||||||
activeTabNameSelector,
|
activeTabNameSelector,
|
||||||
uiSelector,
|
uiSelector,
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Outputs an image from a base 64 data URL.
|
* Outputs an image from a data URL.
|
||||||
*/
|
*/
|
||||||
export type DataURLToImageInvocation = {
|
export type DataURLToImageInvocation = {
|
||||||
/**
|
/**
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export const $DataURLToImageInvocation = {
|
export const $DataURLToImageInvocation = {
|
||||||
description: `Outputs an image from a base 64 data URL.`,
|
description: `Outputs an image from a data URL.`,
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { createAction } from '@reduxjs/toolkit';
|
import { AnyAction, createAction } from '@reduxjs/toolkit';
|
||||||
import {
|
import {
|
||||||
GeneratorProgressEvent,
|
GeneratorProgressEvent,
|
||||||
GraphExecutionStateCompleteEvent,
|
GraphExecutionStateCompleteEvent,
|
||||||
|
@ -5,20 +5,14 @@ import {
|
|||||||
ClientToServerEvents,
|
ClientToServerEvents,
|
||||||
ServerToClientEvents,
|
ServerToClientEvents,
|
||||||
} from 'services/events/types';
|
} from 'services/events/types';
|
||||||
import {
|
import { socketSubscribed, socketUnsubscribed } from './actions';
|
||||||
invocationComplete,
|
import { AppThunkDispatch, RootState } from 'app/store/store';
|
||||||
socketSubscribed,
|
|
||||||
socketUnsubscribed,
|
|
||||||
} from './actions';
|
|
||||||
import { AppDispatch, RootState } from 'app/store/store';
|
|
||||||
import { getTimestamp } from 'common/util/getTimestamp';
|
import { getTimestamp } from 'common/util/getTimestamp';
|
||||||
import {
|
import {
|
||||||
sessionInvoked,
|
sessionInvoked,
|
||||||
isFulfilledSessionCreatedAction,
|
isFulfilledSessionCreatedAction,
|
||||||
} from 'services/thunks/session';
|
} from 'services/thunks/session';
|
||||||
import { OpenAPI } from 'services/api';
|
import { OpenAPI } from 'services/api';
|
||||||
import { isImageOutput } from 'services/types/guards';
|
|
||||||
import { imageReceived, thumbnailReceived } from 'services/thunks/image';
|
|
||||||
import { setEventListeners } from 'services/events/util/setEventListeners';
|
import { setEventListeners } from 'services/events/util/setEventListeners';
|
||||||
import { log } from 'app/logging/useLogger';
|
import { log } from 'app/logging/useLogger';
|
||||||
|
|
||||||
@ -56,13 +50,15 @@ export const socketMiddleware = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const middleware: Middleware =
|
const middleware: Middleware =
|
||||||
(store: MiddlewareAPI<AppDispatch, RootState>) => (next) => (action) => {
|
(storeApi: MiddlewareAPI<AppThunkDispatch, RootState>) =>
|
||||||
const { dispatch, getState } = store;
|
(next) =>
|
||||||
|
(action) => {
|
||||||
|
const { dispatch, getState } = storeApi;
|
||||||
|
|
||||||
// Set listeners for `connect` and `disconnect` events once
|
// Set listeners for `connect` and `disconnect` events once
|
||||||
// Must happen in middleware to get access to `dispatch`
|
// Must happen in middleware to get access to `dispatch`
|
||||||
if (!areListenersSet) {
|
if (!areListenersSet) {
|
||||||
setEventListeners({ store, socket, log: socketioLog });
|
setEventListeners({ storeApi, socket, log: socketioLog });
|
||||||
|
|
||||||
areListenersSet = true;
|
areListenersSet = true;
|
||||||
|
|
||||||
@ -107,26 +103,6 @@ export const socketMiddleware = () => {
|
|||||||
dispatch(sessionInvoked({ sessionId }));
|
dispatch(sessionInvoked({ sessionId }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (invocationComplete.match(action)) {
|
|
||||||
const { config } = getState();
|
|
||||||
|
|
||||||
if (config.shouldFetchImages) {
|
|
||||||
const { result } = action.payload.data;
|
|
||||||
if (isImageOutput(result)) {
|
|
||||||
const imageName = result.image.image_name;
|
|
||||||
const imageType = result.image.image_type;
|
|
||||||
|
|
||||||
dispatch(imageReceived({ imageName, imageType }));
|
|
||||||
dispatch(
|
|
||||||
thumbnailReceived({
|
|
||||||
thumbnailName: imageName,
|
|
||||||
thumbnailType: imageType,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next(action);
|
next(action);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,13 +27,13 @@ import { addToast } from '../../../features/system/store/systemSlice';
|
|||||||
|
|
||||||
type SetEventListenersArg = {
|
type SetEventListenersArg = {
|
||||||
socket: Socket<ServerToClientEvents, ClientToServerEvents>;
|
socket: Socket<ServerToClientEvents, ClientToServerEvents>;
|
||||||
store: MiddlewareAPI<AppDispatch, RootState>;
|
storeApi: MiddlewareAPI<AppDispatch, RootState>;
|
||||||
log: Logger<JsonObject>;
|
log: Logger<JsonObject>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setEventListeners = (arg: SetEventListenersArg) => {
|
export const setEventListeners = (arg: SetEventListenersArg) => {
|
||||||
const { socket, store, log } = arg;
|
const { socket, storeApi, log } = arg;
|
||||||
const { dispatch, getState } = store;
|
const { dispatch, getState } = storeApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect
|
* Connect
|
||||||
|
@ -20,7 +20,12 @@
|
|||||||
"*": ["./src/*"]
|
"*": ["./src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "src/**/*.tsx", "*.d.ts"],
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"*.d.ts",
|
||||||
|
"src/app/store/middleware/listenerMiddleware"
|
||||||
|
],
|
||||||
"exclude": ["src/services/fixtures/*", "node_modules", "dist"],
|
"exclude": ["src/services/fixtures/*", "node_modules", "dist"],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
}
|
}
|
||||||
|
@ -1836,7 +1836,7 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249"
|
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249"
|
||||||
integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==
|
integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==
|
||||||
|
|
||||||
"@types/hoist-non-react-statics@^3.3.1":
|
"@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1":
|
||||||
version "3.3.1"
|
version "3.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
||||||
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
|
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
|
||||||
@ -1907,6 +1907,16 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-redux@^7.1.25":
|
||||||
|
version "7.1.25"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.25.tgz#de841631205b24f9dfb4967dd4a7901e048f9a88"
|
||||||
|
integrity sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==
|
||||||
|
dependencies:
|
||||||
|
"@types/hoist-non-react-statics" "^3.3.0"
|
||||||
|
"@types/react" "*"
|
||||||
|
hoist-non-react-statics "^3.3.0"
|
||||||
|
redux "^4.0.0"
|
||||||
|
|
||||||
"@types/react-transition-group@^4.4.5":
|
"@types/react-transition-group@^4.4.5":
|
||||||
version "4.4.5"
|
version "4.4.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416"
|
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416"
|
||||||
@ -5687,7 +5697,7 @@ redux-thunk@^2.4.2:
|
|||||||
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.2.tgz#b9d05d11994b99f7a91ea223e8b04cf0afa5ef3b"
|
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.2.tgz#b9d05d11994b99f7a91ea223e8b04cf0afa5ef3b"
|
||||||
integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==
|
integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==
|
||||||
|
|
||||||
redux@^4.2.1:
|
redux@^4.0.0, redux@^4.2.1:
|
||||||
version "4.2.1"
|
version "4.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
|
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
|
||||||
integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
|
integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
|
||||||
|
Loading…
Reference in New Issue
Block a user