diff --git a/invokeai/frontend/web/src/app/components/App.tsx b/invokeai/frontend/web/src/app/components/App.tsx index 2208aa5d58..e333d13332 100644 --- a/invokeai/frontend/web/src/app/components/App.tsx +++ b/invokeai/frontend/web/src/app/components/App.tsx @@ -14,6 +14,7 @@ import ChangeBoardModal from 'features/changeBoardModal/components/ChangeBoardMo import DeleteImageModal from 'features/deleteImageModal/components/DeleteImageModal'; import { DynamicPromptsModal } from 'features/dynamicPrompts/components/DynamicPromptsPreviewModal'; import { useStarterModelsToast } from 'features/modelManagerV2/hooks/useStarterModelsToast'; +import { ClearQueueConfirmationsAlertDialog } from 'features/queue/components/ClearQueueConfirmationAlertDialog'; import { StylePresetModal } from 'features/stylePresets/components/StylePresetForm/StylePresetModal'; import { configChanged } from 'features/system/store/configSlice'; import { languageSelector } from 'features/system/store/systemSelectors'; @@ -118,6 +119,7 @@ const App = ({ config = DEFAULT_CONFIG, selectedImage, selectedWorkflowId, desti + ); diff --git a/invokeai/frontend/web/src/common/hooks/useBoolean.ts b/invokeai/frontend/web/src/common/hooks/useBoolean.ts index 123e48cd75..46e96d424c 100644 --- a/invokeai/frontend/web/src/common/hooks/useBoolean.ts +++ b/invokeai/frontend/web/src/common/hooks/useBoolean.ts @@ -1,3 +1,4 @@ +import type { WritableAtom } from 'nanostores'; import { useCallback, useMemo, useState } from 'react'; export const useBoolean = (initialValue: boolean) => { @@ -19,3 +20,33 @@ export const useBoolean = (initialValue: boolean) => { return api; }; + +export const buildUseBoolean = ($boolean: WritableAtom) => { + return () => { + const setTrue = useCallback(() => { + $boolean.set(true); + }, []); + const setFalse = useCallback(() => { + $boolean.set(false); + }, []); + const set = useCallback((value: boolean) => { + $boolean.set(value); + }, []); + const toggle = useCallback(() => { + $boolean.set(!$boolean.get()); + }, []); + + const api = useMemo( + () => ({ + setTrue, + setFalse, + set, + toggle, + $boolean, + }), + [set, setFalse, setTrue, toggle] + ); + + return api; + }; +}; diff --git a/invokeai/frontend/web/src/features/queue/components/ClearQueueButton.tsx b/invokeai/frontend/web/src/features/queue/components/ClearQueueButton.tsx index c899dd0482..5b2806c93a 100644 --- a/invokeai/frontend/web/src/features/queue/components/ClearQueueButton.tsx +++ b/invokeai/frontend/web/src/features/queue/components/ClearQueueButton.tsx @@ -1,6 +1,6 @@ import type { ButtonProps } from '@invoke-ai/ui-library'; -import { Button, useDisclosure } from '@invoke-ai/ui-library'; -import ClearQueueConfirmationAlertDialog from 'features/queue/components/ClearQueueConfirmationAlertDialog'; +import { Button } from '@invoke-ai/ui-library'; +import { useClearQueueConfirmationAlertDialog } from 'features/queue/components/ClearQueueConfirmationAlertDialog'; import { useClearQueue } from 'features/queue/hooks/useClearQueue'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -10,7 +10,7 @@ type Props = ButtonProps; const ClearQueueButton = (props: Props) => { const { t } = useTranslation(); - const disclosure = useDisclosure(); + const dialogState = useClearQueueConfirmationAlertDialog(); const { isLoading, isDisabled } = useClearQueue(); return ( @@ -21,13 +21,12 @@ const ClearQueueButton = (props: Props) => { tooltip={t('queue.clearTooltip')} leftIcon={} colorScheme="error" - onClick={disclosure.onOpen} + onClick={dialogState.setTrue} data-testid={t('queue.clear')} {...props} > {t('queue.clear')} - ); }; diff --git a/invokeai/frontend/web/src/features/queue/components/ClearQueueConfirmationAlertDialog.tsx b/invokeai/frontend/web/src/features/queue/components/ClearQueueConfirmationAlertDialog.tsx index 291419d024..b90d66073e 100644 --- a/invokeai/frontend/web/src/features/queue/components/ClearQueueConfirmationAlertDialog.tsx +++ b/invokeai/frontend/web/src/features/queue/components/ClearQueueConfirmationAlertDialog.tsx @@ -1,21 +1,24 @@ -import type { UseDisclosureReturn } from '@invoke-ai/ui-library'; import { ConfirmationAlertDialog, Text } from '@invoke-ai/ui-library'; +import { useStore } from '@nanostores/react'; +import { buildUseBoolean } from 'common/hooks/useBoolean'; import { useClearQueue } from 'features/queue/hooks/useClearQueue'; +import { atom } from 'nanostores'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -type Props = { - disclosure: UseDisclosureReturn; -}; +const $boolean = atom(false); +export const useClearQueueConfirmationAlertDialog = buildUseBoolean($boolean); -const ClearQueueButton = ({ disclosure }: Props) => { +export const ClearQueueConfirmationsAlertDialog = memo(() => { const { t } = useTranslation(); + const dialogState = useClearQueueConfirmationAlertDialog(); + const isOpen = useStore(dialogState.$boolean); const { clearQueue } = useClearQueue(); return ( { {t('queue.clearQueueAlertDialog2')} ); -}; +}); -export default memo(ClearQueueButton); +ClearQueueConfirmationsAlertDialog.displayName = 'ClearQueueConfirmationsAlertDialog'; diff --git a/invokeai/frontend/web/src/features/queue/components/ClearQueueIconButton.tsx b/invokeai/frontend/web/src/features/queue/components/ClearQueueIconButton.tsx index 41843ad66c..39a84c0216 100644 --- a/invokeai/frontend/web/src/features/queue/components/ClearQueueIconButton.tsx +++ b/invokeai/frontend/web/src/features/queue/components/ClearQueueIconButton.tsx @@ -1,19 +1,17 @@ import type { IconButtonProps } from '@invoke-ai/ui-library'; -import { IconButton, useDisclosure, useShiftModifier } from '@invoke-ai/ui-library'; -import ClearQueueConfirmationAlertDialog from 'features/queue/components/ClearQueueConfirmationAlertDialog'; +import { IconButton, useShiftModifier } from '@invoke-ai/ui-library'; +import { useClearQueueConfirmationAlertDialog } from 'features/queue/components/ClearQueueConfirmationAlertDialog'; import { useCancelCurrentQueueItem } from 'features/queue/hooks/useCancelCurrentQueueItem'; import { useClearQueue } from 'features/queue/hooks/useClearQueue'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { PiTrashSimpleBold, PiXBold } from 'react-icons/pi'; type ClearQueueButtonProps = Omit; -type ClearQueueIconButtonProps = ClearQueueButtonProps & { - onOpen: () => void; -}; - -export const ClearAllQueueIconButton = ({ onOpen, ...props }: ClearQueueIconButtonProps) => { +export const ClearAllQueueIconButton = memo((props: ClearQueueButtonProps) => { const { t } = useTranslation(); + const dialogState = useClearQueueConfirmationAlertDialog(); const { isLoading, isDisabled } = useClearQueue(); return ( @@ -24,14 +22,16 @@ export const ClearAllQueueIconButton = ({ onOpen, ...props }: ClearQueueIconButt tooltip={t('queue.clearTooltip')} icon={} colorScheme="error" - onClick={onOpen} + onClick={dialogState.setTrue} data-testid={t('queue.clear')} {...props} /> ); -}; +}); -const ClearSingleQueueItemIconButton = (props: ClearQueueButtonProps) => { +ClearAllQueueIconButton.displayName = 'ClearAllQueueIconButton'; + +const ClearSingleQueueItemIconButton = memo((props: ClearQueueButtonProps) => { const { t } = useTranslation(); const { cancelQueueItem, isLoading, isDisabled } = useCancelCurrentQueueItem(); @@ -48,22 +48,20 @@ const ClearSingleQueueItemIconButton = (props: ClearQueueButtonProps) => { {...props} /> ); -}; +}); -export const ClearQueueIconButton = (props: ClearQueueButtonProps) => { +ClearSingleQueueItemIconButton.displayName = 'ClearSingleQueueItemIconButton'; + +export const ClearQueueIconButton = memo((props: ClearQueueButtonProps) => { // Show the single item clear button when shift is pressed // Otherwise show the clear queue button const shift = useShiftModifier(); - const disclosure = useDisclosure(); - return ( - <> - {shift ? ( - - ) : ( - - )} - - - ); -}; + if (shift) { + return ; + } + + return ; +}); + +ClearQueueIconButton.displayName = 'ClearQueueIconButton'; diff --git a/invokeai/frontend/web/src/features/queue/components/QueueActionsMenuButton.tsx b/invokeai/frontend/web/src/features/queue/components/QueueActionsMenuButton.tsx index 101c82376c..99456b042f 100644 --- a/invokeai/frontend/web/src/features/queue/components/QueueActionsMenuButton.tsx +++ b/invokeai/frontend/web/src/features/queue/components/QueueActionsMenuButton.tsx @@ -10,7 +10,7 @@ import { useDisclosure, } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; -import ClearQueueConfirmationAlertDialog from 'features/queue/components/ClearQueueConfirmationAlertDialog'; +import { useClearQueueConfirmationAlertDialog } from 'features/queue/components/ClearQueueConfirmationAlertDialog'; import { useClearQueue } from 'features/queue/hooks/useClearQueue'; import { usePauseProcessor } from 'features/queue/hooks/usePauseProcessor'; import { useResumeProcessor } from 'features/queue/hooks/useResumeProcessor'; @@ -26,7 +26,7 @@ export const QueueActionsMenuButton = memo(() => { const { isOpen, onOpen, onClose } = useDisclosure(); const dispatch = useAppDispatch(); const { t } = useTranslation(); - const clearQueueDisclosure = useDisclosure(); + const dialogState = useClearQueueConfirmationAlertDialog(); const isPauseEnabled = useFeatureStatus('pauseQueue'); const isResumeEnabled = useFeatureStatus('resumeQueue'); const { queueSize } = useGetQueueStatusQuery(undefined, { @@ -51,15 +51,13 @@ export const QueueActionsMenuButton = memo(() => { return ( - - } /> } - onClick={clearQueueDisclosure.onOpen} + onClick={dialogState.setTrue} isLoading={isLoadingClearQueue} isDisabled={isDisabledClearQueue} > diff --git a/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx b/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx index 28a12808ea..570708642d 100644 --- a/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx +++ b/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx @@ -17,9 +17,6 @@ const QueueControls = () => { - {/* - {isResumeEnabled && } - {isPauseEnabled && } */} diff --git a/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx b/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx index 5a8273b7fc..58e1461ec7 100644 --- a/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx +++ b/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx @@ -1,7 +1,6 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; -import { ButtonGroup, Flex, Icon, IconButton, Portal, spinAnimation, useDisclosure } from '@invoke-ai/ui-library'; +import { ButtonGroup, Flex, Icon, IconButton, Portal, spinAnimation } from '@invoke-ai/ui-library'; import CancelCurrentQueueItemIconButton from 'features/queue/components/CancelCurrentQueueItemIconButton'; -import ClearQueueConfirmationAlertDialog from 'features/queue/components/ClearQueueConfirmationAlertDialog'; import { ClearAllQueueIconButton } from 'features/queue/components/ClearQueueIconButton'; import { QueueButtonTooltip } from 'features/queue/components/QueueButtonTooltip'; import { useQueueBack } from 'features/queue/hooks/useQueueBack'; @@ -36,8 +35,6 @@ const FloatingSidePanelButtons = (props: Props) => { [isDisabled, queueStatus?.processor.is_processing] ); - const disclosure = useDisclosure(); - if (!props.panelApi.isCollapsed) { return null; } @@ -76,8 +73,7 @@ const FloatingSidePanelButtons = (props: Props) => { - - + ); diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/WorkflowLibraryMenu.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/WorkflowLibraryMenu.tsx index 73e9f5d4ba..38f44314d6 100644 --- a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/WorkflowLibraryMenu.tsx +++ b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/WorkflowLibraryMenu.tsx @@ -25,7 +25,7 @@ const WorkflowLibraryMenu = () => { const shift = useShiftModifier(); useGlobalMenuClose(onClose); return ( - +