feat(ui): use singleton for clear q confirm dialog

This commit is contained in:
psychedelicious 2024-08-23 19:44:58 +10:00
parent d0464330f7
commit 3af577b210
9 changed files with 77 additions and 53 deletions

View File

@ -14,6 +14,7 @@ import ChangeBoardModal from 'features/changeBoardModal/components/ChangeBoardMo
import DeleteImageModal from 'features/deleteImageModal/components/DeleteImageModal'; import DeleteImageModal from 'features/deleteImageModal/components/DeleteImageModal';
import { DynamicPromptsModal } from 'features/dynamicPrompts/components/DynamicPromptsPreviewModal'; import { DynamicPromptsModal } from 'features/dynamicPrompts/components/DynamicPromptsPreviewModal';
import { useStarterModelsToast } from 'features/modelManagerV2/hooks/useStarterModelsToast'; import { useStarterModelsToast } from 'features/modelManagerV2/hooks/useStarterModelsToast';
import { ClearQueueConfirmationsAlertDialog } from 'features/queue/components/ClearQueueConfirmationAlertDialog';
import { StylePresetModal } from 'features/stylePresets/components/StylePresetForm/StylePresetModal'; import { StylePresetModal } from 'features/stylePresets/components/StylePresetForm/StylePresetModal';
import { configChanged } from 'features/system/store/configSlice'; import { configChanged } from 'features/system/store/configSlice';
import { languageSelector } from 'features/system/store/systemSelectors'; import { languageSelector } from 'features/system/store/systemSelectors';
@ -118,6 +119,7 @@ const App = ({ config = DEFAULT_CONFIG, selectedImage, selectedWorkflowId, desti
<ChangeBoardModal /> <ChangeBoardModal />
<DynamicPromptsModal /> <DynamicPromptsModal />
<StylePresetModal /> <StylePresetModal />
<ClearQueueConfirmationsAlertDialog />
<PreselectedImage selectedImage={selectedImage} /> <PreselectedImage selectedImage={selectedImage} />
</ErrorBoundary> </ErrorBoundary>
); );

View File

@ -1,3 +1,4 @@
import type { WritableAtom } from 'nanostores';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
export const useBoolean = (initialValue: boolean) => { export const useBoolean = (initialValue: boolean) => {
@ -19,3 +20,33 @@ export const useBoolean = (initialValue: boolean) => {
return api; return api;
}; };
export const buildUseBoolean = ($boolean: WritableAtom<boolean>) => {
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;
};
};

View File

@ -1,6 +1,6 @@
import type { ButtonProps } from '@invoke-ai/ui-library'; import type { ButtonProps } from '@invoke-ai/ui-library';
import { Button, useDisclosure } from '@invoke-ai/ui-library'; import { Button } from '@invoke-ai/ui-library';
import ClearQueueConfirmationAlertDialog from 'features/queue/components/ClearQueueConfirmationAlertDialog'; import { useClearQueueConfirmationAlertDialog } from 'features/queue/components/ClearQueueConfirmationAlertDialog';
import { useClearQueue } from 'features/queue/hooks/useClearQueue'; import { useClearQueue } from 'features/queue/hooks/useClearQueue';
import { memo } from 'react'; import { memo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -10,7 +10,7 @@ type Props = ButtonProps;
const ClearQueueButton = (props: Props) => { const ClearQueueButton = (props: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const disclosure = useDisclosure(); const dialogState = useClearQueueConfirmationAlertDialog();
const { isLoading, isDisabled } = useClearQueue(); const { isLoading, isDisabled } = useClearQueue();
return ( return (
@ -21,13 +21,12 @@ const ClearQueueButton = (props: Props) => {
tooltip={t('queue.clearTooltip')} tooltip={t('queue.clearTooltip')}
leftIcon={<PiTrashSimpleFill />} leftIcon={<PiTrashSimpleFill />}
colorScheme="error" colorScheme="error"
onClick={disclosure.onOpen} onClick={dialogState.setTrue}
data-testid={t('queue.clear')} data-testid={t('queue.clear')}
{...props} {...props}
> >
{t('queue.clear')} {t('queue.clear')}
</Button> </Button>
<ClearQueueConfirmationAlertDialog disclosure={disclosure} />
</> </>
); );
}; };

View File

@ -1,21 +1,24 @@
import type { UseDisclosureReturn } from '@invoke-ai/ui-library';
import { ConfirmationAlertDialog, Text } 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 { useClearQueue } from 'features/queue/hooks/useClearQueue';
import { atom } from 'nanostores';
import { memo } from 'react'; import { memo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
type Props = { const $boolean = atom(false);
disclosure: UseDisclosureReturn; export const useClearQueueConfirmationAlertDialog = buildUseBoolean($boolean);
};
const ClearQueueButton = ({ disclosure }: Props) => { export const ClearQueueConfirmationsAlertDialog = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const dialogState = useClearQueueConfirmationAlertDialog();
const isOpen = useStore(dialogState.$boolean);
const { clearQueue } = useClearQueue(); const { clearQueue } = useClearQueue();
return ( return (
<ConfirmationAlertDialog <ConfirmationAlertDialog
isOpen={disclosure.isOpen} isOpen={isOpen}
onClose={disclosure.onClose} onClose={dialogState.setFalse}
title={t('queue.clearTooltip')} title={t('queue.clearTooltip')}
acceptCallback={clearQueue} acceptCallback={clearQueue}
acceptButtonText={t('queue.clear')} acceptButtonText={t('queue.clear')}
@ -25,6 +28,6 @@ const ClearQueueButton = ({ disclosure }: Props) => {
<Text>{t('queue.clearQueueAlertDialog2')}</Text> <Text>{t('queue.clearQueueAlertDialog2')}</Text>
</ConfirmationAlertDialog> </ConfirmationAlertDialog>
); );
}; });
export default memo(ClearQueueButton); ClearQueueConfirmationsAlertDialog.displayName = 'ClearQueueConfirmationsAlertDialog';

View File

@ -1,19 +1,17 @@
import type { IconButtonProps } from '@invoke-ai/ui-library'; import type { IconButtonProps } from '@invoke-ai/ui-library';
import { IconButton, useDisclosure, useShiftModifier } from '@invoke-ai/ui-library'; import { IconButton, useShiftModifier } from '@invoke-ai/ui-library';
import ClearQueueConfirmationAlertDialog from 'features/queue/components/ClearQueueConfirmationAlertDialog'; import { useClearQueueConfirmationAlertDialog } from 'features/queue/components/ClearQueueConfirmationAlertDialog';
import { useCancelCurrentQueueItem } from 'features/queue/hooks/useCancelCurrentQueueItem'; import { useCancelCurrentQueueItem } from 'features/queue/hooks/useCancelCurrentQueueItem';
import { useClearQueue } from 'features/queue/hooks/useClearQueue'; import { useClearQueue } from 'features/queue/hooks/useClearQueue';
import { memo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiTrashSimpleBold, PiXBold } from 'react-icons/pi'; import { PiTrashSimpleBold, PiXBold } from 'react-icons/pi';
type ClearQueueButtonProps = Omit<IconButtonProps, 'aria-label'>; type ClearQueueButtonProps = Omit<IconButtonProps, 'aria-label'>;
type ClearQueueIconButtonProps = ClearQueueButtonProps & { export const ClearAllQueueIconButton = memo((props: ClearQueueButtonProps) => {
onOpen: () => void;
};
export const ClearAllQueueIconButton = ({ onOpen, ...props }: ClearQueueIconButtonProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const dialogState = useClearQueueConfirmationAlertDialog();
const { isLoading, isDisabled } = useClearQueue(); const { isLoading, isDisabled } = useClearQueue();
return ( return (
@ -24,14 +22,16 @@ export const ClearAllQueueIconButton = ({ onOpen, ...props }: ClearQueueIconButt
tooltip={t('queue.clearTooltip')} tooltip={t('queue.clearTooltip')}
icon={<PiTrashSimpleBold size="16px" />} icon={<PiTrashSimpleBold size="16px" />}
colorScheme="error" colorScheme="error"
onClick={onOpen} onClick={dialogState.setTrue}
data-testid={t('queue.clear')} data-testid={t('queue.clear')}
{...props} {...props}
/> />
); );
}; });
const ClearSingleQueueItemIconButton = (props: ClearQueueButtonProps) => { ClearAllQueueIconButton.displayName = 'ClearAllQueueIconButton';
const ClearSingleQueueItemIconButton = memo((props: ClearQueueButtonProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { cancelQueueItem, isLoading, isDisabled } = useCancelCurrentQueueItem(); const { cancelQueueItem, isLoading, isDisabled } = useCancelCurrentQueueItem();
@ -48,22 +48,20 @@ const ClearSingleQueueItemIconButton = (props: ClearQueueButtonProps) => {
{...props} {...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 // Show the single item clear button when shift is pressed
// Otherwise show the clear queue button // Otherwise show the clear queue button
const shift = useShiftModifier(); const shift = useShiftModifier();
const disclosure = useDisclosure();
return ( if (shift) {
<> return <ClearAllQueueIconButton {...props} />;
{shift ? ( }
<ClearAllQueueIconButton {...props} onOpen={disclosure.onOpen} />
) : ( return <ClearSingleQueueItemIconButton {...props} />;
<ClearSingleQueueItemIconButton {...props} /> });
)}
<ClearQueueConfirmationAlertDialog disclosure={disclosure} /> ClearQueueIconButton.displayName = 'ClearQueueIconButton';
</>
);
};

View File

@ -10,7 +10,7 @@ import {
useDisclosure, useDisclosure,
} from '@invoke-ai/ui-library'; } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks'; 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 { useClearQueue } from 'features/queue/hooks/useClearQueue';
import { usePauseProcessor } from 'features/queue/hooks/usePauseProcessor'; import { usePauseProcessor } from 'features/queue/hooks/usePauseProcessor';
import { useResumeProcessor } from 'features/queue/hooks/useResumeProcessor'; import { useResumeProcessor } from 'features/queue/hooks/useResumeProcessor';
@ -26,7 +26,7 @@ export const QueueActionsMenuButton = memo(() => {
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const clearQueueDisclosure = useDisclosure(); const dialogState = useClearQueueConfirmationAlertDialog();
const isPauseEnabled = useFeatureStatus('pauseQueue'); const isPauseEnabled = useFeatureStatus('pauseQueue');
const isResumeEnabled = useFeatureStatus('resumeQueue'); const isResumeEnabled = useFeatureStatus('resumeQueue');
const { queueSize } = useGetQueueStatusQuery(undefined, { const { queueSize } = useGetQueueStatusQuery(undefined, {
@ -51,15 +51,13 @@ export const QueueActionsMenuButton = memo(() => {
return ( return (
<Box pos="relative"> <Box pos="relative">
<ClearQueueConfirmationAlertDialog disclosure={clearQueueDisclosure} />
<Menu isOpen={isOpen} onOpen={onOpen} onClose={onClose} placement="bottom-end"> <Menu isOpen={isOpen} onOpen={onOpen} onClose={onClose} placement="bottom-end">
<MenuButton as={IconButton} aria-label="Queue Actions Menu" icon={<RiListCheck />} /> <MenuButton as={IconButton} aria-label="Queue Actions Menu" icon={<RiListCheck />} />
<MenuList> <MenuList>
<MenuItem <MenuItem
isDestructive isDestructive
icon={<PiTrashSimpleBold size="16px" />} icon={<PiTrashSimpleBold size="16px" />}
onClick={clearQueueDisclosure.onOpen} onClick={dialogState.setTrue}
isLoading={isLoadingClearQueue} isLoading={isLoadingClearQueue}
isDisabled={isDisabledClearQueue} isDisabled={isDisabledClearQueue}
> >

View File

@ -17,9 +17,6 @@ const QueueControls = () => {
<InvokeQueueBackButton /> <InvokeQueueBackButton />
<Spacer /> <Spacer />
<QueueActionsMenuButton /> <QueueActionsMenuButton />
{/* <CancelCurrentQueueItemButton asIconButton />
{isResumeEnabled && <ResumeProcessorButton asIconButton />}
{isPauseEnabled && <PauseProcessorButton asIconButton />} */}
<ClearQueueIconButton /> <ClearQueueIconButton />
</ButtonGroup> </ButtonGroup>
<ProgressBar /> <ProgressBar />

View File

@ -1,7 +1,6 @@
import type { SystemStyleObject } from '@invoke-ai/ui-library'; 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 CancelCurrentQueueItemIconButton from 'features/queue/components/CancelCurrentQueueItemIconButton';
import ClearQueueConfirmationAlertDialog from 'features/queue/components/ClearQueueConfirmationAlertDialog';
import { ClearAllQueueIconButton } from 'features/queue/components/ClearQueueIconButton'; import { ClearAllQueueIconButton } from 'features/queue/components/ClearQueueIconButton';
import { QueueButtonTooltip } from 'features/queue/components/QueueButtonTooltip'; import { QueueButtonTooltip } from 'features/queue/components/QueueButtonTooltip';
import { useQueueBack } from 'features/queue/hooks/useQueueBack'; import { useQueueBack } from 'features/queue/hooks/useQueueBack';
@ -36,8 +35,6 @@ const FloatingSidePanelButtons = (props: Props) => {
[isDisabled, queueStatus?.processor.is_processing] [isDisabled, queueStatus?.processor.is_processing]
); );
const disclosure = useDisclosure();
if (!props.panelApi.isCollapsed) { if (!props.panelApi.isCollapsed) {
return null; return null;
} }
@ -76,8 +73,7 @@ const FloatingSidePanelButtons = (props: Props) => {
</QueueButtonTooltip> </QueueButtonTooltip>
<CancelCurrentQueueItemIconButton sx={floatingButtonStyles} /> <CancelCurrentQueueItemIconButton sx={floatingButtonStyles} />
</ButtonGroup> </ButtonGroup>
<ClearAllQueueIconButton sx={floatingButtonStyles} onOpen={disclosure.onOpen} /> <ClearAllQueueIconButton sx={floatingButtonStyles} />
<ClearQueueConfirmationAlertDialog disclosure={disclosure} />
</Flex> </Flex>
</Portal> </Portal>
); );

View File

@ -25,7 +25,7 @@ const WorkflowLibraryMenu = () => {
const shift = useShiftModifier(); const shift = useShiftModifier();
useGlobalMenuClose(onClose); useGlobalMenuClose(onClose);
return ( return (
<Menu isOpen={isOpen} onOpen={onOpen} onClose={onClose}> <Menu isOpen={isOpen} onOpen={onOpen} onClose={onClose} isLazy>
<MenuButton <MenuButton
as={IconButton} as={IconButton}
aria-label={t('workflows.workflowEditorMenu')} aria-label={t('workflows.workflowEditorMenu')}