ui: pay back tech debt (#3896)

## What type of PR is this? (check all applicable)

- [x] Refactor
- [ ] Feature
- [ ] Bug Fix
- [ ] Optimization
- [ ] Documentation Update
- [ ] Community Node Submission


## Have you discussed this change with the InvokeAI team?
- [ ] Yes
- [x] No, because: n/a

      
## Have you updated all relevant documentation?
- [ ] Yes
- [x] No n/a


## Description

Big cleanup:
- improve & simplify the app logging
- resolve all TS issues
- resolve all circular dependencies
- fix all lint/format issues

## QA Instructions, Screenshots, Recordings

`yarn lint` passes:


![image](https://github.com/invoke-ai/InvokeAI/assets/4822129/7b763922-f00c-4b17-be23-2432da50f816)
<!-- 
Please provide steps on how to test changes, any hardware or 
software specifications as well as any other pertinent information. 
-->

## Added/updated tests?

- [ ] Yes
- [x] No : n/a

## [optional] Are there any post deployment tasks we need to perform?

bask in the glory of what *should* be a fully-passing frontend lint on
this PR
This commit is contained in:
blessedcoolant 2023-07-23 13:57:43 +12:00 committed by GitHub
commit 075f9b3a7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
227 changed files with 1508 additions and 2916 deletions

View File

@ -5,6 +5,10 @@ patches/
stats.html stats.html
index.html index.html
.yarn/ .yarn/
.yalc/
*.scss *.scss
src/services/api/ src/services/api/
src/services/fixtures/* src/services/fixtures/*
docs/
static/
src/theme/css/overlayscrollbars.css

View File

@ -69,6 +69,7 @@
"@mantine/core": "^6.0.14", "@mantine/core": "^6.0.14",
"@mantine/form": "^6.0.15", "@mantine/form": "^6.0.15",
"@mantine/hooks": "^6.0.14", "@mantine/hooks": "^6.0.14",
"@nanostores/react": "^0.7.1",
"@reduxjs/toolkit": "^1.9.5", "@reduxjs/toolkit": "^1.9.5",
"@roarr/browser-log-writer": "^1.1.5", "@roarr/browser-log-writer": "^1.1.5",
"chakra-ui-contextmenu": "^1.0.5", "chakra-ui-contextmenu": "^1.0.5",

View File

@ -10,7 +10,7 @@ async function main() {
); );
const types = await openapiTS(OPENAPI_URL, { const types = await openapiTS(OPENAPI_URL, {
exportType: true, exportType: true,
transform: (schemaObject, metadata) => { transform: (schemaObject) => {
if ('format' in schemaObject && schemaObject.format === 'binary') { if ('format' in schemaObject && schemaObject.format === 'binary') {
return schemaObject.nullable ? 'Blob | null' : 'Blob'; return schemaObject.nullable ? 'Blob | null' : 'Blob';
} }

View File

@ -14,6 +14,7 @@ import FloatingParametersPanelButtons from 'features/ui/components/FloatingParam
import InvokeTabs from 'features/ui/components/InvokeTabs'; import InvokeTabs from 'features/ui/components/InvokeTabs';
import ParametersDrawer from 'features/ui/components/ParametersDrawer'; import ParametersDrawer from 'features/ui/components/ParametersDrawer';
import i18n from 'i18n'; import i18n from 'i18n';
import { size } from 'lodash-es';
import { ReactNode, memo, useEffect } from 'react'; import { ReactNode, memo, useEffect } from 'react';
import UpdateImageBoardModal from '../../features/gallery/components/Boards/UpdateImageBoardModal'; import UpdateImageBoardModal from '../../features/gallery/components/Boards/UpdateImageBoardModal';
import GlobalHotkeys from './GlobalHotkeys'; import GlobalHotkeys from './GlobalHotkeys';
@ -29,8 +30,7 @@ interface Props {
const App = ({ config = DEFAULT_CONFIG, headerComponent }: Props) => { const App = ({ config = DEFAULT_CONFIG, headerComponent }: Props) => {
const language = useAppSelector(languageSelector); const language = useAppSelector(languageSelector);
const log = useLogger(); const logger = useLogger();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
useEffect(() => { useEffect(() => {
@ -38,9 +38,11 @@ const App = ({ config = DEFAULT_CONFIG, headerComponent }: Props) => {
}, [language]); }, [language]);
useEffect(() => { useEffect(() => {
log.info({ namespace: 'App', data: config }, 'Received config'); if (size(config)) {
dispatch(configChanged(config)); logger.info({ namespace: 'App', config }, 'Received config');
}, [dispatch, config, log]); dispatch(configChanged(config));
}
}, [dispatch, config, logger]);
useEffect(() => { useEffect(() => {
dispatch(appStarted()); dispatch(appStarted());

View File

@ -27,7 +27,7 @@ const STYLES: ChakraProps['sx'] = {
const DragPreview = (props: OverlayDragImageProps) => { const DragPreview = (props: OverlayDragImageProps) => {
if (!props.dragData) { if (!props.dragData) {
return; return null;
} }
if (props.dragData.payloadType === 'IMAGE_DTO') { if (props.dragData.payloadType === 'IMAGE_DTO') {

View File

@ -39,7 +39,6 @@ const ImageDndContext = (props: ImageDndContextProps) => {
const handleDragEnd = useCallback( const handleDragEnd = useCallback(
(event: DragEndEvent) => { (event: DragEndEvent) => {
console.log('dragEnd', event.active.data.current); console.log('dragEnd', event.active.data.current);
const activeData = event.active.data.current;
const overData = event.over?.data.current; const overData = event.over?.data.current;
if (!activeDragData || !overData) { if (!activeDragData || !overData) {
return; return;

View File

@ -11,7 +11,7 @@ import {
useDraggable as useOriginalDraggable, useDraggable as useOriginalDraggable,
useDroppable as useOriginalDroppable, useDroppable as useOriginalDroppable,
} from '@dnd-kit/core'; } from '@dnd-kit/core';
import { BoardId } from 'features/gallery/store/gallerySlice'; import { BoardId } from 'features/gallery/store/types';
import { ImageDTO } from 'services/api/types'; import { ImageDTO } from 'services/api/types';
type BaseDropData = { type BaseDropData = {

View File

@ -1,28 +1,10 @@
import { useToast, UseToastOptions } from '@chakra-ui/react'; import { useToast } from '@chakra-ui/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { toastQueueSelector } from 'features/system/store/systemSelectors'; import { toastQueueSelector } from 'features/system/store/systemSelectors';
import { addToast, clearToastQueue } from 'features/system/store/systemSlice'; import { addToast, clearToastQueue } from 'features/system/store/systemSlice';
import { MakeToastArg, makeToast } from 'features/system/util/makeToast';
import { useCallback, useEffect } from 'react'; import { useCallback, useEffect } from 'react';
export type MakeToastArg = string | UseToastOptions;
/**
* Makes a toast from a string or a UseToastOptions object.
* If a string is passed, the toast will have the status 'info' and will be closable with a duration of 2500ms.
*/
export const makeToast = (arg: MakeToastArg): UseToastOptions => {
if (typeof arg === 'string') {
return {
title: arg,
status: 'info',
isClosable: true,
duration: 2500,
};
}
return { status: 'info', isClosable: true, duration: 2500, ...arg };
};
/** /**
* Logical component. Watches the toast queue and makes toasts when the queue is not empty. * Logical component. Watches the toast queue and makes toasts when the queue is not empty.
* @returns null * @returns null

View File

@ -1,66 +1,2 @@
// zod needs the array to be `as const` to infer the type correctly
import { SchedulerParam } from 'features/parameters/types/parameterSchemas';
// this is the source of the `SchedulerParam` type, which is generated by zod
export const SCHEDULER_NAMES_AS_CONST = [
'euler',
'deis',
'ddim',
'ddpm',
'dpmpp_2s',
'dpmpp_2m',
'dpmpp_2m_sde',
'dpmpp_sde',
'heun',
'kdpm_2',
'lms',
'pndm',
'unipc',
'euler_k',
'dpmpp_2s_k',
'dpmpp_2m_k',
'dpmpp_2m_sde_k',
'dpmpp_sde_k',
'heun_k',
'lms_k',
'euler_a',
'kdpm_2_a',
] as const;
export const DEFAULT_SCHEDULER_NAME = 'euler';
export const SCHEDULER_NAMES: SchedulerParam[] = [...SCHEDULER_NAMES_AS_CONST];
export const SCHEDULER_LABEL_MAP: Record<SchedulerParam, string> = {
euler: 'Euler',
deis: 'DEIS',
ddim: 'DDIM',
ddpm: 'DDPM',
dpmpp_sde: 'DPM++ SDE',
dpmpp_2s: 'DPM++ 2S',
dpmpp_2m: 'DPM++ 2M',
dpmpp_2m_sde: 'DPM++ 2M SDE',
heun: 'Heun',
kdpm_2: 'KDPM 2',
lms: 'LMS',
pndm: 'PNDM',
unipc: 'UniPC',
euler_k: 'Euler Karras',
dpmpp_sde_k: 'DPM++ SDE Karras',
dpmpp_2s_k: 'DPM++ 2S Karras',
dpmpp_2m_k: 'DPM++ 2M Karras',
dpmpp_2m_sde_k: 'DPM++ 2M SDE Karras',
heun_k: 'Heun Karras',
lms_k: 'LMS Karras',
euler_a: 'Euler Ancestral',
kdpm_2_a: 'KDPM 2 Ancestral',
};
export type Scheduler = (typeof SCHEDULER_NAMES)[number];
export const NUMPY_RAND_MIN = 0; export const NUMPY_RAND_MIN = 0;
export const NUMPY_RAND_MAX = 2147483647; export const NUMPY_RAND_MAX = 2147483647;
export const NODE_MIN_WIDTH = 250;

View File

@ -0,0 +1,46 @@
import { createLogWriter } from '@roarr/browser-log-writer';
import { atom } from 'nanostores';
import { Logger, ROARR, Roarr } from 'roarr';
ROARR.write = createLogWriter();
export const BASE_CONTEXT = {};
export const log = Roarr.child(BASE_CONTEXT);
export const $logger = atom<Logger>(Roarr.child(BASE_CONTEXT));
type LoggerNamespace =
| 'images'
| 'models'
| 'config'
| 'canvas'
| 'txt2img'
| 'img2img'
| 'nodes'
| 'system'
| 'socketio'
| 'session';
export const logger = (namespace: LoggerNamespace) =>
$logger.get().child({ namespace });
export const VALID_LOG_LEVELS = [
'trace',
'debug',
'info',
'warn',
'error',
'fatal',
] as const;
export type InvokeLogLevel = (typeof VALID_LOG_LEVELS)[number];
// Translate human-readable log levels to numbers, used for log filtering
export const LOG_LEVEL_MAP: Record<InvokeLogLevel, number> = {
trace: 10,
debug: 20,
info: 30,
warn: 40,
error: 50,
fatal: 60,
};

View File

@ -1,48 +1,19 @@
import { useStore } from '@nanostores/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { createLogWriter } from '@roarr/browser-log-writer';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { systemSelector } from 'features/system/store/systemSelectors'; import { systemSelector } from 'features/system/store/systemSelectors';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { LogLevelName, ROARR, Roarr } from 'roarr'; import { ROARR, Roarr } from 'roarr';
import { createLogWriter } from '@roarr/browser-log-writer'; import { $logger, BASE_CONTEXT, LOG_LEVEL_MAP } from './logger';
// Base logging context includes only the package name
const baseContext = { package: '@invoke-ai/invoke-ai-ui' };
// Create browser log writer
ROARR.write = createLogWriter();
// Module-scoped logger - can be imported and used anywhere
export let log = Roarr.child(baseContext);
// Translate human-readable log levels to numbers, used for log filtering
export const LOG_LEVEL_MAP: Record<LogLevelName, number> = {
trace: 10,
debug: 20,
info: 30,
warn: 40,
error: 50,
fatal: 60,
};
export const VALID_LOG_LEVELS = [
'trace',
'debug',
'info',
'warn',
'error',
'fatal',
] as const;
export type InvokeLogLevel = (typeof VALID_LOG_LEVELS)[number];
const selector = createSelector( const selector = createSelector(
systemSelector, systemSelector,
(system) => { (system) => {
const { app_version, consoleLogLevel, shouldLogToConsole } = system; const { consoleLogLevel, shouldLogToConsole } = system;
return { return {
version: app_version,
consoleLogLevel, consoleLogLevel,
shouldLogToConsole, shouldLogToConsole,
}; };
@ -55,8 +26,7 @@ const selector = createSelector(
); );
export const useLogger = () => { export const useLogger = () => {
const { version, consoleLogLevel, shouldLogToConsole } = const { consoleLogLevel, shouldLogToConsole } = useAppSelector(selector);
useAppSelector(selector);
// The provided Roarr browser log writer uses localStorage to config logging to console // The provided Roarr browser log writer uses localStorage to config logging to console
useEffect(() => { useEffect(() => {
@ -78,17 +48,16 @@ export const useLogger = () => {
// Update the module-scoped logger context as needed // Update the module-scoped logger context as needed
useEffect(() => { useEffect(() => {
// TODO: type this properly
//eslint-disable-next-line @typescript-eslint/no-explicit-any
const newContext: Record<string, any> = { const newContext: Record<string, any> = {
...baseContext, ...BASE_CONTEXT,
}; };
if (version) { $logger.set(Roarr.child(newContext));
newContext.version = version; }, []);
}
log = Roarr.child(newContext); const logger = useStore($logger);
}, [version]);
// Use the logger within components - no different than just importing it directly return logger;
return log;
}; };

View File

@ -12,7 +12,7 @@ import { defaultsDeep } from 'lodash-es';
import { UnserializeFunction } from 'redux-remember'; import { UnserializeFunction } from 'redux-remember';
const initialStates: { const initialStates: {
[key: string]: any; [key: string]: object; // TODO: type this properly
} = { } = {
canvas: initialCanvasState, canvas: initialCanvasState,
gallery: initialGalleryState, gallery: initialGalleryState,

View File

@ -8,10 +8,11 @@ import {
import type { AppDispatch, RootState } from '../../store'; import type { AppDispatch, RootState } from '../../store';
import { addCommitStagingAreaImageListener } from './listeners/addCommitStagingAreaImageListener'; import { addCommitStagingAreaImageListener } from './listeners/addCommitStagingAreaImageListener';
import { addFirstListImagesListener } from './listeners/addFirstListImagesListener.ts';
import { addAppConfigReceivedListener } from './listeners/appConfigReceived'; import { addAppConfigReceivedListener } from './listeners/appConfigReceived';
import { addAppStartedListener } from './listeners/appStarted'; import { addAppStartedListener } from './listeners/appStarted';
import { addBoardIdSelectedListener } from './listeners/boardIdSelected';
import { addDeleteBoardAndImagesFulfilledListener } from './listeners/boardAndImagesDeleted'; import { addDeleteBoardAndImagesFulfilledListener } from './listeners/boardAndImagesDeleted';
import { addBoardIdSelectedListener } from './listeners/boardIdSelected';
import { addCanvasCopiedToClipboardListener } from './listeners/canvasCopiedToClipboard'; import { addCanvasCopiedToClipboardListener } from './listeners/canvasCopiedToClipboard';
import { addCanvasDownloadedAsImageListener } from './listeners/canvasDownloadedAsImage'; import { addCanvasDownloadedAsImageListener } from './listeners/canvasDownloadedAsImage';
import { addCanvasMergedListener } from './listeners/canvasMerged'; import { addCanvasMergedListener } from './listeners/canvasMerged';
@ -34,10 +35,6 @@ import {
addImageRemovedFromBoardRejectedListener, addImageRemovedFromBoardRejectedListener,
} from './listeners/imageRemovedFromBoard'; } from './listeners/imageRemovedFromBoard';
import { addImageToDeleteSelectedListener } from './listeners/imageToDeleteSelected'; import { addImageToDeleteSelectedListener } from './listeners/imageToDeleteSelected';
import {
addImageUpdatedFulfilledListener,
addImageUpdatedRejectedListener,
} from './listeners/imageUpdated';
import { import {
addImageUploadedFulfilledListener, addImageUploadedFulfilledListener,
addImageUploadedRejectedListener, addImageUploadedRejectedListener,
@ -69,17 +66,15 @@ import { addGraphExecutionStateCompleteEventListener as addGraphExecutionStateCo
import { addInvocationCompleteEventListener as addInvocationCompleteListener } from './listeners/socketio/socketInvocationComplete'; import { addInvocationCompleteEventListener as addInvocationCompleteListener } from './listeners/socketio/socketInvocationComplete';
import { addInvocationErrorEventListener as addInvocationErrorListener } from './listeners/socketio/socketInvocationError'; import { addInvocationErrorEventListener as addInvocationErrorListener } from './listeners/socketio/socketInvocationError';
import { addInvocationStartedEventListener as addInvocationStartedListener } from './listeners/socketio/socketInvocationStarted'; import { addInvocationStartedEventListener as addInvocationStartedListener } from './listeners/socketio/socketInvocationStarted';
import { addModelLoadEventListener } from './listeners/socketio/socketModelLoad';
import { addSocketSubscribedEventListener as addSocketSubscribedListener } from './listeners/socketio/socketSubscribed'; import { addSocketSubscribedEventListener as addSocketSubscribedListener } from './listeners/socketio/socketSubscribed';
import { addSocketUnsubscribedEventListener as addSocketUnsubscribedListener } from './listeners/socketio/socketUnsubscribed'; import { addSocketUnsubscribedEventListener as addSocketUnsubscribedListener } from './listeners/socketio/socketUnsubscribed';
import { addStagingAreaImageSavedListener } from './listeners/stagingAreaImageSaved'; import { addStagingAreaImageSavedListener } from './listeners/stagingAreaImageSaved';
import { addUpscaleRequestedListener } from './listeners/upscaleRequested';
import { addUserInvokedCanvasListener } from './listeners/userInvokedCanvas'; import { addUserInvokedCanvasListener } from './listeners/userInvokedCanvas';
import { addUserInvokedImageToImageListener } from './listeners/userInvokedImageToImage'; import { addUserInvokedImageToImageListener } from './listeners/userInvokedImageToImage';
import { addUserInvokedNodesListener } from './listeners/userInvokedNodes'; import { addUserInvokedNodesListener } from './listeners/userInvokedNodes';
import { addUserInvokedTextToImageListener } from './listeners/userInvokedTextToImage'; import { addUserInvokedTextToImageListener } from './listeners/userInvokedTextToImage';
import { addModelLoadStartedEventListener } from './listeners/socketio/socketModelLoadStarted';
import { addModelLoadCompletedEventListener } from './listeners/socketio/socketModelLoadCompleted';
import { addUpscaleRequestedListener } from './listeners/upscaleRequested';
import { addFirstListImagesListener } from './listeners/addFirstListImagesListener.ts';
export const listenerMiddleware = createListenerMiddleware(); export const listenerMiddleware = createListenerMiddleware();
@ -109,10 +104,6 @@ export type AppListenerEffect = ListenerEffect<
addImageUploadedFulfilledListener(); addImageUploadedFulfilledListener();
addImageUploadedRejectedListener(); addImageUploadedRejectedListener();
// Image updated
addImageUpdatedFulfilledListener();
addImageUpdatedRejectedListener();
// Image selected // Image selected
addInitialImageSelectedListener(); addInitialImageSelectedListener();
@ -161,8 +152,7 @@ addSocketConnectedListener();
addSocketDisconnectedListener(); addSocketDisconnectedListener();
addSocketSubscribedListener(); addSocketSubscribedListener();
addSocketUnsubscribedListener(); addSocketUnsubscribedListener();
addModelLoadStartedEventListener(); addModelLoadEventListener();
addModelLoadCompletedEventListener();
// Session Created // Session Created
addSessionCreatedPendingListener(); addSessionCreatedPendingListener();

View File

@ -1,14 +1,13 @@
import { startAppListening } from '..'; import { logger } from 'app/logging/logger';
import { log } from 'app/logging/useLogger';
import { commitStagingAreaImage } from 'features/canvas/store/canvasSlice'; import { commitStagingAreaImage } from 'features/canvas/store/canvasSlice';
import { sessionCanceled } from 'services/api/thunks/session'; import { sessionCanceled } from 'services/api/thunks/session';
import { startAppListening } from '..';
const moduleLog = log.child({ namespace: 'canvas' });
export const addCommitStagingAreaImageListener = () => { export const addCommitStagingAreaImageListener = () => {
startAppListening({ startAppListening({
actionCreator: commitStagingAreaImage, actionCreator: commitStagingAreaImage,
effect: async (action, { dispatch, getState }) => { effect: async (action, { dispatch, getState }) => {
const log = logger('canvas');
const state = getState(); const state = getState();
const { sessionId: session_id, isProcessing } = state.system; const { sessionId: session_id, isProcessing } = state.system;
const canvasSessionId = action.payload; const canvasSessionId = action.payload;
@ -19,17 +18,15 @@ export const addCommitStagingAreaImageListener = () => {
} }
if (!canvasSessionId) { if (!canvasSessionId) {
moduleLog.debug('No canvas session, skipping cancel'); log.debug('No canvas session, skipping cancel');
return; return;
} }
if (canvasSessionId !== session_id) { if (canvasSessionId !== session_id) {
moduleLog.debug( log.debug(
{ {
data: { canvasSessionId,
canvasSessionId, session_id,
session_id,
},
}, },
'Canvas session does not match global session, skipping cancel' 'Canvas session does not match global session, skipping cancel'
); );

View File

@ -1,8 +1,6 @@
import { createAction } from '@reduxjs/toolkit'; import { createAction } from '@reduxjs/toolkit';
import { import { imageSelected } from 'features/gallery/store/gallerySlice';
IMAGE_CATEGORIES, import { IMAGE_CATEGORIES } from 'features/gallery/store/types';
imageSelected,
} from 'features/gallery/store/gallerySlice';
import { import {
ImageCache, ImageCache,
getListImagesUrl, getListImagesUrl,
@ -17,7 +15,7 @@ export const addFirstListImagesListener = () => {
matcher: imagesApi.endpoints.listImages.matchFulfilled, matcher: imagesApi.endpoints.listImages.matchFulfilled,
effect: async ( effect: async (
action, action,
{ getState, dispatch, unsubscribe, cancelActiveListeners } { dispatch, unsubscribe, cancelActiveListeners }
) => { ) => {
// Only run this listener on the first listImages request for no-board images // Only run this listener on the first listImages request for no-board images
if ( if (

View File

@ -6,10 +6,7 @@ export const appStarted = createAction('app/appStarted');
export const addAppStartedListener = () => { export const addAppStartedListener = () => {
startAppListening({ startAppListening({
actionCreator: appStarted, actionCreator: appStarted,
effect: async ( effect: async (action, { unsubscribe, cancelActiveListeners }) => {
action,
{ getState, dispatch, unsubscribe, cancelActiveListeners }
) => {
// this should only run once // this should only run once
cancelActiveListeners(); cancelActiveListeners();
unsubscribe(); unsubscribe();

View File

@ -1,6 +1,6 @@
import { resetCanvas } from 'features/canvas/store/canvasSlice'; import { resetCanvas } from 'features/canvas/store/canvasSlice';
import { controlNetReset } from 'features/controlNet/store/controlNetSlice'; import { controlNetReset } from 'features/controlNet/store/controlNetSlice';
import { getImageUsage } from 'features/imageDeletion/store/imageDeletionSlice'; import { getImageUsage } from 'features/imageDeletion/store/imageDeletionSelectors';
import { nodeEditorReset } from 'features/nodes/store/nodesSlice'; import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
import { clearInitialImage } from 'features/parameters/store/generationSlice'; import { clearInitialImage } from 'features/parameters/store/generationSlice';
import { startAppListening } from '..'; import { startAppListening } from '..';
@ -9,8 +9,8 @@ import { boardsApi } from '../../../../../services/api/endpoints/boards';
export const addDeleteBoardAndImagesFulfilledListener = () => { export const addDeleteBoardAndImagesFulfilledListener = () => {
startAppListening({ startAppListening({
matcher: boardsApi.endpoints.deleteBoardAndImages.matchFulfilled, matcher: boardsApi.endpoints.deleteBoardAndImages.matchFulfilled,
effect: async (action, { dispatch, getState, condition }) => { effect: async (action, { dispatch, getState }) => {
const { board_id, deleted_board_images, deleted_images } = action.payload; const { deleted_images } = action.payload;
// Remove all deleted images from the UI // Remove all deleted images from the UI

View File

@ -1,16 +1,15 @@
import { log } from 'app/logging/useLogger'; import { isAnyOf } from '@reduxjs/toolkit';
import { import {
ASSETS_CATEGORIES,
IMAGE_CATEGORIES,
boardIdSelected, boardIdSelected,
galleryViewChanged, galleryViewChanged,
imageSelected, imageSelected,
} from 'features/gallery/store/gallerySlice'; } from 'features/gallery/store/gallerySlice';
import {
ASSETS_CATEGORIES,
IMAGE_CATEGORIES,
} from 'features/gallery/store/types';
import { imagesApi } from 'services/api/endpoints/images'; import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..'; import { startAppListening } from '..';
import { isAnyOf } from '@reduxjs/toolkit';
const moduleLog = log.child({ namespace: 'boards' });
export const addBoardIdSelectedListener = () => { export const addBoardIdSelectedListener = () => {
startAppListening({ startAppListening({

View File

@ -1,16 +1,17 @@
import { canvasCopiedToClipboard } from 'features/canvas/store/actions'; import { canvasCopiedToClipboard } from 'features/canvas/store/actions';
import { startAppListening } from '..'; import { startAppListening } from '..';
import { log } from 'app/logging/useLogger'; import { $logger } from 'app/logging/logger';
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob'; import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
import { copyBlobToClipboard } from 'features/canvas/util/copyBlobToClipboard'; import { copyBlobToClipboard } from 'features/canvas/util/copyBlobToClipboard';
const moduleLog = log.child({ namespace: 'canvasCopiedToClipboardListener' });
export const addCanvasCopiedToClipboardListener = () => { export const addCanvasCopiedToClipboardListener = () => {
startAppListening({ startAppListening({
actionCreator: canvasCopiedToClipboard, actionCreator: canvasCopiedToClipboard,
effect: async (action, { dispatch, getState }) => { effect: async (action, { dispatch, getState }) => {
const moduleLog = $logger
.get()
.child({ namespace: 'canvasCopiedToClipboardListener' });
const state = getState(); const state = getState();
const blob = await getBaseLayerBlob(state); const blob = await getBaseLayerBlob(state);

View File

@ -1,16 +1,17 @@
import { canvasDownloadedAsImage } from 'features/canvas/store/actions'; import { canvasDownloadedAsImage } from 'features/canvas/store/actions';
import { startAppListening } from '..'; import { startAppListening } from '..';
import { log } from 'app/logging/useLogger'; import { $logger } from 'app/logging/logger';
import { downloadBlob } from 'features/canvas/util/downloadBlob'; import { downloadBlob } from 'features/canvas/util/downloadBlob';
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob'; import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
const moduleLog = log.child({ namespace: 'canvasSavedToGalleryListener' });
export const addCanvasDownloadedAsImageListener = () => { export const addCanvasDownloadedAsImageListener = () => {
startAppListening({ startAppListening({
actionCreator: canvasDownloadedAsImage, actionCreator: canvasDownloadedAsImage,
effect: async (action, { dispatch, getState }) => { effect: async (action, { dispatch, getState }) => {
const moduleLog = $logger
.get()
.child({ namespace: 'canvasSavedToGalleryListener' });
const state = getState(); const state = getState();
const blob = await getBaseLayerBlob(state); const blob = await getBaseLayerBlob(state);

View File

@ -1,4 +1,4 @@
import { log } from 'app/logging/useLogger'; import { $logger } from 'app/logging/logger';
import { canvasMerged } from 'features/canvas/store/actions'; import { canvasMerged } from 'features/canvas/store/actions';
import { setMergedCanvas } from 'features/canvas/store/canvasSlice'; import { setMergedCanvas } from 'features/canvas/store/canvasSlice';
import { getFullBaseLayerBlob } from 'features/canvas/util/getFullBaseLayerBlob'; import { getFullBaseLayerBlob } from 'features/canvas/util/getFullBaseLayerBlob';
@ -7,12 +7,13 @@ import { addToast } from 'features/system/store/systemSlice';
import { imagesApi } from 'services/api/endpoints/images'; import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..'; import { startAppListening } from '..';
const moduleLog = log.child({ namespace: 'canvasCopiedToClipboardListener' });
export const addCanvasMergedListener = () => { export const addCanvasMergedListener = () => {
startAppListening({ startAppListening({
actionCreator: canvasMerged, actionCreator: canvasMerged,
effect: async (action, { dispatch, getState, take }) => { effect: async (action, { dispatch }) => {
const moduleLog = $logger
.get()
.child({ namespace: 'canvasCopiedToClipboardListener' });
const blob = await getFullBaseLayerBlob(); const blob = await getFullBaseLayerBlob();
if (!blob) { if (!blob) {

View File

@ -1,22 +1,21 @@
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { canvasSavedToGallery } from 'features/canvas/store/actions'; import { canvasSavedToGallery } from 'features/canvas/store/actions';
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob'; import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
import { imagesApi } from 'services/api/endpoints/images'; import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..'; import { startAppListening } from '..';
const moduleLog = log.child({ namespace: 'canvasSavedToGalleryListener' });
export const addCanvasSavedToGalleryListener = () => { export const addCanvasSavedToGalleryListener = () => {
startAppListening({ startAppListening({
actionCreator: canvasSavedToGallery, actionCreator: canvasSavedToGallery,
effect: async (action, { dispatch, getState, take }) => { effect: async (action, { dispatch, getState }) => {
const log = logger('canvas');
const state = getState(); const state = getState();
const blob = await getBaseLayerBlob(state); const blob = await getBaseLayerBlob(state);
if (!blob) { if (!blob) {
moduleLog.error('Problem getting base layer blob'); log.error('Problem getting base layer blob');
dispatch( dispatch(
addToast({ addToast({
title: 'Problem Saving Canvas', title: 'Problem Saving Canvas',

View File

@ -1,6 +1,6 @@
import { AnyListenerPredicate } from '@reduxjs/toolkit'; import { AnyListenerPredicate } from '@reduxjs/toolkit';
import { startAppListening } from '..'; import { logger } from 'app/logging/logger';
import { log } from 'app/logging/useLogger'; import { RootState } from 'app/store/store';
import { controlNetImageProcessed } from 'features/controlNet/store/actions'; import { controlNetImageProcessed } from 'features/controlNet/store/actions';
import { import {
controlNetAutoConfigToggled, controlNetAutoConfigToggled,
@ -9,9 +9,7 @@ import {
controlNetProcessorParamsChanged, controlNetProcessorParamsChanged,
controlNetProcessorTypeChanged, controlNetProcessorTypeChanged,
} from 'features/controlNet/store/controlNetSlice'; } from 'features/controlNet/store/controlNetSlice';
import { RootState } from 'app/store/store'; import { startAppListening } from '..';
const moduleLog = log.child({ namespace: 'controlNet' });
const predicate: AnyListenerPredicate<RootState> = ( const predicate: AnyListenerPredicate<RootState> = (
action, action,
@ -64,18 +62,13 @@ const predicate: AnyListenerPredicate<RootState> = (
export const addControlNetAutoProcessListener = () => { export const addControlNetAutoProcessListener = () => {
startAppListening({ startAppListening({
predicate, predicate,
effect: async ( effect: async (action, { dispatch, cancelActiveListeners, delay }) => {
action, const log = logger('session');
{ dispatch, getState, cancelActiveListeners, delay }
) => {
const { controlNetId } = action.payload; const { controlNetId } = action.payload;
// Cancel any in-progress instances of this listener // Cancel any in-progress instances of this listener
cancelActiveListeners(); cancelActiveListeners();
moduleLog.trace( log.trace('ControlNet auto-process triggered');
{ data: action.payload },
'ControlNet auto-process triggered'
);
// Delay before starting actual work // Delay before starting actual work
await delay(300); await delay(300);

View File

@ -1,4 +1,4 @@
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { controlNetImageProcessed } from 'features/controlNet/store/actions'; import { controlNetImageProcessed } from 'features/controlNet/store/actions';
import { controlNetProcessedImageChanged } from 'features/controlNet/store/controlNetSlice'; import { controlNetProcessedImageChanged } from 'features/controlNet/store/controlNetSlice';
import { sessionReadyToInvoke } from 'features/system/store/actions'; import { sessionReadyToInvoke } from 'features/system/store/actions';
@ -9,20 +9,16 @@ import { Graph, ImageDTO } from 'services/api/types';
import { socketInvocationComplete } from 'services/events/actions'; import { socketInvocationComplete } from 'services/events/actions';
import { startAppListening } from '..'; import { startAppListening } from '..';
const moduleLog = log.child({ namespace: 'controlNet' });
export const addControlNetImageProcessedListener = () => { export const addControlNetImageProcessedListener = () => {
startAppListening({ startAppListening({
actionCreator: controlNetImageProcessed, actionCreator: controlNetImageProcessed,
effect: async ( effect: async (action, { dispatch, getState, take }) => {
action, const log = logger('session');
{ dispatch, getState, take, unsubscribe, subscribe }
) => {
const { controlNetId } = action.payload; const { controlNetId } = action.payload;
const controlNet = getState().controlNet.controlNets[controlNetId]; const controlNet = getState().controlNet.controlNets[controlNetId];
if (!controlNet.controlImage) { if (!controlNet.controlImage) {
moduleLog.error('Unable to process ControlNet image'); log.error('Unable to process ControlNet image');
return; return;
} }
@ -70,8 +66,8 @@ export const addControlNetImageProcessedListener = () => {
const processedControlImage = payload as ImageDTO; const processedControlImage = payload as ImageDTO;
moduleLog.debug( log.debug(
{ data: { arg: action.payload, processedControlImage } }, { controlNetId: action.payload, processedControlImage },
'ControlNet image processed' 'ControlNet image processed'
); );

View File

@ -1,18 +1,17 @@
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { imagesApi } from 'services/api/endpoints/images'; import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..'; import { startAppListening } from '..';
const moduleLog = log.child({ namespace: 'boards' });
export const addImageAddedToBoardFulfilledListener = () => { export const addImageAddedToBoardFulfilledListener = () => {
startAppListening({ startAppListening({
matcher: imagesApi.endpoints.addImageToBoard.matchFulfilled, matcher: imagesApi.endpoints.addImageToBoard.matchFulfilled,
effect: (action, { getState, dispatch }) => { effect: (action) => {
const log = logger('images');
const { board_id, imageDTO } = action.meta.arg.originalArgs; const { board_id, imageDTO } = action.meta.arg.originalArgs;
// TODO: update listImages cache for this board // TODO: update listImages cache for this board
moduleLog.debug({ data: { board_id, imageDTO } }, 'Image added to board'); log.debug({ board_id, imageDTO }, 'Image added to board');
}, },
}); });
}; };
@ -20,13 +19,11 @@ export const addImageAddedToBoardFulfilledListener = () => {
export const addImageAddedToBoardRejectedListener = () => { export const addImageAddedToBoardRejectedListener = () => {
startAppListening({ startAppListening({
matcher: imagesApi.endpoints.addImageToBoard.matchRejected, matcher: imagesApi.endpoints.addImageToBoard.matchRejected,
effect: (action, { getState, dispatch }) => { effect: (action) => {
const log = logger('images');
const { board_id, imageDTO } = action.meta.arg.originalArgs; const { board_id, imageDTO } = action.meta.arg.originalArgs;
moduleLog.debug( log.debug({ board_id, imageDTO }, 'Problem adding image to board');
{ data: { board_id, imageDTO } },
'Problem adding image to board'
);
}, },
}); });
}; };

View File

@ -1,12 +1,10 @@
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { resetCanvas } from 'features/canvas/store/canvasSlice'; import { resetCanvas } from 'features/canvas/store/canvasSlice';
import { controlNetReset } from 'features/controlNet/store/controlNetSlice'; import { controlNetReset } from 'features/controlNet/store/controlNetSlice';
import { selectListImagesBaseQueryArgs } from 'features/gallery/store/gallerySelectors'; import { selectListImagesBaseQueryArgs } from 'features/gallery/store/gallerySelectors';
import { imageSelected } from 'features/gallery/store/gallerySlice'; import { imageSelected } from 'features/gallery/store/gallerySlice';
import { import { imageDeletionConfirmed } from 'features/imageDeletion/store/actions';
imageDeletionConfirmed, import { isModalOpenChanged } from 'features/imageDeletion/store/imageDeletionSlice';
isModalOpenChanged,
} from 'features/imageDeletion/store/imageDeletionSlice';
import { nodeEditorReset } from 'features/nodes/store/nodesSlice'; import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
import { clearInitialImage } from 'features/parameters/store/generationSlice'; import { clearInitialImage } from 'features/parameters/store/generationSlice';
import { clamp } from 'lodash-es'; import { clamp } from 'lodash-es';
@ -14,8 +12,6 @@ import { api } from 'services/api';
import { imagesApi } from 'services/api/endpoints/images'; import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..'; import { startAppListening } from '..';
const moduleLog = log.child({ namespace: 'image' });
/** /**
* Called when the user requests an image deletion * Called when the user requests an image deletion
*/ */
@ -107,7 +103,7 @@ export const addRequestedImageDeletionListener = () => {
export const addImageDeletedPendingListener = () => { export const addImageDeletedPendingListener = () => {
startAppListening({ startAppListening({
matcher: imagesApi.endpoints.deleteImage.matchPending, matcher: imagesApi.endpoints.deleteImage.matchPending,
effect: (action, { dispatch, getState }) => { effect: () => {
// //
}, },
}); });
@ -119,11 +115,9 @@ export const addImageDeletedPendingListener = () => {
export const addImageDeletedFulfilledListener = () => { export const addImageDeletedFulfilledListener = () => {
startAppListening({ startAppListening({
matcher: imagesApi.endpoints.deleteImage.matchFulfilled, matcher: imagesApi.endpoints.deleteImage.matchFulfilled,
effect: (action, { dispatch, getState }) => { effect: (action) => {
moduleLog.debug( const log = logger('images');
{ data: { image: action.meta.arg.originalArgs } }, log.debug({ imageDTO: action.meta.arg.originalArgs }, 'Image deleted');
'Image deleted'
);
}, },
}); });
}; };
@ -134,9 +128,10 @@ export const addImageDeletedFulfilledListener = () => {
export const addImageDeletedRejectedListener = () => { export const addImageDeletedRejectedListener = () => {
startAppListening({ startAppListening({
matcher: imagesApi.endpoints.deleteImage.matchRejected, matcher: imagesApi.endpoints.deleteImage.matchRejected,
effect: (action, { dispatch, getState }) => { effect: (action) => {
moduleLog.debug( const log = logger('images');
{ data: { image: action.meta.arg.originalArgs } }, log.debug(
{ imageDTO: action.meta.arg.originalArgs },
'Unable to delete image' 'Unable to delete image'
); );
}, },

View File

@ -3,7 +3,7 @@ import {
TypesafeDraggableData, TypesafeDraggableData,
TypesafeDroppableData, TypesafeDroppableData,
} from 'app/components/ImageDnd/typesafeDnd'; } from 'app/components/ImageDnd/typesafeDnd';
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice'; import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
import { controlNetImageChanged } from 'features/controlNet/store/controlNetSlice'; import { controlNetImageChanged } from 'features/controlNet/store/controlNetSlice';
import { import {
@ -15,8 +15,6 @@ import { initialImageChanged } from 'features/parameters/store/generationSlice';
import { imagesApi } from 'services/api/endpoints/images'; import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '../'; import { startAppListening } from '../';
const moduleLog = log.child({ namespace: 'dnd' });
export const dndDropped = createAction<{ export const dndDropped = createAction<{
overData: TypesafeDroppableData; overData: TypesafeDroppableData;
activeData: TypesafeDraggableData; activeData: TypesafeDraggableData;
@ -25,14 +23,11 @@ export const dndDropped = createAction<{
export const addImageDroppedListener = () => { export const addImageDroppedListener = () => {
startAppListening({ startAppListening({
actionCreator: dndDropped, actionCreator: dndDropped,
effect: async (action, { dispatch, getState, take }) => { effect: async (action, { dispatch }) => {
const log = logger('images');
const { activeData, overData } = action.payload; const { activeData, overData } = action.payload;
const state = getState();
moduleLog.debug( log.debug({ activeData, overData }, 'Image or selection dropped');
{ data: { activeData, overData } },
'Image or selection dropped'
);
// set current image // set current image
if ( if (

View File

@ -1,19 +1,15 @@
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { imagesApi } from 'services/api/endpoints/images'; import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..'; import { startAppListening } from '..';
const moduleLog = log.child({ namespace: 'boards' });
export const addImageRemovedFromBoardFulfilledListener = () => { export const addImageRemovedFromBoardFulfilledListener = () => {
startAppListening({ startAppListening({
matcher: imagesApi.endpoints.removeImageFromBoard.matchFulfilled, matcher: imagesApi.endpoints.removeImageFromBoard.matchFulfilled,
effect: (action, { getState, dispatch }) => { effect: (action) => {
const { board_id, image_name } = action.meta.arg.originalArgs; const log = logger('images');
const imageDTO = action.meta.arg.originalArgs;
moduleLog.debug( log.debug({ imageDTO }, 'Image removed from board');
{ data: { board_id, image_name } },
'Image added to board'
);
}, },
}); });
}; };
@ -21,13 +17,11 @@ export const addImageRemovedFromBoardFulfilledListener = () => {
export const addImageRemovedFromBoardRejectedListener = () => { export const addImageRemovedFromBoardRejectedListener = () => {
startAppListening({ startAppListening({
matcher: imagesApi.endpoints.removeImageFromBoard.matchRejected, matcher: imagesApi.endpoints.removeImageFromBoard.matchRejected,
effect: (action, { getState, dispatch }) => { effect: (action) => {
const { board_id, image_name } = action.meta.arg.originalArgs; const log = logger('images');
const imageDTO = action.meta.arg.originalArgs;
moduleLog.debug( log.debug({ imageDTO }, 'Problem removing image from board');
{ data: { board_id, image_name } },
'Problem adding image to board'
);
}, },
}); });
}; };

View File

@ -1,18 +1,15 @@
import { startAppListening } from '..'; import { imageDeletionConfirmed } from 'features/imageDeletion/store/actions';
import { log } from 'app/logging/useLogger'; import { selectImageUsage } from 'features/imageDeletion/store/imageDeletionSelectors';
import { import {
imageDeletionConfirmed,
imageToDeleteSelected, imageToDeleteSelected,
isModalOpenChanged, isModalOpenChanged,
selectImageUsage,
} from 'features/imageDeletion/store/imageDeletionSlice'; } from 'features/imageDeletion/store/imageDeletionSlice';
import { startAppListening } from '..';
const moduleLog = log.child({ namespace: 'image' });
export const addImageToDeleteSelectedListener = () => { export const addImageToDeleteSelectedListener = () => {
startAppListening({ startAppListening({
actionCreator: imageToDeleteSelected, actionCreator: imageToDeleteSelected,
effect: async (action, { dispatch, getState, condition }) => { effect: async (action, { dispatch, getState }) => {
const imageDTO = action.payload; const imageDTO = action.payload;
const state = getState(); const state = getState();
const { shouldConfirmOnDelete } = state.system; const { shouldConfirmOnDelete } = state.system;

View File

@ -1,34 +0,0 @@
import { log } from 'app/logging/useLogger';
import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..';
const moduleLog = log.child({ namespace: 'image' });
export const addImageUpdatedFulfilledListener = () => {
// startAppListening({
// matcher: imagesApi.endpoints.updateImage.matchFulfilled,
// effect: (action, { dispatch, getState }) => {
// moduleLog.debug(
// {
// data: {
// oldImage: action.meta.arg.originalArgs,
// updatedImage: action.payload,
// },
// },
// 'Image updated'
// );
// },
// });
};
export const addImageUpdatedRejectedListener = () => {
// startAppListening({
// matcher: imagesApi.endpoints.updateImage.matchRejected,
// effect: (action, { dispatch }) => {
// moduleLog.debug(
// { data: action.meta.arg.originalArgs },
// 'Image update failed'
// );
// },
// });
};

View File

@ -1,5 +1,5 @@
import { UseToastOptions } from '@chakra-ui/react'; import { UseToastOptions } from '@chakra-ui/react';
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice'; import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
import { controlNetImageChanged } from 'features/controlNet/store/controlNetSlice'; import { controlNetImageChanged } from 'features/controlNet/store/controlNetSlice';
import { imagesAddedToBatch } from 'features/gallery/store/gallerySlice'; import { imagesAddedToBatch } from 'features/gallery/store/gallerySlice';
@ -9,8 +9,7 @@ import { addToast } from 'features/system/store/systemSlice';
import { boardsApi } from 'services/api/endpoints/boards'; import { boardsApi } from 'services/api/endpoints/boards';
import { startAppListening } from '..'; import { startAppListening } from '..';
import { imagesApi } from '../../../../../services/api/endpoints/images'; import { imagesApi } from '../../../../../services/api/endpoints/images';
import { omit } from 'lodash-es';
const moduleLog = log.child({ namespace: 'image' });
const DEFAULT_UPLOADED_TOAST: UseToastOptions = { const DEFAULT_UPLOADED_TOAST: UseToastOptions = {
title: 'Image Uploaded', title: 'Image Uploaded',
@ -21,11 +20,12 @@ export const addImageUploadedFulfilledListener = () => {
startAppListening({ startAppListening({
matcher: imagesApi.endpoints.uploadImage.matchFulfilled, matcher: imagesApi.endpoints.uploadImage.matchFulfilled,
effect: (action, { dispatch, getState }) => { effect: (action, { dispatch, getState }) => {
const log = logger('images');
const imageDTO = action.payload; const imageDTO = action.payload;
const state = getState(); const state = getState();
const { selectedBoardId, autoAddBoardId } = state.gallery; const { autoAddBoardId } = state.gallery;
moduleLog.debug({ arg: '<Blob>', imageDTO }, 'Image uploaded'); log.debug({ imageDTO }, 'Image uploaded');
const { postUploadAction } = action.meta.arg.originalArgs; const { postUploadAction } = action.meta.arg.originalArgs;
@ -140,9 +140,14 @@ export const addImageUploadedRejectedListener = () => {
startAppListening({ startAppListening({
matcher: imagesApi.endpoints.uploadImage.matchRejected, matcher: imagesApi.endpoints.uploadImage.matchRejected,
effect: (action, { dispatch }) => { effect: (action, { dispatch }) => {
const { file, postUploadAction, ...rest } = action.meta.arg.originalArgs; const log = logger('images');
const sanitizedData = { arg: { ...rest, file: '<Blob>' } }; const sanitizedData = {
moduleLog.error({ data: sanitizedData }, 'Image upload failed'); arg: {
...omit(action.meta.arg.originalArgs, ['file', 'postUploadAction']),
file: '<Blob>',
},
};
log.error({ ...sanitizedData }, 'Image upload failed');
dispatch( dispatch(
addToast({ addToast({
title: 'Image Upload Failed', title: 'Image Upload Failed',

View File

@ -1,14 +1,14 @@
import { makeToast } from 'app/components/Toaster';
import { initialImageSelected } from 'features/parameters/store/actions'; import { initialImageSelected } from 'features/parameters/store/actions';
import { initialImageChanged } from 'features/parameters/store/generationSlice'; import { initialImageChanged } from 'features/parameters/store/generationSlice';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
import { makeToast } from 'features/system/util/makeToast';
import { t } from 'i18next'; import { t } from 'i18next';
import { startAppListening } from '..'; import { startAppListening } from '..';
export const addInitialImageSelectedListener = () => { export const addInitialImageSelectedListener = () => {
startAppListening({ startAppListening({
actionCreator: initialImageSelected, actionCreator: initialImageSelected,
effect: (action, { getState, dispatch }) => { effect: (action, { dispatch }) => {
if (!action.payload) { if (!action.payload) {
dispatch( dispatch(
addToast( addToast(

View File

@ -1,5 +1,5 @@
import { makeToast } from 'app/components/Toaster'; import { logger } from 'app/logging/logger';
import { log } from 'app/logging/useLogger'; import { controlNetRemoved } from 'features/controlNet/store/controlNetSlice';
import { loraRemoved } from 'features/lora/store/loraSlice'; import { loraRemoved } from 'features/lora/store/loraSlice';
import { modelSelected } from 'features/parameters/store/actions'; import { modelSelected } from 'features/parameters/store/actions';
import { import {
@ -8,21 +8,21 @@ import {
} from 'features/parameters/store/generationSlice'; } from 'features/parameters/store/generationSlice';
import { zMainModel } from 'features/parameters/types/parameterSchemas'; import { zMainModel } from 'features/parameters/types/parameterSchemas';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
import { makeToast } from 'features/system/util/makeToast';
import { forEach } from 'lodash-es'; import { forEach } from 'lodash-es';
import { startAppListening } from '..'; import { startAppListening } from '..';
import { controlNetRemoved } from 'features/controlNet/store/controlNetSlice';
const moduleLog = log.child({ module: 'models' });
export const addModelSelectedListener = () => { export const addModelSelectedListener = () => {
startAppListening({ startAppListening({
actionCreator: modelSelected, actionCreator: modelSelected,
effect: (action, { getState, dispatch }) => { effect: (action, { getState, dispatch }) => {
const log = logger('models');
const state = getState(); const state = getState();
const result = zMainModel.safeParse(action.payload); const result = zMainModel.safeParse(action.payload);
if (!result.success) { if (!result.success) {
moduleLog.error( log.error(
{ error: result.error.format() }, { error: result.error.format() },
'Failed to parse main model' 'Failed to parse main model'
); );

View File

@ -1,4 +1,5 @@
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { controlNetRemoved } from 'features/controlNet/store/controlNetSlice';
import { loraRemoved } from 'features/lora/store/loraSlice'; import { loraRemoved } from 'features/lora/store/loraSlice';
import { import {
modelChanged, modelChanged,
@ -11,15 +12,17 @@ import {
import { forEach, some } from 'lodash-es'; import { forEach, some } from 'lodash-es';
import { modelsApi } from 'services/api/endpoints/models'; import { modelsApi } from 'services/api/endpoints/models';
import { startAppListening } from '..'; import { startAppListening } from '..';
import { controlNetRemoved } from 'features/controlNet/store/controlNetSlice';
const moduleLog = log.child({ module: 'models' });
export const addModelsLoadedListener = () => { export const addModelsLoadedListener = () => {
startAppListening({ startAppListening({
matcher: modelsApi.endpoints.getMainModels.matchFulfilled, matcher: modelsApi.endpoints.getMainModels.matchFulfilled,
effect: async (action, { getState, dispatch }) => { effect: async (action, { getState, dispatch }) => {
// models loaded, we need to ensure the selected model is available and if not, select the first one // models loaded, we need to ensure the selected model is available and if not, select the first one
const log = logger('models');
log.info(
{ models: action.payload.entities },
`Main models loaded (${action.payload.ids.length})`
);
const currentModel = getState().generation.model; const currentModel = getState().generation.model;
@ -46,7 +49,7 @@ export const addModelsLoadedListener = () => {
const result = zMainModel.safeParse(firstModel); const result = zMainModel.safeParse(firstModel);
if (!result.success) { if (!result.success) {
moduleLog.error( log.error(
{ error: result.error.format() }, { error: result.error.format() },
'Failed to parse main model' 'Failed to parse main model'
); );
@ -60,6 +63,11 @@ export const addModelsLoadedListener = () => {
matcher: modelsApi.endpoints.getVaeModels.matchFulfilled, matcher: modelsApi.endpoints.getVaeModels.matchFulfilled,
effect: async (action, { getState, dispatch }) => { effect: async (action, { getState, dispatch }) => {
// VAEs loaded, need to reset the VAE is it's no longer available // VAEs loaded, need to reset the VAE is it's no longer available
const log = logger('models');
log.info(
{ models: action.payload.entities },
`VAEs loaded (${action.payload.ids.length})`
);
const currentVae = getState().generation.vae; const currentVae = getState().generation.vae;
@ -91,7 +99,7 @@ export const addModelsLoadedListener = () => {
const result = zVaeModel.safeParse(firstModel); const result = zVaeModel.safeParse(firstModel);
if (!result.success) { if (!result.success) {
moduleLog.error( log.error(
{ error: result.error.format() }, { error: result.error.format() },
'Failed to parse VAE model' 'Failed to parse VAE model'
); );
@ -105,6 +113,11 @@ export const addModelsLoadedListener = () => {
matcher: modelsApi.endpoints.getLoRAModels.matchFulfilled, matcher: modelsApi.endpoints.getLoRAModels.matchFulfilled,
effect: async (action, { getState, dispatch }) => { effect: async (action, { getState, dispatch }) => {
// LoRA models loaded - need to remove missing LoRAs from state // LoRA models loaded - need to remove missing LoRAs from state
const log = logger('models');
log.info(
{ models: action.payload.entities },
`LoRAs loaded (${action.payload.ids.length})`
);
const loras = getState().lora.loras; const loras = getState().lora.loras;
@ -128,6 +141,12 @@ export const addModelsLoadedListener = () => {
matcher: modelsApi.endpoints.getControlNetModels.matchFulfilled, matcher: modelsApi.endpoints.getControlNetModels.matchFulfilled,
effect: async (action, { getState, dispatch }) => { effect: async (action, { getState, dispatch }) => {
// ControlNet models loaded - need to remove missing ControlNets from state // ControlNet models loaded - need to remove missing ControlNets from state
const log = logger('models');
log.info(
{ models: action.payload.entities },
`ControlNet models loaded (${action.payload.ids.length})`
);
const controlNets = getState().controlNet.controlNets; const controlNets = getState().controlNet.controlNets;
forEach(controlNets, (controlNet, controlNetId) => { forEach(controlNets, (controlNet, controlNetId) => {
@ -146,4 +165,14 @@ export const addModelsLoadedListener = () => {
}); });
}, },
}); });
startAppListening({
matcher: modelsApi.endpoints.getTextualInversionModels.matchFulfilled,
effect: async (action) => {
const log = logger('models');
log.info(
{ models: action.payload.entities },
`Embeddings loaded (${action.payload.ids.length})`
);
},
});
}; };

View File

@ -1,24 +1,24 @@
import { logger } from 'app/logging/logger';
import { parseify } from 'common/util/serialize';
import { nodeTemplatesBuilt } from 'features/nodes/store/nodesSlice';
import { parseSchema } from 'features/nodes/util/parseSchema';
import { size } from 'lodash-es';
import { receivedOpenAPISchema } from 'services/api/thunks/schema'; import { receivedOpenAPISchema } from 'services/api/thunks/schema';
import { startAppListening } from '..'; import { startAppListening } from '..';
import { log } from 'app/logging/useLogger';
import { parseSchema } from 'features/nodes/util/parseSchema';
import { nodeTemplatesBuilt } from 'features/nodes/store/nodesSlice';
import { size } from 'lodash-es';
const schemaLog = log.child({ namespace: 'schema' });
export const addReceivedOpenAPISchemaListener = () => { export const addReceivedOpenAPISchemaListener = () => {
startAppListening({ startAppListening({
actionCreator: receivedOpenAPISchema.fulfilled, actionCreator: receivedOpenAPISchema.fulfilled,
effect: (action, { dispatch, getState }) => { effect: (action, { dispatch }) => {
const log = logger('system');
const schemaJSON = action.payload; const schemaJSON = action.payload;
schemaLog.info({ data: { schemaJSON } }, 'Dereferenced OpenAPI schema'); log.debug({ schemaJSON }, 'Dereferenced OpenAPI schema');
const nodeTemplates = parseSchema(schemaJSON); const nodeTemplates = parseSchema(schemaJSON);
schemaLog.info( log.debug(
{ data: { nodeTemplates } }, { nodeTemplates: parseify(nodeTemplates) },
`Built ${size(nodeTemplates)} node templates` `Built ${size(nodeTemplates)} node templates`
); );
@ -28,8 +28,9 @@ export const addReceivedOpenAPISchemaListener = () => {
startAppListening({ startAppListening({
actionCreator: receivedOpenAPISchema.rejected, actionCreator: receivedOpenAPISchema.rejected,
effect: (action, { dispatch, getState }) => { effect: () => {
schemaLog.error('Problem dereferencing OpenAPI Schema'); const log = logger('system');
log.error('Problem dereferencing OpenAPI Schema');
}, },
}); });
}; };

View File

@ -1,14 +1,12 @@
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { startAppListening } from '..';
import { sessionCanceled } from 'services/api/thunks/session';
import { serializeError } from 'serialize-error'; import { serializeError } from 'serialize-error';
import { sessionCanceled } from 'services/api/thunks/session';
const moduleLog = log.child({ namespace: 'session' }); import { startAppListening } from '..';
export const addSessionCanceledPendingListener = () => { export const addSessionCanceledPendingListener = () => {
startAppListening({ startAppListening({
actionCreator: sessionCanceled.pending, actionCreator: sessionCanceled.pending,
effect: (action, { getState, dispatch }) => { effect: () => {
// //
}, },
}); });
@ -17,12 +15,10 @@ export const addSessionCanceledPendingListener = () => {
export const addSessionCanceledFulfilledListener = () => { export const addSessionCanceledFulfilledListener = () => {
startAppListening({ startAppListening({
actionCreator: sessionCanceled.fulfilled, actionCreator: sessionCanceled.fulfilled,
effect: (action, { getState, dispatch }) => { effect: (action) => {
const log = logger('session');
const { session_id } = action.meta.arg; const { session_id } = action.meta.arg;
moduleLog.debug( log.debug({ session_id }, `Session canceled (${session_id})`);
{ data: { session_id } },
`Session canceled (${session_id})`
);
}, },
}); });
}; };
@ -30,15 +26,15 @@ export const addSessionCanceledFulfilledListener = () => {
export const addSessionCanceledRejectedListener = () => { export const addSessionCanceledRejectedListener = () => {
startAppListening({ startAppListening({
actionCreator: sessionCanceled.rejected, actionCreator: sessionCanceled.rejected,
effect: (action, { getState, dispatch }) => { effect: (action) => {
const log = logger('session');
const { session_id } = action.meta.arg;
if (action.payload) { if (action.payload) {
const { arg, error } = action.payload; const { error } = action.payload;
moduleLog.error( log.error(
{ {
data: { session_id,
arg, error: serializeError(error),
error: serializeError(error),
},
}, },
`Problem canceling session` `Problem canceling session`
); );

View File

@ -1,14 +1,13 @@
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { parseify } from 'common/util/serialize';
import { serializeError } from 'serialize-error'; import { serializeError } from 'serialize-error';
import { sessionCreated } from 'services/api/thunks/session'; import { sessionCreated } from 'services/api/thunks/session';
import { startAppListening } from '..'; import { startAppListening } from '..';
const moduleLog = log.child({ namespace: 'session' });
export const addSessionCreatedPendingListener = () => { export const addSessionCreatedPendingListener = () => {
startAppListening({ startAppListening({
actionCreator: sessionCreated.pending, actionCreator: sessionCreated.pending,
effect: (action, { getState, dispatch }) => { effect: () => {
// //
}, },
}); });
@ -17,9 +16,13 @@ export const addSessionCreatedPendingListener = () => {
export const addSessionCreatedFulfilledListener = () => { export const addSessionCreatedFulfilledListener = () => {
startAppListening({ startAppListening({
actionCreator: sessionCreated.fulfilled, actionCreator: sessionCreated.fulfilled,
effect: (action, { getState, dispatch }) => { effect: (action) => {
const log = logger('session');
const session = action.payload; const session = action.payload;
moduleLog.debug({ data: { session } }, `Session created (${session.id})`); log.debug(
{ session: parseify(session) },
`Session created (${session.id})`
);
}, },
}); });
}; };
@ -27,17 +30,14 @@ export const addSessionCreatedFulfilledListener = () => {
export const addSessionCreatedRejectedListener = () => { export const addSessionCreatedRejectedListener = () => {
startAppListening({ startAppListening({
actionCreator: sessionCreated.rejected, actionCreator: sessionCreated.rejected,
effect: (action, { getState, dispatch }) => { effect: (action) => {
const log = logger('session');
if (action.payload) { if (action.payload) {
const { arg, error } = action.payload; const { error } = action.payload;
const graph = parseify(action.meta.arg);
const stringifiedError = JSON.stringify(error); const stringifiedError = JSON.stringify(error);
moduleLog.error( log.error(
{ { graph, error: serializeError(error) },
data: {
arg,
error: serializeError(error),
},
},
`Problem creating session: ${stringifiedError}` `Problem creating session: ${stringifiedError}`
); );
} }

View File

@ -1,14 +1,12 @@
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { startAppListening } from '..';
import { sessionInvoked } from 'services/api/thunks/session';
import { serializeError } from 'serialize-error'; import { serializeError } from 'serialize-error';
import { sessionInvoked } from 'services/api/thunks/session';
const moduleLog = log.child({ namespace: 'session' }); import { startAppListening } from '..';
export const addSessionInvokedPendingListener = () => { export const addSessionInvokedPendingListener = () => {
startAppListening({ startAppListening({
actionCreator: sessionInvoked.pending, actionCreator: sessionInvoked.pending,
effect: (action, { getState, dispatch }) => { effect: () => {
// //
}, },
}); });
@ -17,12 +15,10 @@ export const addSessionInvokedPendingListener = () => {
export const addSessionInvokedFulfilledListener = () => { export const addSessionInvokedFulfilledListener = () => {
startAppListening({ startAppListening({
actionCreator: sessionInvoked.fulfilled, actionCreator: sessionInvoked.fulfilled,
effect: (action, { getState, dispatch }) => { effect: (action) => {
const log = logger('session');
const { session_id } = action.meta.arg; const { session_id } = action.meta.arg;
moduleLog.debug( log.debug({ session_id }, `Session invoked (${session_id})`);
{ data: { session_id } },
`Session invoked (${session_id})`
);
}, },
}); });
}; };
@ -30,16 +26,16 @@ export const addSessionInvokedFulfilledListener = () => {
export const addSessionInvokedRejectedListener = () => { export const addSessionInvokedRejectedListener = () => {
startAppListening({ startAppListening({
actionCreator: sessionInvoked.rejected, actionCreator: sessionInvoked.rejected,
effect: (action, { getState, dispatch }) => { effect: (action) => {
const log = logger('session');
const { session_id } = action.meta.arg;
if (action.payload) { if (action.payload) {
const { arg, error } = action.payload; const { error } = action.payload;
const stringifiedError = JSON.stringify(error); const stringifiedError = JSON.stringify(error);
moduleLog.error( log.error(
{ {
data: { session_id,
arg, error: serializeError(error),
error: serializeError(error),
},
}, },
`Problem invoking session: ${stringifiedError}` `Problem invoking session: ${stringifiedError}`
); );

View File

@ -1,20 +1,16 @@
import { startAppListening } from '..'; import { logger } from 'app/logging/logger';
import { sessionInvoked } from 'services/api/thunks/session';
import { log } from 'app/logging/useLogger';
import { sessionReadyToInvoke } from 'features/system/store/actions'; import { sessionReadyToInvoke } from 'features/system/store/actions';
import { sessionInvoked } from 'services/api/thunks/session';
const moduleLog = log.child({ namespace: 'session' }); import { startAppListening } from '..';
export const addSessionReadyToInvokeListener = () => { export const addSessionReadyToInvokeListener = () => {
startAppListening({ startAppListening({
actionCreator: sessionReadyToInvoke, actionCreator: sessionReadyToInvoke,
effect: (action, { getState, dispatch }) => { effect: (action, { getState, dispatch }) => {
const log = logger('session');
const { sessionId: session_id } = getState().system; const { sessionId: session_id } = getState().system;
if (session_id) { if (session_id) {
moduleLog.debug( log.debug({ session_id }, `Session ready to invoke (${session_id})})`);
{ session_id },
`Session ready to invoke (${session_id})})`
);
dispatch(sessionInvoked({ session_id })); dispatch(sessionInvoked({ session_id }));
} }
}, },

View File

@ -1,18 +1,16 @@
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { modelsApi } from 'services/api/endpoints/models'; import { modelsApi } from 'services/api/endpoints/models';
import { receivedOpenAPISchema } from 'services/api/thunks/schema'; import { receivedOpenAPISchema } from 'services/api/thunks/schema';
import { appSocketConnected, socketConnected } from 'services/events/actions'; import { appSocketConnected, socketConnected } from 'services/events/actions';
import { startAppListening } from '../..'; import { startAppListening } from '../..';
const moduleLog = log.child({ namespace: 'socketio' });
export const addSocketConnectedEventListener = () => { export const addSocketConnectedEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketConnected, actionCreator: socketConnected,
effect: (action, { dispatch, getState }) => { effect: (action, { dispatch, getState }) => {
const { timestamp } = action.payload; const log = logger('socketio');
moduleLog.debug({ timestamp }, 'Connected'); log.debug('Connected');
const { nodes, config } = getState(); const { nodes, config } = getState();

View File

@ -1,17 +1,16 @@
import { startAppListening } from '../..'; import { logger } from 'app/logging/logger';
import { log } from 'app/logging/useLogger';
import { import {
socketDisconnected,
appSocketDisconnected, appSocketDisconnected,
socketDisconnected,
} from 'services/events/actions'; } from 'services/events/actions';
import { startAppListening } from '../..';
const moduleLog = log.child({ namespace: 'socketio' });
export const addSocketDisconnectedEventListener = () => { export const addSocketDisconnectedEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketDisconnected, actionCreator: socketDisconnected,
effect: (action, { dispatch, getState }) => { effect: (action, { dispatch }) => {
moduleLog.debug(action.payload, 'Disconnected'); const log = logger('socketio');
log.debug('Disconnected');
// pass along the socket event as an application action // pass along the socket event as an application action
dispatch(appSocketDisconnected(action.payload)); dispatch(appSocketDisconnected(action.payload));
}, },

View File

@ -1,28 +1,27 @@
import { startAppListening } from '../..'; import { logger } from 'app/logging/logger';
import { log } from 'app/logging/useLogger';
import { import {
appSocketGeneratorProgress, appSocketGeneratorProgress,
socketGeneratorProgress, socketGeneratorProgress,
} from 'services/events/actions'; } from 'services/events/actions';
import { startAppListening } from '../..';
const moduleLog = log.child({ namespace: 'socketio' });
export const addGeneratorProgressEventListener = () => { export const addGeneratorProgressEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketGeneratorProgress, actionCreator: socketGeneratorProgress,
effect: (action, { dispatch, getState }) => { effect: (action, { dispatch, getState }) => {
const log = logger('socketio');
if ( if (
getState().system.canceledSession === getState().system.canceledSession ===
action.payload.data.graph_execution_state_id action.payload.data.graph_execution_state_id
) { ) {
moduleLog.trace( log.trace(
action.payload, action.payload,
'Ignored generator progress for canceled session' 'Ignored generator progress for canceled session'
); );
return; return;
} }
moduleLog.trace( log.trace(
action.payload, action.payload,
`Generator progress (${action.payload.data.node.type})` `Generator progress (${action.payload.data.node.type})`
); );

View File

@ -1,20 +1,16 @@
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { import {
appSocketGraphExecutionStateComplete, appSocketGraphExecutionStateComplete,
socketGraphExecutionStateComplete, socketGraphExecutionStateComplete,
} from 'services/events/actions'; } from 'services/events/actions';
import { startAppListening } from '../..'; import { startAppListening } from '../..';
const moduleLog = log.child({ namespace: 'socketio' });
export const addGraphExecutionStateCompleteEventListener = () => { export const addGraphExecutionStateCompleteEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketGraphExecutionStateComplete, actionCreator: socketGraphExecutionStateComplete,
effect: (action, { dispatch, getState }) => { effect: (action, { dispatch }) => {
moduleLog.debug( const log = logger('socketio');
action.payload, log.debug(action.payload, 'Session complete');
`Session invocation complete (${action.payload.data.graph_execution_state_id})`
);
// pass along the socket event as an application action // pass along the socket event as an application action
dispatch(appSocketGraphExecutionStateComplete(action.payload)); dispatch(appSocketGraphExecutionStateComplete(action.payload));
}, },

View File

@ -1,11 +1,12 @@
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { parseify } from 'common/util/serialize';
import { addImageToStagingArea } from 'features/canvas/store/canvasSlice'; import { addImageToStagingArea } from 'features/canvas/store/canvasSlice';
import { import {
IMAGE_CATEGORIES,
boardIdSelected, boardIdSelected,
galleryViewChanged, galleryViewChanged,
imageSelected, imageSelected,
} from 'features/gallery/store/gallerySlice'; } from 'features/gallery/store/gallerySlice';
import { IMAGE_CATEGORIES } from 'features/gallery/store/types';
import { progressImageSet } from 'features/system/store/systemSlice'; import { progressImageSet } from 'features/system/store/systemSlice';
import { imagesAdapter, imagesApi } from 'services/api/endpoints/images'; import { imagesAdapter, imagesApi } from 'services/api/endpoints/images';
import { isImageOutput } from 'services/api/guards'; import { isImageOutput } from 'services/api/guards';
@ -16,15 +17,16 @@ import {
} from 'services/events/actions'; } from 'services/events/actions';
import { startAppListening } from '../..'; import { startAppListening } from '../..';
const moduleLog = log.child({ namespace: 'socketio' });
const nodeDenylist = ['dataURL_image']; const nodeDenylist = ['dataURL_image'];
export const addInvocationCompleteEventListener = () => { export const addInvocationCompleteEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketInvocationComplete, actionCreator: socketInvocationComplete,
effect: async (action, { dispatch, getState, take }) => { effect: async (action, { dispatch, getState }) => {
moduleLog.debug( const log = logger('socketio');
{ data: action.payload }, const { data } = action.payload;
log.debug(
{ data: parseify(data) },
`Invocation complete (${action.payload.data.node.type})` `Invocation complete (${action.payload.data.node.type})`
); );
const session_id = action.payload.data.graph_execution_state_id; const session_id = action.payload.data.graph_execution_state_id;
@ -36,7 +38,6 @@ export const addInvocationCompleteEventListener = () => {
dispatch(sessionCanceled({ session_id })); dispatch(sessionCanceled({ session_id }));
} }
const { data } = action.payload;
const { result, node, graph_execution_state_id } = data; const { result, node, graph_execution_state_id } = data;
// This complete event has an associated image output // This complete event has an associated image output

View File

@ -1,19 +1,18 @@
import { startAppListening } from '../..'; import { logger } from 'app/logging/logger';
import { log } from 'app/logging/useLogger';
import { import {
appSocketInvocationError, appSocketInvocationError,
socketInvocationError, socketInvocationError,
} from 'services/events/actions'; } from 'services/events/actions';
import { startAppListening } from '../..';
const moduleLog = log.child({ namespace: 'socketio' });
export const addInvocationErrorEventListener = () => { export const addInvocationErrorEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketInvocationError, actionCreator: socketInvocationError,
effect: (action, { dispatch, getState }) => { effect: (action, { dispatch }) => {
moduleLog.error( const log = logger('socketio');
log.error(
action.payload, action.payload,
`Invocation error (${action.payload.data.node.type}): ${action.payload.data.error}` `Invocation error (${action.payload.data.node.type})`
); );
dispatch(appSocketInvocationError(action.payload)); dispatch(appSocketInvocationError(action.payload));
}, },

View File

@ -1,31 +1,27 @@
import { startAppListening } from '../..'; import { logger } from 'app/logging/logger';
import { log } from 'app/logging/useLogger';
import { import {
appSocketInvocationStarted, appSocketInvocationStarted,
socketInvocationStarted, socketInvocationStarted,
} from 'services/events/actions'; } from 'services/events/actions';
import { startAppListening } from '../..';
const moduleLog = log.child({ namespace: 'socketio' });
export const addInvocationStartedEventListener = () => { export const addInvocationStartedEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketInvocationStarted, actionCreator: socketInvocationStarted,
effect: (action, { dispatch, getState }) => { effect: (action, { dispatch, getState }) => {
const log = logger('socketio');
if ( if (
getState().system.canceledSession === getState().system.canceledSession ===
action.payload.data.graph_execution_state_id action.payload.data.graph_execution_state_id
) { ) {
moduleLog.trace( log.trace(
action.payload, action.payload,
'Ignored invocation started for canceled session' 'Ignored invocation started for canceled session'
); );
return; return;
} }
moduleLog.debug( log.debug(action.payload, 'Invocation started');
action.payload,
`Invocation started (${action.payload.data.node.type})`
);
dispatch(appSocketInvocationStarted(action.payload)); dispatch(appSocketInvocationStarted(action.payload));
}, },
}); });

View File

@ -0,0 +1,49 @@
import { logger } from 'app/logging/logger';
import {
appSocketModelLoadCompleted,
appSocketModelLoadStarted,
socketModelLoadCompleted,
socketModelLoadStarted,
} from 'services/events/actions';
import { startAppListening } from '../..';
export const addModelLoadEventListener = () => {
startAppListening({
actionCreator: socketModelLoadStarted,
effect: (action, { dispatch }) => {
const log = logger('socketio');
const { base_model, model_name, model_type, submodel } =
action.payload.data;
let message = `Model load started: ${base_model}/${model_type}/${model_name}`;
if (submodel) {
message = message.concat(`/${submodel}`);
}
log.debug(action.payload, message);
// pass along the socket event as an application action
dispatch(appSocketModelLoadStarted(action.payload));
},
});
startAppListening({
actionCreator: socketModelLoadCompleted,
effect: (action, { dispatch }) => {
const log = logger('socketio');
const { base_model, model_name, model_type, submodel } =
action.payload.data;
let message = `Model load complete: ${base_model}/${model_type}/${model_name}`;
if (submodel) {
message = message.concat(`/${submodel}`);
}
log.debug(action.payload, message);
// pass along the socket event as an application action
dispatch(appSocketModelLoadCompleted(action.payload));
},
});
};

View File

@ -1,28 +0,0 @@
import { log } from 'app/logging/useLogger';
import {
appSocketModelLoadCompleted,
socketModelLoadCompleted,
} from 'services/events/actions';
import { startAppListening } from '../..';
const moduleLog = log.child({ namespace: 'socketio' });
export const addModelLoadCompletedEventListener = () => {
startAppListening({
actionCreator: socketModelLoadCompleted,
effect: (action, { dispatch, getState }) => {
const { model_name, model_type, submodel } = action.payload.data;
let modelString = `${model_type} model: ${model_name}`;
if (submodel) {
modelString = modelString.concat(`, submodel: ${submodel}`);
}
moduleLog.debug(action.payload, `Model load completed (${modelString})`);
// pass along the socket event as an application action
dispatch(appSocketModelLoadCompleted(action.payload));
},
});
};

View File

@ -1,28 +0,0 @@
import { log } from 'app/logging/useLogger';
import {
appSocketModelLoadStarted,
socketModelLoadStarted,
} from 'services/events/actions';
import { startAppListening } from '../..';
const moduleLog = log.child({ namespace: 'socketio' });
export const addModelLoadStartedEventListener = () => {
startAppListening({
actionCreator: socketModelLoadStarted,
effect: (action, { dispatch, getState }) => {
const { model_name, model_type, submodel } = action.payload.data;
let modelString = `${model_type} model: ${model_name}`;
if (submodel) {
modelString = modelString.concat(`, submodel: ${submodel}`);
}
moduleLog.debug(action.payload, `Model load started (${modelString})`);
// pass along the socket event as an application action
dispatch(appSocketModelLoadStarted(action.payload));
},
});
};

View File

@ -1,17 +1,13 @@
import { startAppListening } from '../..'; import { logger } from 'app/logging/logger';
import { log } from 'app/logging/useLogger';
import { appSocketSubscribed, socketSubscribed } from 'services/events/actions'; import { appSocketSubscribed, socketSubscribed } from 'services/events/actions';
import { startAppListening } from '../..';
const moduleLog = log.child({ namespace: 'socketio' });
export const addSocketSubscribedEventListener = () => { export const addSocketSubscribedEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketSubscribed, actionCreator: socketSubscribed,
effect: (action, { dispatch, getState }) => { effect: (action, { dispatch }) => {
moduleLog.debug( const log = logger('socketio');
action.payload, log.debug(action.payload, 'Subscribed');
`Subscribed (${action.payload.sessionId}))`
);
dispatch(appSocketSubscribed(action.payload)); dispatch(appSocketSubscribed(action.payload));
}, },
}); });

View File

@ -1,20 +1,16 @@
import { startAppListening } from '../..'; import { logger } from 'app/logging/logger';
import { log } from 'app/logging/useLogger';
import { import {
appSocketUnsubscribed, appSocketUnsubscribed,
socketUnsubscribed, socketUnsubscribed,
} from 'services/events/actions'; } from 'services/events/actions';
import { startAppListening } from '../..';
const moduleLog = log.child({ namespace: 'socketio' });
export const addSocketUnsubscribedEventListener = () => { export const addSocketUnsubscribedEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketUnsubscribed, actionCreator: socketUnsubscribed,
effect: (action, { dispatch, getState }) => { effect: (action, { dispatch }) => {
moduleLog.debug( const log = logger('socketio');
action.payload, log.debug(action.payload, 'Unsubscribed');
`Unsubscribed (${action.payload.sessionId})`
);
dispatch(appSocketUnsubscribed(action.payload)); dispatch(appSocketUnsubscribed(action.payload));
}, },
}); });

View File

@ -1,15 +1,12 @@
import { log } from 'app/logging/useLogger';
import { stagingAreaImageSaved } from 'features/canvas/store/actions'; import { stagingAreaImageSaved } from 'features/canvas/store/actions';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
import { imagesApi } from 'services/api/endpoints/images'; import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..'; import { startAppListening } from '..';
const moduleLog = log.child({ namespace: 'canvas' });
export const addStagingAreaImageSavedListener = () => { export const addStagingAreaImageSavedListener = () => {
startAppListening({ startAppListening({
actionCreator: stagingAreaImageSaved, actionCreator: stagingAreaImageSaved,
effect: async (action, { dispatch, getState, take }) => { effect: async (action, { dispatch, getState }) => {
const { imageDTO } = action.payload; const { imageDTO } = action.payload;
try { try {

View File

@ -1,12 +1,9 @@
import { createAction } from '@reduxjs/toolkit'; import { createAction } from '@reduxjs/toolkit';
import { log } from 'app/logging/useLogger';
import { buildAdHocUpscaleGraph } from 'features/nodes/util/graphBuilders/buildAdHocUpscaleGraph'; import { buildAdHocUpscaleGraph } from 'features/nodes/util/graphBuilders/buildAdHocUpscaleGraph';
import { sessionReadyToInvoke } from 'features/system/store/actions'; import { sessionReadyToInvoke } from 'features/system/store/actions';
import { sessionCreated } from 'services/api/thunks/session'; import { sessionCreated } from 'services/api/thunks/session';
import { startAppListening } from '..'; import { startAppListening } from '..';
const moduleLog = log.child({ namespace: 'upscale' });
export const upscaleRequested = createAction<{ image_name: string }>( export const upscaleRequested = createAction<{ image_name: string }>(
`upscale/upscaleRequested` `upscale/upscaleRequested`
); );
@ -14,10 +11,7 @@ export const upscaleRequested = createAction<{ image_name: string }>(
export const addUpscaleRequestedListener = () => { export const addUpscaleRequestedListener = () => {
startAppListening({ startAppListening({
actionCreator: upscaleRequested, actionCreator: upscaleRequested,
effect: async ( effect: async (action, { dispatch, getState, take }) => {
action,
{ dispatch, getState, take, unsubscribe, subscribe }
) => {
const { image_name } = action.payload; const { image_name } = action.payload;
const { esrganModelName } = getState().postprocessing; const { esrganModelName } = getState().postprocessing;

View File

@ -1,4 +1,4 @@
import { log } from 'app/logging/useLogger'; import { logger } from 'app/logging/logger';
import { userInvoked } from 'app/store/actions'; import { userInvoked } from 'app/store/actions';
import openBase64ImageInTab from 'common/util/openBase64ImageInTab'; import openBase64ImageInTab from 'common/util/openBase64ImageInTab';
import { import {
@ -15,8 +15,7 @@ import { imagesApi } from 'services/api/endpoints/images';
import { sessionCreated } from 'services/api/thunks/session'; import { sessionCreated } from 'services/api/thunks/session';
import { ImageDTO } from 'services/api/types'; import { ImageDTO } from 'services/api/types';
import { startAppListening } from '..'; import { startAppListening } from '..';
import { parseify } from 'common/util/serialize';
const moduleLog = log.child({ namespace: 'invoke' });
/** /**
* This listener is responsible invoking the canvas. This involves a number of steps: * This listener is responsible invoking the canvas. This involves a number of steps:
@ -36,13 +35,15 @@ export const addUserInvokedCanvasListener = () => {
predicate: (action): action is ReturnType<typeof userInvoked> => predicate: (action): action is ReturnType<typeof userInvoked> =>
userInvoked.match(action) && action.payload === 'unifiedCanvas', userInvoked.match(action) && action.payload === 'unifiedCanvas',
effect: async (action, { getState, dispatch, take }) => { effect: async (action, { getState, dispatch, take }) => {
const log = logger('session');
const state = getState(); const state = getState();
// Build canvas blobs // Build canvas blobs
const canvasBlobsAndImageData = await getCanvasData(state); const canvasBlobsAndImageData = await getCanvasData(state);
if (!canvasBlobsAndImageData) { if (!canvasBlobsAndImageData) {
moduleLog.error('Unable to create canvas data'); log.error('Unable to create canvas data');
return; return;
} }
@ -64,7 +65,7 @@ export const addUserInvokedCanvasListener = () => {
]); ]);
} }
moduleLog.debug(`Generation mode: ${generationMode}`); log.debug(`Generation mode: ${generationMode}`);
// Temp placeholders for the init and mask images // Temp placeholders for the init and mask images
let canvasInitImage: ImageDTO | undefined; let canvasInitImage: ImageDTO | undefined;
@ -105,7 +106,7 @@ export const addUserInvokedCanvasListener = () => {
canvasMaskImage canvasMaskImage
); );
moduleLog.debug({ graph }, `Canvas graph built`); log.debug({ graph: parseify(graph) }, `Canvas graph built`);
// currently this action is just listened to for logging // currently this action is just listened to for logging
dispatch(canvasGraphBuilt(graph)); dispatch(canvasGraphBuilt(graph));

View File

@ -1,23 +1,23 @@
import { startAppListening } from '..'; import { logger } from 'app/logging/logger';
import { sessionCreated } from 'services/api/thunks/session';
import { log } from 'app/logging/useLogger';
import { imageToImageGraphBuilt } from 'features/nodes/store/actions';
import { userInvoked } from 'app/store/actions'; import { userInvoked } from 'app/store/actions';
import { sessionReadyToInvoke } from 'features/system/store/actions'; import { parseify } from 'common/util/serialize';
import { imageToImageGraphBuilt } from 'features/nodes/store/actions';
import { buildLinearImageToImageGraph } from 'features/nodes/util/graphBuilders/buildLinearImageToImageGraph'; import { buildLinearImageToImageGraph } from 'features/nodes/util/graphBuilders/buildLinearImageToImageGraph';
import { sessionReadyToInvoke } from 'features/system/store/actions';
const moduleLog = log.child({ namespace: 'invoke' }); import { sessionCreated } from 'services/api/thunks/session';
import { startAppListening } from '..';
export const addUserInvokedImageToImageListener = () => { export const addUserInvokedImageToImageListener = () => {
startAppListening({ startAppListening({
predicate: (action): action is ReturnType<typeof userInvoked> => predicate: (action): action is ReturnType<typeof userInvoked> =>
userInvoked.match(action) && action.payload === 'img2img', userInvoked.match(action) && action.payload === 'img2img',
effect: async (action, { getState, dispatch, take }) => { effect: async (action, { getState, dispatch, take }) => {
const log = logger('session');
const state = getState(); const state = getState();
const graph = buildLinearImageToImageGraph(state); const graph = buildLinearImageToImageGraph(state);
dispatch(imageToImageGraphBuilt(graph)); dispatch(imageToImageGraphBuilt(graph));
moduleLog.debug({ data: graph }, 'Image to Image graph built'); log.debug({ graph: parseify(graph) }, 'Image to Image graph built');
dispatch(sessionCreated({ graph })); dispatch(sessionCreated({ graph }));

View File

@ -1,23 +1,23 @@
import { startAppListening } from '..'; import { logger } from 'app/logging/logger';
import { sessionCreated } from 'services/api/thunks/session';
import { buildNodesGraph } from 'features/nodes/util/graphBuilders/buildNodesGraph';
import { log } from 'app/logging/useLogger';
import { nodesGraphBuilt } from 'features/nodes/store/actions';
import { userInvoked } from 'app/store/actions'; import { userInvoked } from 'app/store/actions';
import { parseify } from 'common/util/serialize';
import { nodesGraphBuilt } from 'features/nodes/store/actions';
import { buildNodesGraph } from 'features/nodes/util/graphBuilders/buildNodesGraph';
import { sessionReadyToInvoke } from 'features/system/store/actions'; import { sessionReadyToInvoke } from 'features/system/store/actions';
import { sessionCreated } from 'services/api/thunks/session';
const moduleLog = log.child({ namespace: 'invoke' }); import { startAppListening } from '..';
export const addUserInvokedNodesListener = () => { export const addUserInvokedNodesListener = () => {
startAppListening({ startAppListening({
predicate: (action): action is ReturnType<typeof userInvoked> => predicate: (action): action is ReturnType<typeof userInvoked> =>
userInvoked.match(action) && action.payload === 'nodes', userInvoked.match(action) && action.payload === 'nodes',
effect: async (action, { getState, dispatch, take }) => { effect: async (action, { getState, dispatch, take }) => {
const log = logger('session');
const state = getState(); const state = getState();
const graph = buildNodesGraph(state); const graph = buildNodesGraph(state);
dispatch(nodesGraphBuilt(graph)); dispatch(nodesGraphBuilt(graph));
moduleLog.debug({ data: graph }, 'Nodes graph built'); log.debug({ graph: parseify(graph) }, 'Nodes graph built');
dispatch(sessionCreated({ graph })); dispatch(sessionCreated({ graph }));

View File

@ -1,25 +1,25 @@
import { startAppListening } from '..'; import { logger } from 'app/logging/logger';
import { sessionCreated } from 'services/api/thunks/session';
import { log } from 'app/logging/useLogger';
import { textToImageGraphBuilt } from 'features/nodes/store/actions';
import { userInvoked } from 'app/store/actions'; import { userInvoked } from 'app/store/actions';
import { sessionReadyToInvoke } from 'features/system/store/actions'; import { parseify } from 'common/util/serialize';
import { textToImageGraphBuilt } from 'features/nodes/store/actions';
import { buildLinearTextToImageGraph } from 'features/nodes/util/graphBuilders/buildLinearTextToImageGraph'; import { buildLinearTextToImageGraph } from 'features/nodes/util/graphBuilders/buildLinearTextToImageGraph';
import { sessionReadyToInvoke } from 'features/system/store/actions';
const moduleLog = log.child({ namespace: 'invoke' }); import { sessionCreated } from 'services/api/thunks/session';
import { startAppListening } from '..';
export const addUserInvokedTextToImageListener = () => { export const addUserInvokedTextToImageListener = () => {
startAppListening({ startAppListening({
predicate: (action): action is ReturnType<typeof userInvoked> => predicate: (action): action is ReturnType<typeof userInvoked> =>
userInvoked.match(action) && action.payload === 'txt2img', userInvoked.match(action) && action.payload === 'txt2img',
effect: async (action, { getState, dispatch, take }) => { effect: async (action, { getState, dispatch, take }) => {
const log = logger('session');
const state = getState(); const state = getState();
const graph = buildLinearTextToImageGraph(state); const graph = buildLinearTextToImageGraph(state);
dispatch(textToImageGraphBuilt(graph)); dispatch(textToImageGraphBuilt(graph));
moduleLog.debug({ data: graph }, 'Text to Image graph built'); log.debug({ graph: parseify(graph) }, 'Text to Image graph built');
dispatch(sessionCreated({ graph })); dispatch(sessionCreated({ graph }));

View File

@ -5,13 +5,9 @@ import {
combineReducers, combineReducers,
configureStore, configureStore,
} from '@reduxjs/toolkit'; } from '@reduxjs/toolkit';
import dynamicMiddlewares from 'redux-dynamic-middlewares';
import { rememberEnhancer, rememberReducer } from 'redux-remember';
import canvasReducer from 'features/canvas/store/canvasSlice'; import canvasReducer from 'features/canvas/store/canvasSlice';
import controlNetReducer from 'features/controlNet/store/controlNetSlice'; import controlNetReducer from 'features/controlNet/store/controlNetSlice';
import dynamicPromptsReducer from 'features/dynamicPrompts/store/slice'; import dynamicPromptsReducer from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import boardsReducer from 'features/gallery/store/boardSlice'; import boardsReducer from 'features/gallery/store/boardSlice';
import galleryReducer from 'features/gallery/store/gallerySlice'; import galleryReducer from 'features/gallery/store/gallerySlice';
import imageDeletionReducer from 'features/imageDeletion/store/imageDeletionSlice'; import imageDeletionReducer from 'features/imageDeletion/store/imageDeletionSlice';
@ -24,9 +20,8 @@ import systemReducer from 'features/system/store/systemSlice';
import modelmanagerReducer from 'features/ui/components/tabs/ModelManager/store/modelManagerSlice'; import modelmanagerReducer from 'features/ui/components/tabs/ModelManager/store/modelManagerSlice';
import hotkeysReducer from 'features/ui/store/hotkeysSlice'; import hotkeysReducer from 'features/ui/store/hotkeysSlice';
import uiReducer from 'features/ui/store/uiSlice'; import uiReducer from 'features/ui/store/uiSlice';
import dynamicMiddlewares from 'redux-dynamic-middlewares';
import { listenerMiddleware } from './middleware/listenerMiddleware'; import { rememberEnhancer, rememberReducer } from 'redux-remember';
import { api } from 'services/api'; import { api } from 'services/api';
import { LOCALSTORAGE_PREFIX } from './constants'; import { LOCALSTORAGE_PREFIX } from './constants';
import { serialize } from './enhancers/reduxRemember/serialize'; import { serialize } from './enhancers/reduxRemember/serialize';
@ -34,6 +29,7 @@ import { unserialize } from './enhancers/reduxRemember/unserialize';
import { actionSanitizer } from './middleware/devtools/actionSanitizer'; import { actionSanitizer } from './middleware/devtools/actionSanitizer';
import { actionsDenylist } from './middleware/devtools/actionsDenylist'; import { actionsDenylist } from './middleware/devtools/actionsDenylist';
import { stateSanitizer } from './middleware/devtools/stateSanitizer'; import { stateSanitizer } from './middleware/devtools/stateSanitizer';
import { listenerMiddleware } from './middleware/listenerMiddleware';
const allReducers = { const allReducers = {
canvas: canvasReducer, canvas: canvasReducer,
@ -121,6 +117,7 @@ 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>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type AppThunkDispatch = ThunkDispatch<RootState, any, AnyAction>; export type AppThunkDispatch = ThunkDispatch<RootState, any, AnyAction>;
export type AppDispatch = typeof store.dispatch; export type AppDispatch = typeof store.dispatch;
export const stateSelector = (state: RootState) => state; export const stateSelector = (state: RootState) => state;

View File

@ -1,8 +0,0 @@
import { createAsyncThunk } from '@reduxjs/toolkit';
import { AppDispatch, RootState } from 'app/store/store';
// https://redux-toolkit.js.org/usage/usage-with-typescript#defining-a-pre-typed-createasyncthunk
export const createAppAsyncThunk = createAsyncThunk.withTypes<{
state: RootState;
dispatch: AppDispatch;
}>();

View File

@ -1,23 +0,0 @@
import { Box, forwardRef, Icon } from '@chakra-ui/react';
import { Feature } from 'app/features';
import { memo } from 'react';
import { IconType } from 'react-icons';
import { MdHelp } from 'react-icons/md';
import GuidePopover from './GuidePopover';
type GuideIconProps = {
feature: Feature;
icon?: IconType;
};
const GuideIcon = forwardRef(
({ feature, icon = MdHelp }: GuideIconProps, ref) => (
<GuidePopover feature={feature}>
<Box ref={ref}>
<Icon marginBottom="-.15rem" as={icon} />
</Box>
</GuidePopover>
)
);
export default memo(GuideIcon);

View File

@ -1,49 +0,0 @@
import {
Box,
Popover,
PopoverArrow,
PopoverBody,
PopoverContent,
PopoverTrigger,
} from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { Feature, useFeatureHelpInfo } from 'app/features';
import { useAppSelector } from 'app/store/storeHooks';
import { systemSelector } from 'features/system/store/systemSelectors';
import { SystemState } from 'features/system/store/systemSlice';
import { memo, ReactElement } from 'react';
type GuideProps = {
children: ReactElement;
feature: Feature;
};
const guidePopoverSelector = createSelector(
systemSelector,
(system: SystemState) => system.shouldDisplayGuides
);
const GuidePopover = ({ children, feature }: GuideProps) => {
const shouldDisplayGuides = useAppSelector(guidePopoverSelector);
const { text } = useFeatureHelpInfo(feature);
if (!shouldDisplayGuides) return null;
return (
<Popover trigger="hover" isLazy>
<PopoverTrigger>
<Box>{children}</Box>
</PopoverTrigger>
<PopoverContent
maxWidth="400px"
onClick={(e) => e.preventDefault()}
cursor="initial"
>
<PopoverArrow />
<PopoverBody>{text}</PopoverBody>
</PopoverContent>
</Popover>
);
};
export default memo(GuidePopover);

View File

@ -169,7 +169,9 @@ const IAIDndImage = (props: IAIDndImageProps) => {
...imageSx, ...imageSx,
}} }}
/> />
{withMetadataOverlay && <ImageMetadataOverlay image={imageDTO} />} {withMetadataOverlay && (
<ImageMetadataOverlay imageDTO={imageDTO} />
)}
<SelectionOverlay <SelectionOverlay
isSelected={isSelected} isSelected={isSelected}
isHovered={withHoverOverlay ? isHovered : false} isHovered={withHoverOverlay ? isHovered : false}

View File

@ -13,11 +13,10 @@ type IAIDroppableProps = {
dropLabel?: ReactNode; dropLabel?: ReactNode;
disabled?: boolean; disabled?: boolean;
data?: TypesafeDroppableData; data?: TypesafeDroppableData;
hoverRef?: React.Ref<HTMLDivElement>;
}; };
const IAIDroppable = (props: IAIDroppableProps) => { const IAIDroppable = (props: IAIDroppableProps) => {
const { dropLabel, data, disabled, hoverRef } = props; const { dropLabel, data, disabled } = props;
const dndId = useRef(uuidv4()); const dndId = useRef(uuidv4());
const { isOver, setNodeRef, active } = useDroppable({ const { isOver, setNodeRef, active } = useDroppable({

View File

@ -10,7 +10,10 @@ interface ItemProps extends React.ComponentPropsWithoutRef<'div'> {
} }
const IAIMantineSelectItemWithTooltip = forwardRef<HTMLDivElement, ItemProps>( const IAIMantineSelectItemWithTooltip = forwardRef<HTMLDivElement, ItemProps>(
({ label, tooltip, description, disabled, ...others }: ItemProps, ref) => ( (
{ label, tooltip, description, disabled: _disabled, ...others }: ItemProps,
ref
) => (
<Tooltip label={tooltip} placement="top" hasArrow openDelay={500}> <Tooltip label={tooltip} placement="top" hasArrow openDelay={500}>
<Box ref={ref} {...others}> <Box ref={ref} {...others}>
<Box> <Box>

View File

@ -1,5 +1,4 @@
import { import {
ChakraProps,
FormControl, FormControl,
FormControlProps, FormControlProps,
FormLabel, FormLabel,
@ -24,16 +23,15 @@ import {
Tooltip, Tooltip,
TooltipProps, TooltipProps,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { clamp } from 'lodash-es';
import { useAppDispatch } from 'app/store/storeHooks'; import { useAppDispatch } from 'app/store/storeHooks';
import { roundDownToMultiple } from 'common/util/roundDownToMultiple'; import { roundDownToMultiple } from 'common/util/roundDownToMultiple';
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice'; import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
import { clamp } from 'lodash-es';
import { import {
FocusEvent, FocusEvent,
KeyboardEvent, KeyboardEvent,
memo,
MouseEvent, MouseEvent,
memo,
useCallback, useCallback,
useEffect, useEffect,
useMemo, useMemo,

View File

@ -1,21 +1,11 @@
import { Badge, Flex } from '@chakra-ui/react'; import { Badge, Flex } from '@chakra-ui/react';
import { isString } from 'lodash-es';
import { useMemo } from 'react';
import { ImageDTO } from 'services/api/types'; import { ImageDTO } from 'services/api/types';
type ImageMetadataOverlayProps = { type ImageMetadataOverlayProps = {
image: ImageDTO; imageDTO: ImageDTO;
}; };
const ImageMetadataOverlay = ({ image }: ImageMetadataOverlayProps) => { const ImageMetadataOverlay = ({ imageDTO }: ImageMetadataOverlayProps) => {
const model = useMemo(() => {
if (!isString(image.metadata?.model)) {
return;
}
return image.metadata?.model;
}, [image.metadata]);
return ( return (
<Flex <Flex
sx={{ sx={{
@ -30,13 +20,8 @@ const ImageMetadataOverlay = ({ image }: ImageMetadataOverlayProps) => {
}} }}
> >
<Badge variant="solid" colorScheme="base"> <Badge variant="solid" colorScheme="base">
{image.width} × {image.height} {imageDTO.width} × {imageDTO.height}
</Badge> </Badge>
{model && (
<Badge variant="solid" colorScheme="base">
{model}
</Badge>
)}
</Flex> </Flex>
); );
}; };

View File

@ -2,17 +2,16 @@ import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store'; import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { validateSeedWeights } from 'common/util/seedWeightPairs'; // import { validateSeedWeights } from 'common/util/seedWeightPairs';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { modelsApi } from '../../services/api/endpoints/models';
import { forEach } from 'lodash-es'; import { forEach } from 'lodash-es';
import { modelsApi } from '../../services/api/endpoints/models';
const readinessSelector = createSelector( const readinessSelector = createSelector(
[stateSelector, activeTabNameSelector], [stateSelector, activeTabNameSelector],
(state, activeTabName) => { (state, activeTabName) => {
const { generation, system } = state; const { generation, system } = state;
const { shouldGenerateVariations, seedWeights, initialImage, seed } = const { initialImage } = generation;
generation;
const { isProcessing, isConnected } = system; const { isProcessing, isConnected } = system;
@ -44,19 +43,19 @@ const readinessSelector = createSelector(
reasonsWhyNotReady.push('System Disconnected'); reasonsWhyNotReady.push('System Disconnected');
} }
// Cannot generate variations without valid seed weights // // Cannot generate variations without valid seed weights
if ( // if (
shouldGenerateVariations && // shouldGenerateVariations &&
(!(validateSeedWeights(seedWeights) || seedWeights === '') || seed === -1) // (!(validateSeedWeights(seedWeights) || seedWeights === '') || seed === -1)
) { // ) {
isReady = false; // isReady = false;
reasonsWhyNotReady.push('Seed-Weights badly formatted.'); // reasonsWhyNotReady.push('Seed-Weights badly formatted.');
} // }
forEach(state.controlNet.controlNets, (controlNet, id) => { forEach(state.controlNet.controlNets, (controlNet, id) => {
if (!controlNet.model) { if (!controlNet.model) {
isReady = false; isReady = false;
reasonsWhyNotReady.push('ControlNet ${id} has no model selected.'); reasonsWhyNotReady.push(`ControlNet ${id} has no model selected.`);
} }
}); });

View File

@ -1,31 +0,0 @@
import * as InvokeAI from 'app/types/invokeai';
import promptToString from './promptToString';
export function getPromptAndNegative(inputPrompt: InvokeAI.Prompt) {
let prompt: string =
typeof inputPrompt === 'string' ? inputPrompt : promptToString(inputPrompt);
let negativePrompt = '';
// Matches all negative prompts, 1st capturing group is the prompt itself
const negativePromptRegExp = new RegExp(/\[([^\][]*)]/, 'gi');
// Grab the actual prompt matches (capturing group 1 is 1st index of match)
const negativePromptMatches = [...prompt.matchAll(negativePromptRegExp)].map(
(match) => match[1]
);
if (negativePromptMatches.length) {
// Build the negative prompt itself
negativePrompt = negativePromptMatches.join(' ');
// Replace each match, including its surrounding brackets
// Remove each pair of empty brackets
// Trim whitespace
negativePromptMatches.forEach((match) => {
prompt = prompt.replace(`[${match}]`, '').replaceAll('[]', '').trim();
});
}
return [prompt, negativePrompt];
}

View File

@ -1,20 +0,0 @@
import * as InvokeAI from 'app/types/invokeai';
const promptToString = (prompt: InvokeAI.Prompt): string => {
if (typeof prompt === 'string') {
return prompt;
}
if (prompt.length === 1) {
return prompt[0].prompt;
}
return prompt
.map(
(promptItem: InvokeAI.PromptItem): string =>
`${promptItem.prompt}:${promptItem.weight}`
)
.join(' ');
};
export default promptToString;

View File

@ -1,68 +1,71 @@
import * as InvokeAI from 'app/types/invokeai'; // TODO: Restore variations
// Support code from v2.3 in here.
export const stringToSeedWeights = ( // export const stringToSeedWeights = (
string: string // string: string
): InvokeAI.SeedWeights | boolean => { // ): InvokeAI.SeedWeights | boolean => {
const stringPairs = string.split(','); // const stringPairs = string.split(',');
const arrPairs = stringPairs.map((p) => p.split(':')); // const arrPairs = stringPairs.map((p) => p.split(':'));
const pairs = arrPairs.map((p: Array<string>): InvokeAI.SeedWeightPair => { // const pairs = arrPairs.map((p: Array<string>): InvokeAI.SeedWeightPair => {
return { seed: Number(p[0]), weight: Number(p[1]) }; // return { seed: Number(p[0]), weight: Number(p[1]) };
}); // });
if (!validateSeedWeights(pairs)) { // if (!validateSeedWeights(pairs)) {
return false; // return false;
} // }
return pairs; // return pairs;
}; // };
export const validateSeedWeights = ( // export const validateSeedWeights = (
seedWeights: InvokeAI.SeedWeights | string // seedWeights: InvokeAI.SeedWeights | string
): boolean => { // ): boolean => {
return typeof seedWeights === 'string' // return typeof seedWeights === 'string'
? Boolean(stringToSeedWeights(seedWeights)) // ? Boolean(stringToSeedWeights(seedWeights))
: Boolean( // : Boolean(
seedWeights.length && // seedWeights.length &&
!seedWeights.some((pair: InvokeAI.SeedWeightPair) => { // !seedWeights.some((pair: InvokeAI.SeedWeightPair) => {
const { seed, weight } = pair; // const { seed, weight } = pair;
const isSeedValid = !isNaN(parseInt(seed.toString(), 10)); // const isSeedValid = !isNaN(parseInt(seed.toString(), 10));
const isWeightValid = // const isWeightValid =
!isNaN(parseInt(weight.toString(), 10)) && // !isNaN(parseInt(weight.toString(), 10)) &&
weight >= 0 && // weight >= 0 &&
weight <= 1; // weight <= 1;
return !(isSeedValid && isWeightValid); // return !(isSeedValid && isWeightValid);
}) // })
); // );
}; // };
export const seedWeightsToString = ( // export const seedWeightsToString = (
seedWeights: InvokeAI.SeedWeights // seedWeights: InvokeAI.SeedWeights
): string => { // ): string => {
return seedWeights.reduce((acc, pair, i, arr) => { // return seedWeights.reduce((acc, pair, i, arr) => {
const { seed, weight } = pair; // const { seed, weight } = pair;
acc += `${seed}:${weight}`; // acc += `${seed}:${weight}`;
if (i !== arr.length - 1) { // if (i !== arr.length - 1) {
acc += ','; // acc += ',';
} // }
return acc; // return acc;
}, ''); // }, '');
}; // };
export const seedWeightsToArray = ( // export const seedWeightsToArray = (
seedWeights: InvokeAI.SeedWeights // seedWeights: InvokeAI.SeedWeights
): Array<Array<number>> => { // ): Array<Array<number>> => {
return seedWeights.map((pair: InvokeAI.SeedWeightPair) => [ // return seedWeights.map((pair: InvokeAI.SeedWeightPair) => [
pair.seed, // pair.seed,
pair.weight, // pair.weight,
]); // ]);
}; // };
export const stringToSeedWeightsArray = ( // export const stringToSeedWeightsArray = (
string: string // string: string
): Array<Array<number>> => { // ): Array<Array<number>> => {
const stringPairs = string.split(','); // const stringPairs = string.split(',');
const arrPairs = stringPairs.map((p) => p.split(':')); // const arrPairs = stringPairs.map((p) => p.split(':'));
return arrPairs.map( // return arrPairs.map(
(p: Array<string>): Array<number> => [parseInt(p[0], 10), parseFloat(p[1])] // (p: Array<string>): Array<number> => [parseInt(p[0], 10), parseFloat(p[1])]
); // );
}; // };
export default {};

View File

@ -0,0 +1,4 @@
/**
* Serialize an object to JSON and back to a new object
*/
export const parseify = (obj: unknown) => JSON.parse(JSON.stringify(obj));

View File

@ -1,6 +1,7 @@
import { Box, chakra, Flex } from '@chakra-ui/react'; import { Box, chakra, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { import {
canvasSelector, canvasSelector,
isStagingSelector, isStagingSelector,
@ -8,8 +9,6 @@ import {
import Konva from 'konva'; import Konva from 'konva';
import { KonvaEventObject } from 'konva/lib/Node'; import { KonvaEventObject } from 'konva/lib/Node';
import { Vector2d } from 'konva/lib/types'; import { Vector2d } from 'konva/lib/types';
import { isEqual } from 'lodash-es';
import { useCallback, useRef } from 'react'; import { useCallback, useRef } from 'react';
import { Layer, Stage } from 'react-konva'; import { Layer, Stage } from 'react-konva';
import useCanvasDragMove from '../hooks/useCanvasDragMove'; import useCanvasDragMove from '../hooks/useCanvasDragMove';
@ -34,7 +33,6 @@ import IAICanvasStagingAreaToolbar from './IAICanvasStagingAreaToolbar';
import IAICanvasStatusText from './IAICanvasStatusText'; import IAICanvasStatusText from './IAICanvasStatusText';
import IAICanvasBoundingBox from './IAICanvasToolbar/IAICanvasBoundingBox'; import IAICanvasBoundingBox from './IAICanvasToolbar/IAICanvasBoundingBox';
import IAICanvasToolPreview from './IAICanvasToolPreview'; import IAICanvasToolPreview from './IAICanvasToolPreview';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
const selector = createSelector( const selector = createSelector(
[canvasSelector, isStagingSelector], [canvasSelector, isStagingSelector],

View File

@ -1,10 +1,10 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { import {
roundDownToMultiple, roundDownToMultiple,
roundToMultiple, roundToMultiple,
} from 'common/util/roundDownToMultiple'; } from 'common/util/roundDownToMultiple';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import { import {
setBoundingBoxCoordinates, setBoundingBoxCoordinates,
setBoundingBoxDimensions, setBoundingBoxDimensions,
@ -13,7 +13,6 @@ import {
setIsTransformingBoundingBox, setIsTransformingBoundingBox,
setShouldSnapToGrid, setShouldSnapToGrid,
} from 'features/canvas/store/canvasSlice'; } from 'features/canvas/store/canvasSlice';
import { uiSelector } from 'features/ui/store/uiSelectors';
import Konva from 'konva'; import Konva from 'konva';
import { GroupConfig } from 'konva/lib/Group'; import { GroupConfig } from 'konva/lib/Group';
import { KonvaEventObject } from 'konva/lib/Node'; import { KonvaEventObject } from 'konva/lib/Node';
@ -25,8 +24,8 @@ import { useHotkeys } from 'react-hotkeys-hook';
import { Group, Rect, Transformer } from 'react-konva'; import { Group, Rect, Transformer } from 'react-konva';
const boundingBoxPreviewSelector = createSelector( const boundingBoxPreviewSelector = createSelector(
[canvasSelector, uiSelector], [stateSelector],
(canvas, ui) => { ({ canvas, generation }) => {
const { const {
boundingBoxCoordinates, boundingBoxCoordinates,
boundingBoxDimensions, boundingBoxDimensions,
@ -38,7 +37,7 @@ const boundingBoxPreviewSelector = createSelector(
shouldSnapToGrid, shouldSnapToGrid,
} = canvas; } = canvas;
const { aspectRatio } = ui; const { aspectRatio } = generation;
return { return {
boundingBoxCoordinates, boundingBoxCoordinates,

View File

@ -4,14 +4,13 @@ import {
roundDownToMultiple, roundDownToMultiple,
roundToMultiple, roundToMultiple,
} from 'common/util/roundDownToMultiple'; } from 'common/util/roundDownToMultiple';
import { IRect, Vector2d } from 'konva/lib/types'; import { setAspectRatio } from 'features/parameters/store/generationSlice';
import { clamp, cloneDeep } from 'lodash-es';
//
import { import {
setActiveTab, setActiveTab,
setAspectRatio,
setShouldUseCanvasBetaLayout, setShouldUseCanvasBetaLayout,
} from 'features/ui/store/uiSlice'; } from 'features/ui/store/uiSlice';
import { IRect, Vector2d } from 'konva/lib/types';
import { clamp, cloneDeep } from 'lodash-es';
import { RgbaColor } from 'react-colorful'; import { RgbaColor } from 'react-colorful';
import { sessionCanceled } from 'services/api/thunks/session'; import { sessionCanceled } from 'services/api/thunks/session';
import { ImageDTO } from 'services/api/types'; import { ImageDTO } from 'services/api/types';
@ -714,7 +713,7 @@ export const canvasSlice = createSlice({
}, },
commitStagingAreaImage: ( commitStagingAreaImage: (
state, state,
action: PayloadAction<string | undefined> _action: PayloadAction<string | undefined>
) => { ) => {
if (!state.layerState.stagingArea.images.length) { if (!state.layerState.stagingArea.images.length) {
return; return;
@ -867,11 +866,11 @@ export const canvasSlice = createSlice({
} }
}); });
builder.addCase(setShouldUseCanvasBetaLayout, (state, action) => { builder.addCase(setShouldUseCanvasBetaLayout, (state) => {
state.doesCanvasNeedScaling = true; state.doesCanvasNeedScaling = true;
}); });
builder.addCase(setActiveTab, (state, action) => { builder.addCase(setActiveTab, (state) => {
state.doesCanvasNeedScaling = true; state.doesCanvasNeedScaling = true;
}); });
builder.addCase(setAspectRatio, (state, action) => { builder.addCase(setAspectRatio, (state, action) => {
@ -883,26 +882,6 @@ export const canvasSlice = createSlice({
); );
} }
}); });
// builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
// const { image_name, image_url, thumbnail_url } = action.payload;
// state.layerState.objects.forEach((object) => {
// if (object.kind === 'image') {
// if (object.image.image_name === image_name) {
// object.image.image_url = image_url;
// object.image.thumbnail_url = thumbnail_url;
// }
// }
// });
// state.layerState.stagingArea.images.forEach((stagedImage) => {
// if (stagedImage.image.image_name === image_name) {
// stagedImage.image.image_url = image_url;
// stagedImage.image.thumbnail_url = thumbnail_url;
// }
// });
// });
}, },
}); });

View File

@ -1,7 +1,5 @@
import * as InvokeAI from 'app/types/invokeai';
import { IRect, Vector2d } from 'konva/lib/types'; import { IRect, Vector2d } from 'konva/lib/types';
import { RgbaColor } from 'react-colorful'; import { RgbaColor } from 'react-colorful';
import { ImageDTO } from 'services/api/types';
export const LAYER_NAMES_DICT = [ export const LAYER_NAMES_DICT = [
{ label: 'Base', value: 'base' }, { label: 'Base', value: 'base' },
@ -133,7 +131,6 @@ export interface CanvasState {
cursorPosition: Vector2d | null; cursorPosition: Vector2d | null;
doesCanvasNeedScaling: boolean; doesCanvasNeedScaling: boolean;
futureLayerStates: CanvasLayerState[]; futureLayerStates: CanvasLayerState[];
intermediateImage?: InvokeAI.Image;
isCanvasInitialized: boolean; isCanvasInitialized: boolean;
isDrawing: boolean; isDrawing: boolean;
isMaskEnabled: boolean; isMaskEnabled: boolean;

View File

@ -1,22 +1,22 @@
import { logger } from 'app/logging/logger';
import { RootState } from 'app/store/store'; import { RootState } from 'app/store/store';
import { getCanvasBaseLayer, getCanvasStage } from './konvaInstanceProvider';
import { isCanvasMaskLine } from '../store/canvasTypes'; import { isCanvasMaskLine } from '../store/canvasTypes';
import { log } from 'app/logging/useLogger';
import createMaskStage from './createMaskStage'; import createMaskStage from './createMaskStage';
import { konvaNodeToImageData } from './konvaNodeToImageData'; import { getCanvasBaseLayer, getCanvasStage } from './konvaInstanceProvider';
import { konvaNodeToBlob } from './konvaNodeToBlob'; import { konvaNodeToBlob } from './konvaNodeToBlob';
import { konvaNodeToImageData } from './konvaNodeToImageData';
const moduleLog = log.child({ namespace: 'getCanvasDataURLs' });
/** /**
* Gets Blob and ImageData objects for the base and mask layers * Gets Blob and ImageData objects for the base and mask layers
*/ */
export const getCanvasData = async (state: RootState) => { export const getCanvasData = async (state: RootState) => {
const log = logger('canvas');
const canvasBaseLayer = getCanvasBaseLayer(); const canvasBaseLayer = getCanvasBaseLayer();
const canvasStage = getCanvasStage(); const canvasStage = getCanvasStage();
if (!canvasBaseLayer || !canvasStage) { if (!canvasBaseLayer || !canvasStage) {
moduleLog.error('Unable to find canvas / stage'); log.error('Unable to find canvas / stage');
return; return;
} }

View File

@ -11,8 +11,8 @@ import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIDndImage from 'common/components/IAIDndImage'; import IAIDndImage from 'common/components/IAIDndImage';
import { memo, useCallback, useMemo, useState } from 'react'; import { memo, useCallback, useMemo, useState } from 'react';
import { useGetImageDTOQuery } from 'services/api/endpoints/images'; import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import { controlNetImageChanged } from '../store/controlNetSlice';
import { PostUploadAction } from 'services/api/types'; import { PostUploadAction } from 'services/api/types';
import { controlNetImageChanged } from '../store/controlNetSlice';
type Props = { type Props = {
controlNetId: string; controlNetId: string;
@ -59,19 +59,13 @@ const ControlNetImagePreview = (props: Props) => {
const [isMouseOverImage, setIsMouseOverImage] = useState(false); const [isMouseOverImage, setIsMouseOverImage] = useState(false);
const { const { currentData: controlImage } = useGetImageDTOQuery(
currentData: controlImage, controlImageName ?? skipToken
isLoading: isLoadingControlImage, );
isError: isErrorControlImage,
isSuccess: isSuccessControlImage,
} = useGetImageDTOQuery(controlImageName ?? skipToken);
const { const { currentData: processedControlImage } = useGetImageDTOQuery(
currentData: processedControlImage, processedControlImageName ?? skipToken
isLoading: isLoadingProcessedControlImage, );
isError: isErrorProcessedControlImage,
isSuccess: isSuccessProcessedControlImage,
} = useGetImageDTOQuery(processedControlImageName ?? skipToken);
const handleResetControlImage = useCallback(() => { const handleResetControlImage = useCallback(() => {
dispatch(controlNetImageChanged({ controlNetId, controlImage: null })); dispatch(controlNetImageChanged({ controlNetId, controlImage: null }));

View File

@ -55,11 +55,6 @@ const ParamControlNetBeginEnd = (props: Props) => {
[controlNetId, dispatch] [controlNetId, dispatch]
); );
const handleStepPctReset = useCallback(() => {
dispatch(controlNetBeginStepPctChanged({ controlNetId, beginStepPct: 0 }));
dispatch(controlNetEndStepPctChanged({ controlNetId, endStepPct: 1 }));
}, [controlNetId, dispatch]);
return ( return (
<FormControl isDisabled={!isEnabled}> <FormControl isDisabled={!isEnabled}>
<FormLabel>Begin / End Step Percentage</FormLabel> <FormLabel>Begin / End Step Percentage</FormLabel>

View File

@ -8,7 +8,6 @@ import {
controlNetControlModeChanged, controlNetControlModeChanged,
} from 'features/controlNet/store/controlNetSlice'; } from 'features/controlNet/store/controlNetSlice';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
type ParamControlNetControlModeProps = { type ParamControlNetControlModeProps = {
controlNetId: string; controlNetId: string;
@ -42,8 +41,6 @@ export default function ParamControlNetControlMode(
const { controlMode, isEnabled } = useAppSelector(selector); const { controlMode, isEnabled } = useAppSelector(selector);
const { t } = useTranslation();
const handleControlModeChange = useCallback( const handleControlModeChange = useCallback(
(controlMode: ControlModes) => { (controlMode: ControlModes) => {
dispatch(controlNetControlModeChanged({ controlNetId, controlMode })); dispatch(controlNetControlModeChanged({ controlNetId, controlMode }));

View File

@ -13,7 +13,6 @@ import { memo, useCallback, useMemo } from 'react';
import { CONTROLNET_PROCESSORS } from '../../store/constants'; import { CONTROLNET_PROCESSORS } from '../../store/constants';
import { controlNetProcessorTypeChanged } from '../../store/controlNetSlice'; import { controlNetProcessorTypeChanged } from '../../store/controlNetSlice';
import { ControlNetProcessorType } from '../../store/types'; import { ControlNetProcessorType } from '../../store/types';
import { FormControl, FormLabel } from '@chakra-ui/react';
type ParamControlNetProcessorSelectProps = { type ParamControlNetProcessorSelectProps = {
controlNetId: string; controlNetId: string;

View File

@ -8,7 +8,6 @@ import {
controlNetResizeModeChanged, controlNetResizeModeChanged,
} from 'features/controlNet/store/controlNetSlice'; } from 'features/controlNet/store/controlNetSlice';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
type ParamControlNetResizeModeProps = { type ParamControlNetResizeModeProps = {
controlNetId: string; controlNetId: string;
@ -41,8 +40,6 @@ export default function ParamControlNetResizeMode(
const { resizeMode, isEnabled } = useAppSelector(selector); const { resizeMode, isEnabled } = useAppSelector(selector);
const { t } = useTranslation();
const handleResizeModeChange = useCallback( const handleResizeModeChange = useCallback(
(resizeMode: ResizeModes) => { (resizeMode: ResizeModes) => {
dispatch(controlNetResizeModeChanged({ controlNetId, resizeMode })); dispatch(controlNetResizeModeChanged({ controlNetId, resizeMode }));

View File

@ -7,7 +7,7 @@ type Props = {
isEnabled: boolean; isEnabled: boolean;
}; };
const ZoeDepthProcessor = (props: Props) => { const ZoeDepthProcessor = (_props: Props) => {
// Has no parameters? // Has no parameters?
return null; return null;
}; };

View File

@ -1,5 +1,4 @@
import { PayloadAction, createSlice } from '@reduxjs/toolkit'; import { PayloadAction, createSlice } from '@reduxjs/toolkit';
import { RootState } from 'app/store/store';
import { ControlNetModelParam } from 'features/parameters/types/parameterSchemas'; import { ControlNetModelParam } from 'features/parameters/types/parameterSchemas';
import { cloneDeep, forEach } from 'lodash-es'; import { cloneDeep, forEach } from 'lodash-es';
import { imagesApi } from 'services/api/endpoints/images'; import { imagesApi } from 'services/api/endpoints/images';
@ -315,11 +314,11 @@ export const controlNetSlice = createSlice({
} }
}); });
builder.addCase(appSocketInvocationError, (state, action) => { builder.addCase(appSocketInvocationError, (state) => {
state.pendingControlImages = []; state.pendingControlImages = [];
}); });
builder.addMatcher(isAnySessionRejected, (state, action) => { builder.addMatcher(isAnySessionRejected, (state) => {
state.pendingControlImages = []; state.pendingControlImages = [];
}); });
@ -365,5 +364,3 @@ export const {
} = controlNetSlice.actions; } = controlNetSlice.actions;
export default controlNetSlice.reducer; export default controlNetSlice.reducer;
export const controlNetSelector = (state: RootState) => state.controlNet;

View File

@ -4,7 +4,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISwitch from 'common/components/IAISwitch'; import IAISwitch from 'common/components/IAISwitch';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { combinatorialToggled } from '../store/slice'; import { combinatorialToggled } from '../store/dynamicPromptsSlice';
const selector = createSelector( const selector = createSelector(
stateSelector, stateSelector,

View File

@ -4,7 +4,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISwitch from 'common/components/IAISwitch'; import IAISwitch from 'common/components/IAISwitch';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { isEnabledToggled } from '../store/slice'; import { isEnabledToggled } from '../store/dynamicPromptsSlice';
const selector = createSelector( const selector = createSelector(
stateSelector, stateSelector,

View File

@ -4,7 +4,10 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISlider from 'common/components/IAISlider'; import IAISlider from 'common/components/IAISlider';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { maxPromptsChanged, maxPromptsReset } from '../store/slice'; import {
maxPromptsChanged,
maxPromptsReset,
} from '../store/dynamicPromptsSlice';
const selector = createSelector( const selector = createSelector(
stateSelector, stateSelector,

View File

@ -1,5 +1,4 @@
import { PayloadAction, createSlice } from '@reduxjs/toolkit'; import { PayloadAction, createSlice } from '@reduxjs/toolkit';
import { RootState } from 'app/store/store';
export interface DynamicPromptsState { export interface DynamicPromptsState {
isEnabled: boolean; isEnabled: boolean;
@ -32,9 +31,6 @@ export const dynamicPromptsSlice = createSlice({
state.isEnabled = !state.isEnabled; state.isEnabled = !state.isEnabled;
}, },
}, },
extraReducers: (builder) => {
//
},
}); });
export const { export const {
@ -45,6 +41,3 @@ export const {
} = dynamicPromptsSlice.actions; } = dynamicPromptsSlice.actions;
export default dynamicPromptsSlice.reducer; export default dynamicPromptsSlice.reducer;
export const dynamicPromptsSelector = (state: RootState) =>
state.dynamicPrompts;

View File

@ -1,19 +1,16 @@
import { MenuGroup, MenuItem, MenuList } from '@chakra-ui/react'; import { MenuGroup, MenuItem, MenuList } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { ContextMenu, ContextMenuProps } from 'chakra-ui-contextmenu'; import { ContextMenu, ContextMenuProps } from 'chakra-ui-contextmenu';
import { import { autoAddBoardIdChanged } from 'features/gallery/store/gallerySlice';
autoAddBoardIdChanged,
boardIdSelected,
} from 'features/gallery/store/gallerySlice';
import { MouseEvent, memo, useCallback, useMemo } from 'react'; import { MouseEvent, memo, useCallback, useMemo } from 'react';
import { FaFolder, FaPlus } from 'react-icons/fa'; import { FaPlus } from 'react-icons/fa';
import { useBoardName } from 'services/api/hooks/useBoardName';
import { BoardDTO } from 'services/api/types'; import { BoardDTO } from 'services/api/types';
import { menuListMotionProps } from 'theme/components/menu'; import { menuListMotionProps } from 'theme/components/menu';
import GalleryBoardContextMenuItems from './GalleryBoardContextMenuItems'; import GalleryBoardContextMenuItems from './GalleryBoardContextMenuItems';
import NoBoardContextMenuItems from './NoBoardContextMenuItems'; import NoBoardContextMenuItems from './NoBoardContextMenuItems';
import { useBoardName } from 'services/api/hooks/useBoardName';
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
type Props = { type Props = {
board?: BoardDTO; board?: BoardDTO;
@ -29,20 +26,15 @@ const BoardContextMenu = memo(
const selector = useMemo( const selector = useMemo(
() => () =>
createSelector(stateSelector, ({ gallery }) => { createSelector(stateSelector, ({ gallery }) => {
const isSelected = gallery.selectedBoardId === board_id;
const isAutoAdd = gallery.autoAddBoardId === board_id; const isAutoAdd = gallery.autoAddBoardId === board_id;
return { isSelected, isAutoAdd }; return { isAutoAdd };
}), }),
[board_id] [board_id]
); );
const { isSelected, isAutoAdd } = useAppSelector(selector); const { isAutoAdd } = useAppSelector(selector);
const boardName = useBoardName(board_id); const boardName = useBoardName(board_id);
const handleSelectBoard = useCallback(() => {
dispatch(boardIdSelected(board_id));
}, [board_id, dispatch]);
const handleSetAutoAdd = useCallback(() => { const handleSetAutoAdd = useCallback(() => {
dispatch(autoAddBoardIdChanged(board_id)); dispatch(autoAddBoardIdChanged(board_id));
}, [board_id, dispatch]); }, [board_id, dispatch]);

View File

@ -1,21 +1,16 @@
import { ButtonGroup, Collapse, Flex, Grid, GridItem } from '@chakra-ui/react'; import { Collapse, Flex, Grid, GridItem } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store'; import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIIconButton from 'common/components/IAIIconButton';
import { AnimatePresence, motion } from 'framer-motion';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import { memo, useCallback, useState } from 'react'; import { memo, useState } from 'react';
import { FaSearch } from 'react-icons/fa';
import { useListAllBoardsQuery } from 'services/api/endpoints/boards'; import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
import { BoardDTO } from 'services/api/types'; import { BoardDTO } from 'services/api/types';
import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus';
import DeleteBoardModal from '../DeleteBoardModal'; import DeleteBoardModal from '../DeleteBoardModal';
import AddBoardButton from './AddBoardButton'; import AddBoardButton from './AddBoardButton';
import BoardsSearch from './BoardsSearch'; import BoardsSearch from './BoardsSearch';
import GalleryBoard from './GalleryBoard'; import GalleryBoard from './GalleryBoard';
import SystemBoardButton from './SystemBoardButton';
import NoBoardBoard from './NoBoardBoard'; import NoBoardBoard from './NoBoardBoard';
const selector = createSelector( const selector = createSelector(
@ -36,7 +31,6 @@ const BoardsList = (props: Props) => {
const { isOpen } = props; const { isOpen } = props;
const { selectedBoardId, searchText } = useAppSelector(selector); const { selectedBoardId, searchText } = useAppSelector(selector);
const { data: boards } = useListAllBoardsQuery(); const { data: boards } = useListAllBoardsQuery();
const isBatchEnabled = useFeatureStatus('batches').isFeatureEnabled;
const filteredBoards = searchText const filteredBoards = searchText
? boards?.filter((board) => ? boards?.filter((board) =>
board.board_name.toLowerCase().includes(searchText.toLowerCase()) board.board_name.toLowerCase().includes(searchText.toLowerCase())

View File

@ -1,7 +1,5 @@
import { import {
Badge,
Box, Box,
ChakraProps,
Editable, Editable,
EditableInput, EditableInput,
EditablePreview, EditablePreview,
@ -17,21 +15,16 @@ import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIDroppable from 'common/components/IAIDroppable'; import IAIDroppable from 'common/components/IAIDroppable';
import SelectionOverlay from 'common/components/SelectionOverlay';
import { boardIdSelected } from 'features/gallery/store/gallerySlice'; import { boardIdSelected } from 'features/gallery/store/gallerySlice';
import { memo, useCallback, useMemo, useState } from 'react'; import { memo, useCallback, useMemo, useState } from 'react';
import { FaUser } from 'react-icons/fa'; import { FaUser } from 'react-icons/fa';
import { useUpdateBoardMutation } from 'services/api/endpoints/boards'; import { useUpdateBoardMutation } from 'services/api/endpoints/boards';
import { useGetImageDTOQuery } from 'services/api/endpoints/images'; import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import { useBoardTotal } from 'services/api/hooks/useBoardTotal';
import { BoardDTO } from 'services/api/types'; import { BoardDTO } from 'services/api/types';
import AutoAddIcon from '../AutoAddIcon'; import AutoAddIcon from '../AutoAddIcon';
import BoardContextMenu from '../BoardContextMenu'; import BoardContextMenu from '../BoardContextMenu';
import SelectionOverlay from 'common/components/SelectionOverlay';
const BASE_BADGE_STYLES: ChakraProps['sx'] = {
bg: 'base.500',
color: 'whiteAlpha.900',
};
interface GalleryBoardProps { interface GalleryBoardProps {
board: BoardDTO; board: BoardDTO;
isSelected: boolean; isSelected: boolean;
@ -68,8 +61,6 @@ const GalleryBoard = memo(
board.cover_image_name ?? skipToken board.cover_image_name ?? skipToken
); );
const { totalImages, totalAssets } = useBoardTotal(board.board_id);
const { board_name, board_id } = board; const { board_name, board_id } = board;
const [localBoardName, setLocalBoardName] = useState(board_name); const [localBoardName, setLocalBoardName] = useState(board_name);

View File

@ -2,7 +2,7 @@ import { As, Badge, Flex } from '@chakra-ui/react';
import { TypesafeDroppableData } from 'app/components/ImageDnd/typesafeDnd'; import { TypesafeDroppableData } from 'app/components/ImageDnd/typesafeDnd';
import IAIDroppable from 'common/components/IAIDroppable'; import IAIDroppable from 'common/components/IAIDroppable';
import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import { BoardId } from 'features/gallery/store/gallerySlice'; import { BoardId } from 'features/gallery/store/types';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import BoardContextMenu from '../BoardContextMenu'; import BoardContextMenu from '../BoardContextMenu';

View File

@ -1,4 +1,4 @@
import { Box, ChakraProps, Flex, Image, Text } from '@chakra-ui/react'; import { Box, Flex, Image, Text } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { MoveBoardDropData } from 'app/components/ImageDnd/typesafeDnd'; import { MoveBoardDropData } from 'app/components/ImageDnd/typesafeDnd';
import { stateSelector } from 'app/store/store'; import { stateSelector } from 'app/store/store';
@ -10,14 +10,8 @@ import SelectionOverlay from 'common/components/SelectionOverlay';
import { boardIdSelected } from 'features/gallery/store/gallerySlice'; import { boardIdSelected } from 'features/gallery/store/gallerySlice';
import { memo, useCallback, useMemo, useState } from 'react'; import { memo, useCallback, useMemo, useState } from 'react';
import { useBoardName } from 'services/api/hooks/useBoardName'; import { useBoardName } from 'services/api/hooks/useBoardName';
import { useBoardTotal } from 'services/api/hooks/useBoardTotal';
import AutoAddIcon from '../AutoAddIcon'; import AutoAddIcon from '../AutoAddIcon';
import BoardContextMenu from '../BoardContextMenu'; import BoardContextMenu from '../BoardContextMenu';
const BASE_BADGE_STYLES: ChakraProps['sx'] = {
bg: 'base.500',
color: 'whiteAlpha.900',
};
interface Props { interface Props {
isSelected: boolean; isSelected: boolean;
} }
@ -33,7 +27,6 @@ const selector = createSelector(
const NoBoardBoard = memo(({ isSelected }: Props) => { const NoBoardBoard = memo(({ isSelected }: Props) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { totalImages, totalAssets } = useBoardTotal(undefined);
const { autoAddBoardId } = useAppSelector(selector); const { autoAddBoardId } = useAppSelector(selector);
const boardName = useBoardName(undefined); const boardName = useBoardName(undefined);
const handleSelectBoard = useCallback(() => { const handleSelectBoard = useCallback(() => {

View File

@ -11,14 +11,12 @@ import {
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { skipToken } from '@reduxjs/toolkit/dist/query'; import { skipToken } from '@reduxjs/toolkit/dist/query';
import { ImageUsage } from 'app/contexts/AddImageToBoardContext';
import { stateSelector } from 'app/store/store'; import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import IAIButton from 'common/components/IAIButton'; import IAIButton from 'common/components/IAIButton';
import ImageUsageMessage from 'features/imageDeletion/components/ImageUsageMessage'; import ImageUsageMessage from 'features/imageDeletion/components/ImageUsageMessage';
import { import { getImageUsage } from 'features/imageDeletion/store/imageDeletionSelectors';
ImageUsage,
getImageUsage,
} from 'features/imageDeletion/store/imageDeletionSlice';
import { some } from 'lodash-es'; import { some } from 'lodash-es';
import { memo, useCallback, useMemo, useRef } from 'react'; import { memo, useCallback, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';

View File

@ -1,11 +1,6 @@
import { MenuItem } from '@chakra-ui/react'; import { MenuItem } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { memo, useCallback } from 'react';
import { stateSelector } from 'app/store/store'; import { FaTrash } from 'react-icons/fa';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { autoAddBoardIdChanged } from 'features/gallery/store/gallerySlice';
import { memo, useCallback, useMemo } from 'react';
import { FaPlus, FaTrash } from 'react-icons/fa';
import { BoardDTO } from 'services/api/types'; import { BoardDTO } from 'services/api/types';
type Props = { type Props = {
@ -14,25 +9,6 @@ type Props = {
}; };
const GalleryBoardContextMenuItems = ({ board, setBoardToDelete }: Props) => { const GalleryBoardContextMenuItems = ({ board, setBoardToDelete }: Props) => {
const dispatch = useAppDispatch();
const selector = useMemo(
() =>
createSelector(
stateSelector,
({ gallery }) => {
const isSelectedForAutoAdd =
board.board_id === gallery.autoAddBoardId;
return { isSelectedForAutoAdd };
},
defaultSelectorOptions
),
[board.board_id]
);
const { isSelectedForAutoAdd } = useAppSelector(selector);
const handleDelete = useCallback(() => { const handleDelete = useCallback(() => {
if (!setBoardToDelete) { if (!setBoardToDelete) {
return; return;
@ -40,12 +16,6 @@ const GalleryBoardContextMenuItems = ({ board, setBoardToDelete }: Props) => {
setBoardToDelete(board); setBoardToDelete(board);
}, [board, setBoardToDelete]); }, [board, setBoardToDelete]);
const handleToggleAutoAdd = useCallback(() => {
dispatch(
autoAddBoardIdChanged(isSelectedForAutoAdd ? undefined : board.board_id)
);
}, [board.board_id, dispatch, isSelectedForAutoAdd]);
return ( return (
<> <>
{board.image_count > 0 && ( {board.image_count > 0 && (
@ -59,11 +29,6 @@ const GalleryBoardContextMenuItems = ({ board, setBoardToDelete }: Props) => {
</MenuItem> */} </MenuItem> */}
</> </>
)} )}
{/* {!isSelectedForAutoAdd && (
<MenuItem icon={<FaPlus />} onClick={handleToggleAutoAdd}>
Auto-add to this Board
</MenuItem>
)} */}
<MenuItem <MenuItem
sx={{ color: 'error.600', _dark: { color: 'error.300' } }} sx={{ color: 'error.600', _dark: { color: 'error.300' } }}
icon={<FaTrash />} icon={<FaTrash />}

View File

@ -1,19 +1,6 @@
import { MenuItem } from '@chakra-ui/react'; import { memo } from 'react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { autoAddBoardIdChanged } from 'features/gallery/store/gallerySlice';
import { memo, useCallback } from 'react';
import { FaPlus } from 'react-icons/fa';
const NoBoardContextMenuItems = () => { const NoBoardContextMenuItems = () => {
const dispatch = useAppDispatch();
const autoAddBoardId = useAppSelector(
(state) => state.gallery.autoAddBoardId
);
const handleDisableAutoAdd = useCallback(() => {
dispatch(autoAddBoardIdChanged(undefined));
}, [dispatch]);
return ( return (
<> <>
{/* {autoAddBoardId && ( {/* {autoAddBoardId && (

View File

@ -108,7 +108,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
500 500
); );
const { currentData: imageDTO, isFetching } = useGetImageDTOQuery( const { currentData: imageDTO } = useGetImageDTOQuery(
lastSelectedImage ?? skipToken lastSelectedImage ?? skipToken
); );

View File

@ -8,17 +8,17 @@ import {
import { stateSelector } from 'app/store/store'; import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import IAIDndImage from 'common/components/IAIDndImage'; import IAIDndImage from 'common/components/IAIDndImage';
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import { useNextPrevImage } from 'features/gallery/hooks/useNextPrevImage'; import { useNextPrevImage } from 'features/gallery/hooks/useNextPrevImage';
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors'; import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { memo, useCallback, useMemo, useRef, useState } from 'react'; import { memo, useCallback, useMemo, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { FaImage } from 'react-icons/fa';
import { useGetImageDTOQuery } from 'services/api/endpoints/images'; import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import ImageMetadataViewer from '../ImageMetadataViewer/ImageMetadataViewer'; import ImageMetadataViewer from '../ImageMetadataViewer/ImageMetadataViewer';
import NextPrevImageButtons from '../NextPrevImageButtons'; import NextPrevImageButtons from '../NextPrevImageButtons';
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import { FaImage } from 'react-icons/fa';
export const imagesSelector = createSelector( export const imagesSelector = createSelector(
[stateSelector, selectLastSelectedImage], [stateSelector, selectLastSelectedImage],
@ -93,12 +93,7 @@ const CurrentImagePreview = () => {
] ]
); );
const { const { currentData: imageDTO } = useGetImageDTOQuery(imageName ?? skipToken);
currentData: imageDTO,
isLoading,
isError,
isSuccess,
} = useGetImageDTOQuery(imageName ?? skipToken);
const draggableData = useMemo<TypesafeDraggableData | undefined>(() => { const draggableData = useMemo<TypesafeDraggableData | undefined>(() => {
if (imageDTO) { if (imageDTO) {

View File

@ -1,73 +0,0 @@
import { Flex, Image, Spinner } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { systemSelector } from 'features/system/store/systemSelectors';
import { memo } from 'react';
import { gallerySelector } from '../store/gallerySelectors';
const selector = createSelector(
[systemSelector, gallerySelector],
(system, gallery) => {
const { shouldUseSingleGalleryColumn, galleryImageObjectFit } = gallery;
const { progressImage, shouldAntialiasProgressImage } = system;
return {
progressImage,
shouldUseSingleGalleryColumn,
galleryImageObjectFit,
shouldAntialiasProgressImage,
};
},
defaultSelectorOptions
);
const GalleryProgressImage = () => {
const {
progressImage,
shouldUseSingleGalleryColumn,
galleryImageObjectFit,
shouldAntialiasProgressImage,
} = useAppSelector(selector);
if (!progressImage) {
return null;
}
return (
<Flex
sx={{
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
aspectRatio: '1/1',
position: 'relative',
}}
>
<Image
draggable={false}
src={progressImage.dataURL}
width={progressImage.width}
height={progressImage.height}
sx={{
objectFit: shouldUseSingleGalleryColumn
? 'contain'
: galleryImageObjectFit,
width: '100%',
height: '100%',
maxWidth: '100%',
maxHeight: '100%',
borderRadius: 'base',
imageRendering: shouldAntialiasProgressImage ? 'auto' : 'pixelated',
}}
/>
<Spinner
sx={{ position: 'absolute', top: 1, right: 1, opacity: 0.7 }}
speed="1.2s"
/>
</Flex>
);
};
export default memo(GalleryProgressImage);

View File

@ -1,9 +1,7 @@
import { import {
Box, Box,
Button,
ButtonGroup, ButtonGroup,
Flex, Flex,
Spacer,
Tab, Tab,
TabList, TabList,
Tabs, Tabs,
@ -14,16 +12,16 @@ import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store'; import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIButton from 'common/components/IAIButton';
import { memo, useCallback, useRef } from 'react'; import { memo, useCallback, useRef } from 'react';
import { FaImages, FaServer } from 'react-icons/fa';
import { galleryViewChanged } from '../store/gallerySlice';
import BoardsList from './Boards/BoardsList/BoardsList'; import BoardsList from './Boards/BoardsList/BoardsList';
import GalleryBoardName from './GalleryBoardName'; import GalleryBoardName from './GalleryBoardName';
import GalleryPinButton from './GalleryPinButton'; import GalleryPinButton from './GalleryPinButton';
import GallerySettingsPopover from './GallerySettingsPopover'; import GallerySettingsPopover from './GallerySettingsPopover';
import BatchImageGrid from './ImageGrid/BatchImageGrid'; import BatchImageGrid from './ImageGrid/BatchImageGrid';
import GalleryImageGrid from './ImageGrid/GalleryImageGrid'; import GalleryImageGrid from './ImageGrid/GalleryImageGrid';
import IAIButton from 'common/components/IAIButton';
import { FaImages, FaServer } from 'react-icons/fa';
import { galleryViewChanged } from '../store/gallerySlice';
const selector = createSelector( const selector = createSelector(
[stateSelector], [stateSelector],

View File

@ -8,13 +8,8 @@ import IAIDndImage from 'common/components/IAIDndImage';
import IAIErrorLoadingImageFallback from 'common/components/IAIErrorLoadingImageFallback'; import IAIErrorLoadingImageFallback from 'common/components/IAIErrorLoadingImageFallback';
import IAIFillSkeleton from 'common/components/IAIFillSkeleton'; import IAIFillSkeleton from 'common/components/IAIFillSkeleton';
import ImageContextMenu from 'features/gallery/components/ImageContextMenu/ImageContextMenu'; import ImageContextMenu from 'features/gallery/components/ImageContextMenu/ImageContextMenu';
import { import { imagesRemovedFromBatch } from 'features/gallery/store/gallerySlice';
imageRangeEndSelected, import { memo, useCallback, useMemo } from 'react';
imageSelected,
imageSelectionToggled,
imagesRemovedFromBatch,
} from 'features/gallery/store/gallerySlice';
import { MouseEvent, memo, useCallback, useMemo } from 'react';
import { useGetImageDTOQuery } from 'services/api/endpoints/images'; import { useGetImageDTOQuery } from 'services/api/endpoints/images';
const makeSelector = (image_name: string) => const makeSelector = (image_name: string) =>
@ -39,7 +34,6 @@ const BatchImage = (props: BatchImageProps) => {
currentData: imageDTO, currentData: imageDTO,
isLoading, isLoading,
isError, isError,
isSuccess,
} = useGetImageDTOQuery(imageName); } = useGetImageDTOQuery(imageName);
const selector = useMemo(() => makeSelector(imageName), [imageName]); const selector = useMemo(() => makeSelector(imageName), [imageName]);
@ -49,18 +43,18 @@ const BatchImage = (props: BatchImageProps) => {
dispatch(imagesRemovedFromBatch([imageName])); dispatch(imagesRemovedFromBatch([imageName]));
}, [dispatch, imageName]); }, [dispatch, imageName]);
const handleClick = useCallback( // const handleClick = useCallback(
(e: MouseEvent<HTMLDivElement>) => { // (e: MouseEvent<HTMLDivElement>) => {
if (e.shiftKey) { // if (e.shiftKey) {
dispatch(imageRangeEndSelected(imageName)); // dispatch(imageRangeEndSelected(imageName));
} else if (e.ctrlKey || e.metaKey) { // } else if (e.ctrlKey || e.metaKey) {
dispatch(imageSelectionToggled(imageName)); // dispatch(imageSelectionToggled(imageName));
} else { // } else {
dispatch(imageSelected(imageName)); // dispatch(imageSelected(imageName));
} // }
}, // },
[dispatch, imageName] // [dispatch, imageName]
); // );
const draggableData = useMemo<TypesafeDraggableData | undefined>(() => { const draggableData = useMemo<TypesafeDraggableData | undefined>(() => {
if (selectionCount > 1) { if (selectionCount > 1) {
@ -105,7 +99,7 @@ const BatchImage = (props: BatchImageProps) => {
}} }}
> >
<IAIDndImage <IAIDndImage
onClick={handleClick} // onClick={handleClick}
imageDTO={imageDTO} imageDTO={imageDTO}
draggableData={draggableData} draggableData={draggableData}
isSelected={isSelected} isSelected={isSelected}

Some files were not shown because too many files have changed in this diff Show More