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); | |