NEWONE1
/
invokeai
/frontend
/web
/src
/features
/changeBoardModal
/components
/ChangeBoardModal.tsx
import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library'; | |
import { Combobox, ConfirmationAlertDialog, Flex, FormControl, Text } from '@invoke-ai/ui-library'; | |
import { createSelector } from '@reduxjs/toolkit'; | |
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; | |
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; | |
import { useAssertSingleton } from 'common/hooks/useAssertSingleton'; | |
import { | |
changeBoardReset, | |
isModalOpenChanged, | |
selectChangeBoardModalSlice, | |
} from 'features/changeBoardModal/store/slice'; | |
import { memo, useCallback, useMemo, useState } from 'react'; | |
import { useTranslation } from 'react-i18next'; | |
import { useListAllBoardsQuery } from 'services/api/endpoints/boards'; | |
import { useAddImagesToBoardMutation, useRemoveImagesFromBoardMutation } from 'services/api/endpoints/images'; | |
const selectImagesToChange = createMemoizedSelector( | |
selectChangeBoardModalSlice, | |
(changeBoardModal) => changeBoardModal.imagesToChange | |
); | |
const selectIsModalOpen = createSelector( | |
selectChangeBoardModalSlice, | |
(changeBoardModal) => changeBoardModal.isModalOpen | |
); | |
const ChangeBoardModal = () => { | |
useAssertSingleton('ChangeBoardModal'); | |
const dispatch = useAppDispatch(); | |
const [selectedBoard, setSelectedBoard] = useState<string | null>(); | |
const { data: boards, isFetching } = useListAllBoardsQuery({ include_archived: true }); | |
const isModalOpen = useAppSelector(selectIsModalOpen); | |
const imagesToChange = useAppSelector(selectImagesToChange); | |
const [addImagesToBoard] = useAddImagesToBoardMutation(); | |
const [removeImagesFromBoard] = useRemoveImagesFromBoardMutation(); | |
const { t } = useTranslation(); | |
const options = useMemo<ComboboxOption[]>(() => { | |
return [{ label: t('boards.uncategorized'), value: 'none' }].concat( | |
(boards ?? []).map((board) => ({ | |
label: board.board_name, | |
value: board.board_id, | |
})) | |
); | |
}, [boards, t]); | |
const value = useMemo(() => options.find((o) => o.value === selectedBoard), [options, selectedBoard]); | |
const handleClose = useCallback(() => { | |
dispatch(changeBoardReset()); | |
dispatch(isModalOpenChanged(false)); | |
}, [dispatch]); | |
const handleChangeBoard = useCallback(() => { | |
if (!imagesToChange.length || !selectedBoard) { | |
return; | |
} | |
if (selectedBoard === 'none') { | |
removeImagesFromBoard({ imageDTOs: imagesToChange }); | |
} else { | |
addImagesToBoard({ | |
imageDTOs: imagesToChange, | |
board_id: selectedBoard, | |
}); | |
} | |
setSelectedBoard(null); | |
dispatch(changeBoardReset()); | |
}, [addImagesToBoard, dispatch, imagesToChange, removeImagesFromBoard, selectedBoard]); | |
const onChange = useCallback<ComboboxOnChange>((v) => { | |
if (!v) { | |
return; | |
} | |
setSelectedBoard(v.value); | |
}, []); | |
return ( | |
<ConfirmationAlertDialog | |
isOpen={isModalOpen} | |
onClose={handleClose} | |
title={t('boards.changeBoard')} | |
acceptCallback={handleChangeBoard} | |
acceptButtonText={t('boards.move')} | |
cancelButtonText={t('boards.cancel')} | |
useInert={false} | |
> | |
<Flex flexDir="column" gap={4}> | |
<Text> | |
{t('boards.movingImagesToBoard', { | |
count: imagesToChange.length, | |
})} | |
: | |
</Text> | |
<FormControl isDisabled={isFetching}> | |
<Combobox | |
placeholder={isFetching ? t('boards.loading') : t('boards.selectBoard')} | |
onChange={onChange} | |
value={value} | |
options={options} | |
/> | |
</FormControl> | |
</Flex> | |
</ConfirmationAlertDialog> | |
); | |
}; | |
export default memo(ChangeBoardModal); | |