fixed merge conflicts

This commit is contained in:
Jennifer Player
2023-09-20 10:00:11 -04:00
334 changed files with 13464 additions and 4184 deletions

View File

@@ -1,40 +1,38 @@
import { Progress } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { SystemState } from 'features/system/store/systemSlice';
import { isEqual } from 'lodash-es';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { systemSelector } from '../store/systemSelectors';
import { useGetQueueStatusQuery } from 'services/api/endpoints/queue';
const progressBarSelector = createSelector(
systemSelector,
(system: SystemState) => {
stateSelector,
({ system }) => {
return {
isProcessing: system.isProcessing,
currentStep: system.currentStep,
totalSteps: system.totalSteps,
currentStatusHasSteps: system.currentStatusHasSteps,
isConnected: system.isConnected,
hasSteps: Boolean(system.denoiseProgress),
value: (system.denoiseProgress?.percentage ?? 0) * 100,
};
},
{
memoizeOptions: { resultEqualityCheck: isEqual },
}
defaultSelectorOptions
);
const ProgressBar = () => {
const { t } = useTranslation();
const { isProcessing, currentStep, totalSteps, currentStatusHasSteps } =
useAppSelector(progressBarSelector);
const value = currentStep ? Math.round((currentStep * 100) / totalSteps) : 0;
const { data: queueStatus } = useGetQueueStatusQuery();
const { hasSteps, value, isConnected } = useAppSelector(progressBarSelector);
return (
<Progress
value={value}
aria-label={t('accessibility.invokeProgressBar')}
isIndeterminate={isProcessing && !currentStatusHasSteps}
height="full"
isIndeterminate={
isConnected && Boolean(queueStatus?.queue.in_progress) && !hasSteps
}
h="full"
w="full"
borderRadius={2}
colorScheme="accent"
/>
);

View File

@@ -49,13 +49,13 @@ import { useTranslation } from 'react-i18next';
import { LogLevelName } from 'roarr';
import { useGetAppConfigQuery } from 'services/api/endpoints/appInfo';
import { useFeatureStatus } from '../../hooks/useFeatureStatus';
import { LANGUAGES } from '../../store/constants';
import { languageSelector } from '../../store/systemSelectors';
import { languageChanged } from '../../store/systemSlice';
import SettingSwitch from './SettingSwitch';
import SettingsClearIntermediates from './SettingsClearIntermediates';
import SettingsSchedulers from './SettingsSchedulers';
import StyledFlex from './StyledFlex';
import { LANGUAGES } from 'features/system/store/types';
const selector = createSelector(
[stateSelector],

View File

@@ -1,5 +1,6 @@
import { Flex, Icon, Text } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { AnimatePresence, motion } from 'framer-motion';
@@ -8,27 +9,17 @@ import { memo, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { FaCircle } from 'react-icons/fa';
import { useHoverDirty } from 'react-use';
import { systemSelector } from '../store/systemSelectors';
import { useGetQueueStatusQuery } from 'services/api/endpoints/queue';
import { STATUS_TRANSLATION_KEYS } from '../store/types';
const statusIndicatorSelector = createSelector(
systemSelector,
(system) => {
const {
isConnected,
isProcessing,
statusTranslationKey,
currentIteration,
totalIterations,
currentStatusHasSteps,
} = system;
stateSelector,
({ system }) => {
const { isConnected, status } = system;
return {
isConnected,
isProcessing,
currentIteration,
totalIterations,
statusTranslationKey,
currentStatusHasSteps,
statusTranslationKey: STATUS_TRANSLATION_KEYS[status],
};
},
defaultSelectorOptions
@@ -47,35 +38,24 @@ const LIGHT_COLOR_MAP = {
};
const StatusIndicator = () => {
const {
isConnected,
isProcessing,
currentIteration,
totalIterations,
statusTranslationKey,
} = useAppSelector(statusIndicatorSelector);
const { isConnected, statusTranslationKey } = useAppSelector(
statusIndicatorSelector
);
const { t } = useTranslation();
const ref = useRef(null);
const { data: queueStatus } = useGetQueueStatusQuery();
const statusString = useMemo(() => {
if (isProcessing) {
const statusColor = useMemo(() => {
if (!isConnected) {
return 'error';
}
if (queueStatus?.queue.in_progress) {
return 'working';
}
if (isConnected) {
return 'ok';
}
return 'error';
}, [isProcessing, isConnected]);
const iterationsText = useMemo(() => {
if (!(currentIteration && totalIterations)) {
return;
}
return ` (${currentIteration}/${totalIterations})`;
}, [currentIteration, totalIterations]);
return 'ok';
}, [queueStatus?.queue.in_progress, isConnected]);
const isHovered = useHoverDirty(ref);
@@ -103,12 +83,11 @@ const StatusIndicator = () => {
fontWeight: '600',
pb: '1px',
userSelect: 'none',
color: LIGHT_COLOR_MAP[statusString],
_dark: { color: DARK_COLOR_MAP[statusString] },
color: LIGHT_COLOR_MAP[statusColor],
_dark: { color: DARK_COLOR_MAP[statusColor] },
}}
>
{t(statusTranslationKey as ResourceKey)}
{iterationsText}
</Text>
</motion.div>
)}
@@ -117,8 +96,8 @@ const StatusIndicator = () => {
as={FaCircle}
sx={{
boxSize: '0.5rem',
color: LIGHT_COLOR_MAP[statusString],
_dark: { color: DARK_COLOR_MAP[statusString] },
color: LIGHT_COLOR_MAP[statusColor],
_dark: { color: DARK_COLOR_MAP[statusColor] },
}}
/>
</Flex>

View File

@@ -24,8 +24,8 @@ export const initialConfigState: AppConfig = {
iterations: {
initial: 1,
min: 1,
sliderMax: 20,
inputMax: 9999,
sliderMax: 1000,
inputMax: 10000,
fineStep: 1,
coarseStep: 1,
},

View File

@@ -1,20 +0,0 @@
import i18n from 'i18n';
export const LANGUAGES = {
ar: i18n.t('common.langArabic', { lng: 'ar' }),
nl: i18n.t('common.langDutch', { lng: 'nl' }),
en: i18n.t('common.langEnglish', { lng: 'en' }),
fr: i18n.t('common.langFrench', { lng: 'fr' }),
de: i18n.t('common.langGerman', { lng: 'de' }),
he: i18n.t('common.langHebrew', { lng: 'he' }),
it: i18n.t('common.langItalian', { lng: 'it' }),
ja: i18n.t('common.langJapanese', { lng: 'ja' }),
ko: i18n.t('common.langKorean', { lng: 'ko' }),
pl: i18n.t('common.langPolish', { lng: 'pl' }),
pt_BR: i18n.t('common.langBrPortuguese', { lng: 'pt_BR' }),
pt: i18n.t('common.langPortuguese', { lng: 'pt' }),
ru: i18n.t('common.langRussian', { lng: 'ru' }),
zh_CN: i18n.t('common.langSimplifiedChinese', { lng: 'zh_CN' }),
es: i18n.t('common.langSpanish', { lng: 'es' }),
uk: i18n.t('common.langUkranian', { lng: 'ua' }),
};

View File

@@ -1,21 +1,7 @@
import { SystemState } from './systemSlice';
import { SystemState } from './types';
/**
* System slice persist denylist
*/
export const systemPersistDenylist: (keyof SystemState)[] = [
'currentIteration',
'currentStep',
'isCancelable',
'isConnected',
'isESRGANAvailable',
'isGFPGANAvailable',
'isProcessing',
'totalIterations',
'totalSteps',
'isCancelScheduled',
'progressImage',
'wereModelsReceived',
'isPersisted',
'isUploading',
'denoiseProgress',
'status',
];

View File

@@ -1,21 +1,11 @@
import { createSelector } from '@reduxjs/toolkit';
import { RootState } from 'app/store/store';
import { stateSelector } from 'app/store/store';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
export const systemSelector = (state: RootState) => state.system;
export const toastQueueSelector = (state: RootState) => state.system.toastQueue;
export const languageSelector = createSelector(
systemSelector,
(system) => system.language,
stateSelector,
({ system }) => system.language,
defaultSelectorOptions
);
export const isProcessingSelector = (state: RootState) =>
state.system.isProcessing;
export const selectIsBusy = createSelector(
(state: RootState) => state,
(state) => state.system.isProcessing || !state.system.isConnected
);

View File

@@ -1,14 +1,9 @@
import { UseToastOptions } from '@chakra-ui/react';
import { PayloadAction, createSlice, isAnyOf } from '@reduxjs/toolkit';
import { InvokeLogLevel } from 'app/logging/logger';
import { userInvoked } from 'app/store/actions';
import { t } from 'i18next';
import { get, startCase, truncate, upperFirst } from 'lodash-es';
import { LogLevelName } from 'roarr';
import {
isAnySessionRejected,
sessionCanceled,
} from 'services/api/thunks/session';
import { isAnySessionRejected } from 'services/api/thunks/session';
import {
appSocketConnected,
appSocketDisconnected,
@@ -18,124 +13,39 @@ import {
appSocketInvocationError,
appSocketInvocationRetrievalError,
appSocketInvocationStarted,
appSocketModelLoadCompleted,
appSocketModelLoadStarted,
appSocketQueueItemStatusChanged,
appSocketSessionRetrievalError,
appSocketSubscribed,
appSocketUnsubscribed,
} from 'services/events/actions';
import { ProgressImage } from 'services/events/types';
import { calculateStepPercentage } from '../util/calculateStepPercentage';
import { makeToast } from '../util/makeToast';
import { LANGUAGES } from './constants';
import { SystemState, LANGUAGES } from './types';
import { zPydanticValidationError } from './zodSchemas';
export type CancelStrategy = 'immediate' | 'scheduled';
export interface SystemState {
isGFPGANAvailable: boolean;
isESRGANAvailable: boolean;
isConnected: boolean;
isProcessing: boolean;
shouldConfirmOnDelete: boolean;
currentStep: number;
totalSteps: number;
currentIteration: number;
totalIterations: number;
currentStatusHasSteps: boolean;
isCancelable: boolean;
enableImageDebugging: boolean;
toastQueue: UseToastOptions[];
/**
* The current progress image
*/
progressImage: ProgressImage | null;
/**
* The current socket session id
*/
sessionId: string | null;
/**
* Cancel strategy
*/
cancelType: CancelStrategy;
/**
* Whether or not a scheduled cancelation is pending
*/
isCancelScheduled: boolean;
/**
* Array of node IDs that we want to handle when events received
*/
subscribedNodeIds: string[];
/**
* Whether or not the available models were received
*/
wereModelsReceived: boolean;
/**
* The console output logging level
*/
consoleLogLevel: InvokeLogLevel;
shouldLogToConsole: boolean;
// TODO: probably better to not store keys here, should just be a string that maps to the translation key
statusTranslationKey: string;
/**
* When a session is canceled, its ID is stored here until a new session is created.
*/
canceledSession: string;
isPersisted: boolean;
shouldAntialiasProgressImage: boolean;
language: keyof typeof LANGUAGES;
isUploading: boolean;
shouldUseNSFWChecker: boolean;
shouldUseWatermarker: boolean;
shouldDisableInformationalPopovers: boolean;
}
export const initialSystemState: SystemState = {
isConnected: false,
isProcessing: false,
isGFPGANAvailable: true,
isESRGANAvailable: true,
shouldConfirmOnDelete: true,
currentStep: 0,
totalSteps: 0,
currentIteration: 0,
totalIterations: 0,
currentStatusHasSteps: false,
isCancelable: true,
enableImageDebugging: false,
toastQueue: [],
progressImage: null,
denoiseProgress: null,
shouldAntialiasProgressImage: false,
sessionId: null,
cancelType: 'immediate',
isCancelScheduled: false,
subscribedNodeIds: [],
wereModelsReceived: false,
consoleLogLevel: 'debug',
shouldLogToConsole: true,
statusTranslationKey: 'common.statusDisconnected',
canceledSession: '',
isPersisted: false,
language: 'en',
isUploading: false,
shouldUseNSFWChecker: false,
shouldUseWatermarker: false,
shouldDisableInformationalPopovers: false,
status: 'DISCONNECTED',
};
export const systemSlice = createSlice({
name: 'system',
initialState: initialSystemState,
reducers: {
setIsProcessing: (state, action: PayloadAction<boolean>) => {
state.isProcessing = action.payload;
},
setCurrentStatus: (state, action: PayloadAction<string>) => {
state.statusTranslationKey = action.payload;
},
setShouldConfirmOnDelete: (state, action: PayloadAction<boolean>) => {
state.shouldConfirmOnDelete = action.payload;
},
setIsCancelable: (state, action: PayloadAction<boolean>) => {
state.isCancelable = action.payload;
},
setEnableImageDebugging: (state, action: PayloadAction<boolean>) => {
state.enableImageDebugging = action.payload;
},
@@ -145,30 +55,6 @@ export const systemSlice = createSlice({
clearToastQueue: (state) => {
state.toastQueue = [];
},
/**
* A cancel was scheduled
*/
cancelScheduled: (state) => {
state.isCancelScheduled = true;
},
/**
* The scheduled cancel was aborted
*/
scheduledCancelAborted: (state) => {
state.isCancelScheduled = false;
},
/**
* The cancel type was changed
*/
cancelTypeChanged: (state, action: PayloadAction<CancelStrategy>) => {
state.cancelType = action.payload;
},
/**
* The array of subscribed node ids was changed
*/
subscribedNodeIdsSet: (state, action: PayloadAction<string[]>) => {
state.subscribedNodeIds = action.payload;
},
consoleLogLevelChanged: (state, action: PayloadAction<LogLevelName>) => {
state.consoleLogLevel = action.payload;
},
@@ -181,15 +67,9 @@ export const systemSlice = createSlice({
) => {
state.shouldAntialiasProgressImage = action.payload;
},
isPersistedChanged: (state, action: PayloadAction<boolean>) => {
state.isPersisted = action.payload;
},
languageChanged: (state, action: PayloadAction<keyof typeof LANGUAGES>) => {
state.language = action.payload;
},
progressImageSet(state, action: PayloadAction<ProgressImage | null>) {
state.progressImage = action.payload;
},
shouldUseNSFWCheckerChanged(state, action: PayloadAction<boolean>) {
state.shouldUseNSFWChecker = action.payload;
},
@@ -204,34 +84,13 @@ export const systemSlice = createSlice({
},
},
extraReducers(builder) {
/**
* Socket Subscribed
*/
builder.addCase(appSocketSubscribed, (state, action) => {
state.sessionId = action.payload.sessionId;
state.canceledSession = '';
});
/**
* Socket Unsubscribed
*/
builder.addCase(appSocketUnsubscribed, (state) => {
state.sessionId = null;
});
/**
* Socket Connected
*/
builder.addCase(appSocketConnected, (state) => {
state.isConnected = true;
state.isCancelable = true;
state.isProcessing = false;
state.currentStatusHasSteps = false;
state.currentStep = 0;
state.totalSteps = 0;
state.currentIteration = 0;
state.totalIterations = 0;
state.statusTranslationKey = 'common.statusConnected';
state.denoiseProgress = null;
state.status = 'CONNECTED';
});
/**
@@ -239,106 +98,73 @@ export const systemSlice = createSlice({
*/
builder.addCase(appSocketDisconnected, (state) => {
state.isConnected = false;
state.isProcessing = false;
state.isCancelable = true;
state.currentStatusHasSteps = false;
state.currentStep = 0;
state.totalSteps = 0;
// state.currentIteration = 0;
// state.totalIterations = 0;
state.statusTranslationKey = 'common.statusDisconnected';
state.denoiseProgress = null;
state.status = 'DISCONNECTED';
});
/**
* Invocation Started
*/
builder.addCase(appSocketInvocationStarted, (state) => {
state.isCancelable = true;
state.isProcessing = true;
state.currentStatusHasSteps = false;
state.currentStep = 0;
state.totalSteps = 0;
// state.currentIteration = 0;
// state.totalIterations = 0;
state.statusTranslationKey = 'common.statusGenerating';
state.denoiseProgress = null;
state.status = 'PROCESSING';
});
/**
* Generator Progress
*/
builder.addCase(appSocketGeneratorProgress, (state, action) => {
const { step, total_steps, progress_image } = action.payload.data;
const {
step,
total_steps,
order,
progress_image,
graph_execution_state_id: session_id,
} = action.payload.data;
state.isProcessing = true;
state.isCancelable = true;
// state.currentIteration = 0;
// state.totalIterations = 0;
state.currentStatusHasSteps = true;
state.currentStep = step + 1; // TODO: step starts at -1, think this is a bug
state.totalSteps = total_steps;
state.progressImage = progress_image ?? null;
state.statusTranslationKey = 'common.statusGenerating';
state.denoiseProgress = {
step,
total_steps,
order,
percentage: calculateStepPercentage(step, total_steps, order),
progress_image,
session_id,
};
state.status = 'PROCESSING';
});
/**
* Invocation Complete
*/
builder.addCase(appSocketInvocationComplete, (state, action) => {
const { data } = action.payload;
// state.currentIteration = 0;
// state.totalIterations = 0;
state.currentStatusHasSteps = false;
state.currentStep = 0;
state.totalSteps = 0;
state.statusTranslationKey = 'common.statusProcessingComplete';
if (state.canceledSession === data.graph_execution_state_id) {
state.isProcessing = false;
state.isCancelable = true;
}
builder.addCase(appSocketInvocationComplete, (state) => {
state.denoiseProgress = null;
state.status = 'CONNECTED';
});
/**
* Graph Execution State Complete
*/
builder.addCase(appSocketGraphExecutionStateComplete, (state) => {
state.isProcessing = false;
state.isCancelable = false;
state.isCancelScheduled = false;
state.currentStep = 0;
state.totalSteps = 0;
state.statusTranslationKey = 'common.statusConnected';
state.progressImage = null;
state.denoiseProgress = null;
state.status = 'CONNECTED';
});
/**
* User Invoked
*/
builder.addCase(userInvoked, (state) => {
state.isProcessing = true;
state.isCancelable = true;
state.currentStatusHasSteps = false;
state.statusTranslationKey = 'common.statusPreparing';
builder.addCase(appSocketModelLoadStarted, (state) => {
state.status = 'LOADING_MODEL';
});
/**
* Session Canceled - FULFILLED
*/
builder.addCase(sessionCanceled.fulfilled, (state, action) => {
state.canceledSession = action.meta.arg.session_id;
state.isProcessing = false;
state.isCancelable = false;
state.isCancelScheduled = false;
state.currentStep = 0;
state.totalSteps = 0;
state.statusTranslationKey = 'common.statusConnected';
state.progressImage = null;
builder.addCase(appSocketModelLoadCompleted, (state) => {
state.status = 'CONNECTED';
});
state.toastQueue.push(
makeToast({ title: t('toast.canceled'), status: 'warning' })
);
builder.addCase(appSocketQueueItemStatusChanged, (state, action) => {
if (
['completed', 'canceled', 'failed'].includes(action.payload.data.status)
) {
state.status = 'CONNECTED';
state.denoiseProgress = null;
}
});
// *** Matchers - must be after all cases ***
@@ -348,14 +174,6 @@ export const systemSlice = createSlice({
* Session Created - REJECTED
*/
builder.addMatcher(isAnySessionRejected, (state, action) => {
state.isProcessing = false;
state.isCancelable = false;
state.isCancelScheduled = false;
state.currentStep = 0;
state.totalSteps = 0;
state.statusTranslationKey = 'common.statusConnected';
state.progressImage = null;
let errorDescription = undefined;
const duration = 5000;
@@ -399,16 +217,6 @@ export const systemSlice = createSlice({
* Any server error
*/
builder.addMatcher(isAnyServerError, (state, action) => {
state.isProcessing = false;
state.isCancelable = true;
// state.currentIteration = 0;
// state.totalIterations = 0;
state.currentStatusHasSteps = false;
state.currentStep = 0;
state.totalSteps = 0;
state.statusTranslationKey = 'common.statusError';
state.progressImage = null;
state.toastQueue.push(
makeToast({
title: t('toast.serverError'),
@@ -421,23 +229,14 @@ export const systemSlice = createSlice({
});
export const {
setIsProcessing,
setShouldConfirmOnDelete,
setCurrentStatus,
setIsCancelable,
setEnableImageDebugging,
addToast,
clearToastQueue,
cancelScheduled,
scheduledCancelAborted,
cancelTypeChanged,
subscribedNodeIdsSet,
consoleLogLevelChanged,
shouldLogToConsoleChanged,
isPersistedChanged,
shouldAntialiasProgressImageChanged,
languageChanged,
progressImageSet,
shouldUseNSFWCheckerChanged,
shouldUseWatermarkerChanged,
setShouldDisableInformationalPopovers,

View File

@@ -0,0 +1,63 @@
import { UseToastOptions } from '@chakra-ui/react';
import { InvokeLogLevel } from 'app/logging/logger';
import i18n from 'i18n';
import { ProgressImage } from 'services/events/types';
export type SystemStatus =
| 'CONNECTED'
| 'DISCONNECTED'
| 'PROCESSING'
| 'ERROR'
| 'LOADING_MODEL';
export type DenoiseProgress = {
session_id: string;
progress_image: ProgressImage | null | undefined;
step: number;
total_steps: number;
order: number;
percentage: number;
};
export interface SystemState {
isConnected: boolean;
shouldConfirmOnDelete: boolean;
enableImageDebugging: boolean;
toastQueue: UseToastOptions[];
denoiseProgress: DenoiseProgress | null;
consoleLogLevel: InvokeLogLevel;
shouldLogToConsole: boolean;
shouldAntialiasProgressImage: boolean;
language: keyof typeof LANGUAGES;
shouldUseNSFWChecker: boolean;
shouldUseWatermarker: boolean;
status: SystemStatus;
shouldDisableInformationalPopovers: boolean;
}
export const LANGUAGES = {
ar: i18n.t('common.langArabic', { lng: 'ar' }),
nl: i18n.t('common.langDutch', { lng: 'nl' }),
en: i18n.t('common.langEnglish', { lng: 'en' }),
fr: i18n.t('common.langFrench', { lng: 'fr' }),
de: i18n.t('common.langGerman', { lng: 'de' }),
he: i18n.t('common.langHebrew', { lng: 'he' }),
it: i18n.t('common.langItalian', { lng: 'it' }),
ja: i18n.t('common.langJapanese', { lng: 'ja' }),
ko: i18n.t('common.langKorean', { lng: 'ko' }),
pl: i18n.t('common.langPolish', { lng: 'pl' }),
pt_BR: i18n.t('common.langBrPortuguese', { lng: 'pt_BR' }),
pt: i18n.t('common.langPortuguese', { lng: 'pt' }),
ru: i18n.t('common.langRussian', { lng: 'ru' }),
zh_CN: i18n.t('common.langSimplifiedChinese', { lng: 'zh_CN' }),
es: i18n.t('common.langSpanish', { lng: 'es' }),
uk: i18n.t('common.langUkranian', { lng: 'ua' }),
};
export const STATUS_TRANSLATION_KEYS: Record<SystemStatus, string> = {
CONNECTED: 'common.statusConnected',
DISCONNECTED: 'common.statusDisconnected',
PROCESSING: 'common.statusProcessing',
ERROR: 'common.statusError',
LOADING_MODEL: 'common.statusLoadingModel',
};

View File

@@ -0,0 +1,17 @@
export const calculateStepPercentage = (
step: number,
total_steps: number,
order: number
) => {
if (total_steps === 0) {
return 0;
}
// we add one extra to step so that the progress bar will be full when denoise completes
if (order === 2) {
return Math.floor((step + 1 + 1) / 2) / Math.floor((total_steps + 1) / 2);
}
return (step + 1 + 1) / (total_steps + 1);
};