fix(ui): more fixing of auto-add

This commit is contained in:
psychedelicious 2023-07-21 15:00:07 +10:00
parent e10e22440d
commit 3a610e1a65
11 changed files with 197 additions and 171 deletions

View File

@ -84,6 +84,13 @@ export const addInvocationCompleteEventListener = () => {
); );
} }
dispatch(
imagesApi.util.invalidateTags([
{ type: 'BoardImagesTotal', id: autoAddBoardId ?? 'none' },
{ type: 'BoardAssetsTotal', id: autoAddBoardId ?? 'none' },
])
);
const { selectedBoardId, shouldAutoSwitch } = gallery; const { selectedBoardId, shouldAutoSwitch } = gallery;
// If auto-switch is enabled, select the new image // If auto-switch is enabled, select the new image

View File

@ -0,0 +1,19 @@
import { Flex, Icon } from '@chakra-ui/react';
import { FaPlus } from 'react-icons/fa';
const AutoAddIcon = () => {
return (
<Flex
sx={{
position: 'absolute',
insetInlineStart: 0,
top: 0,
p: 1,
}}
>
<Icon as={FaPlus} sx={{ fill: 'accent.500' }} />
</Flex>
);
};
export default AutoAddIcon;

View File

@ -52,7 +52,7 @@ const BoardAutoAddSelect = () => {
return; return;
} }
dispatch(autoAddBoardIdChanged(v === 'none' ? null : v)); dispatch(autoAddBoardIdChanged(v === 'none' ? undefined : v));
}, },
[dispatch] [dispatch]
); );

View File

@ -1,9 +1,15 @@
import { Box, MenuItem, MenuList } from '@chakra-ui/react'; import { MenuItem, MenuList } from '@chakra-ui/react';
import { useAppDispatch } from 'app/store/storeHooks'; import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { ContextMenu, ContextMenuProps } from 'chakra-ui-contextmenu'; import { ContextMenu, ContextMenuProps } from 'chakra-ui-contextmenu';
import { boardIdSelected } from 'features/gallery/store/gallerySlice'; import {
import { memo, useCallback } from 'react'; autoAddBoardIdChanged,
import { FaFolder } from 'react-icons/fa'; boardIdSelected,
} from 'features/gallery/store/gallerySlice';
import { memo, useCallback, useMemo } from 'react';
import { FaFolder, FaMinus, FaPlus } from 'react-icons/fa';
import { BoardDTO } from 'services/api/types'; import { BoardDTO } from 'services/api/types';
import { menuListMotionProps } from 'theme/components/menu'; import { menuListMotionProps } from 'theme/components/menu';
import GalleryBoardContextMenuItems from './GalleryBoardContextMenuItems'; import GalleryBoardContextMenuItems from './GalleryBoardContextMenuItems';
@ -11,7 +17,7 @@ import SystemBoardContextMenuItems from './SystemBoardContextMenuItems';
type Props = { type Props = {
board?: BoardDTO; board?: BoardDTO;
board_id: string; board_id?: string;
children: ContextMenuProps<HTMLDivElement>['children']; children: ContextMenuProps<HTMLDivElement>['children'];
setBoardToDelete?: (board?: BoardDTO) => void; setBoardToDelete?: (board?: BoardDTO) => void;
}; };
@ -19,9 +25,30 @@ type Props = {
const BoardContextMenu = memo( const BoardContextMenu = memo(
({ board, board_id, setBoardToDelete, children }: Props) => { ({ board, board_id, setBoardToDelete, children }: Props) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const selector = useMemo(
() =>
createSelector(
stateSelector,
({ gallery }) => {
const isSelectedForAutoAdd = board_id === gallery.autoAddBoardId;
return { isSelectedForAutoAdd };
},
defaultSelectorOptions
),
[board_id]
);
const { isSelectedForAutoAdd } = useAppSelector(selector);
const handleSelectBoard = useCallback(() => { const handleSelectBoard = useCallback(() => {
dispatch(boardIdSelected(board?.board_id ?? board_id)); dispatch(boardIdSelected(board?.board_id ?? board_id));
}, [board?.board_id, board_id, dispatch]); }, [board?.board_id, board_id, dispatch]);
const handleAutoAdd = useCallback(() => {
dispatch(autoAddBoardIdChanged(board_id));
}, [board_id, dispatch]);
return ( return (
<ContextMenu<HTMLDivElement> <ContextMenu<HTMLDivElement>
menuProps={{ size: 'sm', isLazy: true }} menuProps={{ size: 'sm', isLazy: true }}
@ -37,6 +64,14 @@ const BoardContextMenu = memo(
<MenuItem icon={<FaFolder />} onClickCapture={handleSelectBoard}> <MenuItem icon={<FaFolder />} onClickCapture={handleSelectBoard}>
Select Board Select Board
</MenuItem> </MenuItem>
<MenuItem
icon={isSelectedForAutoAdd ? <FaMinus /> : <FaPlus />}
onClickCapture={handleAutoAdd}
>
{isSelectedForAutoAdd
? 'Disable Auto-Add'
: 'Auto-Add to this Board'}
</MenuItem>
{!board && <SystemBoardContextMenuItems board_id={board_id} />} {!board && <SystemBoardContextMenuItems board_id={board_id} />}
{board && ( {board && (
<GalleryBoardContextMenuItems <GalleryBoardContextMenuItems

View File

@ -24,13 +24,9 @@ import { useUpdateBoardMutation } from 'services/api/endpoints/boards';
import { useGetImageDTOQuery } from 'services/api/endpoints/images'; import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import { useBoardTotal } from 'services/api/hooks/useBoardTotal'; import { useBoardTotal } from 'services/api/hooks/useBoardTotal';
import { BoardDTO } from 'services/api/types'; import { BoardDTO } from 'services/api/types';
import AutoAddIcon from '../AutoAddIcon';
import BoardContextMenu from '../BoardContextMenu'; import BoardContextMenu from '../BoardContextMenu';
const AUTO_ADD_BADGE_STYLES: ChakraProps['sx'] = {
bg: 'accent.200',
color: 'blackAlpha.900',
};
const BASE_BADGE_STYLES: ChakraProps['sx'] = { const BASE_BADGE_STYLES: ChakraProps['sx'] = {
bg: 'base.500', bg: 'base.500',
color: 'whiteAlpha.900', color: 'whiteAlpha.900',
@ -200,6 +196,7 @@ const GalleryBoard = memo(
{totalImages}/{totalAssets} {totalImages}/{totalAssets}
</Badge> </Badge>
</Flex> </Flex>
{isSelectedForAutoAdd && <AutoAddIcon />}
<Box <Box
className="selection-box" className="selection-box"
sx={{ sx={{

View File

@ -1,11 +1,16 @@
import { Badge, Box, ChakraProps, Flex, Icon, Text } from '@chakra-ui/react'; import { Badge, Box, ChakraProps, Flex, Icon, Text } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { MoveBoardDropData } from 'app/components/ImageDnd/typesafeDnd'; import { MoveBoardDropData } from 'app/components/ImageDnd/typesafeDnd';
import { useAppDispatch } from 'app/store/storeHooks'; import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIDroppable from 'common/components/IAIDroppable'; import IAIDroppable from 'common/components/IAIDroppable';
import { boardIdSelected } from 'features/gallery/store/gallerySlice'; import { boardIdSelected } from 'features/gallery/store/gallerySlice';
import { memo, useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import { FaFolder } from 'react-icons/fa'; import { FaFolder, FaPlus } from 'react-icons/fa';
import { useBoardTotal } from 'services/api/hooks/useBoardTotal'; import { useBoardTotal } from 'services/api/hooks/useBoardTotal';
import AutoAddIcon from '../AutoAddIcon';
import BoardContextMenu from '../BoardContextMenu';
const BASE_BADGE_STYLES: ChakraProps['sx'] = { const BASE_BADGE_STYLES: ChakraProps['sx'] = {
bg: 'base.500', bg: 'base.500',
@ -15,9 +20,19 @@ interface Props {
isSelected: boolean; isSelected: boolean;
} }
const selector = createSelector(
stateSelector,
({ gallery }) => {
const { autoAddBoardId } = gallery;
return { autoAddBoardId };
},
defaultSelectorOptions
);
const NoBoardBoard = memo(({ isSelected }: Props) => { const NoBoardBoard = memo(({ isSelected }: Props) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { totalImages, totalAssets } = useBoardTotal(undefined); const { totalImages, totalAssets } = useBoardTotal(undefined);
const { autoAddBoardId } = useAppSelector(selector);
const handleSelectBoard = useCallback(() => { const handleSelectBoard = useCallback(() => {
dispatch(boardIdSelected(undefined)); dispatch(boardIdSelected(undefined));
}, [dispatch]); }, [dispatch]);
@ -34,85 +49,92 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
return ( return (
<Box sx={{ w: 'full', h: 'full', touchAction: 'none', userSelect: 'none' }}> <Box sx={{ w: 'full', h: 'full', touchAction: 'none', userSelect: 'none' }}>
<Flex <Flex
onClick={handleSelectBoard}
sx={{ sx={{
position: 'relative', position: 'relative',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
aspectRatio: '1/1', aspectRatio: '1/1',
borderRadius: 'base', borderRadius: 'base',
cursor: 'pointer',
w: 'full', w: 'full',
h: 'full', h: 'full',
}} }}
> >
<Flex <BoardContextMenu>
sx={{ {(ref) => (
w: 'full', <Flex
h: 'full', ref={ref}
justifyContent: 'center', onClick={handleSelectBoard}
alignItems: 'center',
borderRadius: 'base',
bg: 'base.200',
_dark: {
bg: 'base.800',
},
}}
>
<Flex
sx={{
w: 'full',
h: 'full',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Icon
boxSize={12}
as={FaFolder}
sx={{ sx={{
opacity: 0.7, w: 'full',
color: 'base.500', h: 'full',
position: 'relative',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 'base',
cursor: 'pointer',
bg: 'base.200',
_dark: { _dark: {
color: 'base.500', bg: 'base.800',
}, },
}} }}
/> >
</Flex> <Flex
</Flex> sx={{
<Flex w: 'full',
sx={{ h: 'full',
position: 'absolute', justifyContent: 'center',
insetInlineEnd: 0, alignItems: 'center',
top: 0, }}
p: 1, >
}} <Icon
> boxSize={12}
<Badge variant="solid" sx={BASE_BADGE_STYLES}> as={FaFolder}
{totalImages}/{totalAssets} sx={{
</Badge> opacity: 0.7,
</Flex> color: 'base.500',
<Box _dark: {
className="selection-box" color: 'base.500',
sx={{ },
position: 'absolute', }}
top: 0, />
insetInlineEnd: 0, </Flex>
bottom: 0, <Flex
insetInlineStart: 0, sx={{
borderRadius: 'base', position: 'absolute',
transitionProperty: 'common', insetInlineEnd: 0,
transitionDuration: 'common', top: 0,
shadow: isSelected ? 'selected.light' : undefined, p: 1,
_dark: { }}
shadow: isSelected ? 'selected.dark' : undefined, >
}, <Badge variant="solid" sx={BASE_BADGE_STYLES}>
}} {totalImages}/{totalAssets}
/> </Badge>
<IAIDroppable </Flex>
data={droppableData} {!autoAddBoardId && <AutoAddIcon />}
dropLabel={<Text fontSize="md">Move</Text>} <Box
/> className="selection-box"
sx={{
position: 'absolute',
top: 0,
insetInlineEnd: 0,
bottom: 0,
insetInlineStart: 0,
borderRadius: 'base',
transitionProperty: 'common',
transitionDuration: 'common',
shadow: isSelected ? 'selected.light' : undefined,
_dark: {
shadow: isSelected ? 'selected.dark' : undefined,
},
}}
/>
<IAIDroppable
data={droppableData}
dropLabel={<Text fontSize="md">Move</Text>}
/>
</Flex>
)}
</BoardContextMenu>
</Flex> </Flex>
</Box> </Box>
); );

View File

@ -1,11 +1,6 @@
import { MenuItem } from '@chakra-ui/react'; import { MenuItem } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { memo, useCallback } from 'react';
import { stateSelector } from 'app/store/store'; import { FaTrash } from 'react-icons/fa';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { autoAddBoardIdChanged } from 'features/gallery/store/gallerySlice';
import { memo, useCallback, useMemo } from 'react';
import { FaMinus, FaPlus, FaTrash } from 'react-icons/fa';
import { BoardDTO } from 'services/api/types'; import { BoardDTO } from 'services/api/types';
type Props = { type Props = {
@ -14,25 +9,6 @@ type Props = {
}; };
const GalleryBoardContextMenuItems = ({ board, setBoardToDelete }: Props) => { const GalleryBoardContextMenuItems = ({ board, setBoardToDelete }: Props) => {
const dispatch = useAppDispatch();
const selector = useMemo(
() =>
createSelector(
stateSelector,
({ gallery }) => {
const isSelectedForAutoAdd =
board.board_id === gallery.autoAddBoardId;
return { isSelectedForAutoAdd };
},
defaultSelectorOptions
),
[board.board_id]
);
const { isSelectedForAutoAdd } = useAppSelector(selector);
const handleDelete = useCallback(() => { const handleDelete = useCallback(() => {
if (!setBoardToDelete) { if (!setBoardToDelete) {
return; return;
@ -40,31 +16,8 @@ const GalleryBoardContextMenuItems = ({ board, setBoardToDelete }: Props) => {
setBoardToDelete(board); setBoardToDelete(board);
}, [board, setBoardToDelete]); }, [board, setBoardToDelete]);
const handleToggleAutoAdd = useCallback(() => {
dispatch(
autoAddBoardIdChanged(isSelectedForAutoAdd ? null : board.board_id)
);
}, [board.board_id, dispatch, isSelectedForAutoAdd]);
return ( return (
<> <>
{board.image_count > 0 && (
<>
{/* <MenuItem
isDisabled={!board.image_count}
icon={<FaImages />}
onClickCapture={handleAddBoardToBatch}
>
Add Board to Batch
</MenuItem> */}
</>
)}
<MenuItem
icon={isSelectedForAutoAdd ? <FaMinus /> : <FaPlus />}
onClickCapture={handleToggleAutoAdd}
>
{isSelectedForAutoAdd ? 'Disable Auto-Add' : 'Auto-Add to this Board'}
</MenuItem>
<MenuItem <MenuItem
sx={{ color: 'error.600', _dark: { color: 'error.300' } }} sx={{ color: 'error.600', _dark: { color: 'error.300' } }}
icon={<FaTrash />} icon={<FaTrash />}

View File

@ -51,7 +51,7 @@ const GalleryBoardName = (props: Props) => {
as={Button} as={Button}
onClick={onToggle} onClick={onToggle}
size="sm" size="sm"
variant="ghost" // variant="ghost"
sx={{ sx={{
position: 'relative', position: 'relative',
gap: 2, gap: 2,
@ -59,12 +59,12 @@ const GalleryBoardName = (props: Props) => {
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
px: 2, px: 2,
bg: 'base.100', // bg: 'base.100',
_dark: { bg: 'base.800' }, // _dark: { bg: 'base.800' },
_hover: { // _hover: {
bg: 'base.200', // bg: 'base.200',
_dark: { bg: 'base.700' }, // _dark: { bg: 'base.700' },
}, // },
}} }}
> >
<Spacer /> <Spacer />

View File

@ -3,6 +3,9 @@ import {
ButtonGroup, ButtonGroup,
Flex, Flex,
Spacer, Spacer,
Tab,
TabList,
Tabs,
VStack, VStack,
useDisclosure, useDisclosure,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
@ -87,30 +90,22 @@ const ImageGalleryContent = () => {
gap: 2, gap: 2,
}} }}
> >
<ButtonGroup isAttached sx={{ w: 'full' }}> <Tabs variant="line" size="sm" sx={{ w: 'full' }}>
<IAIButton <TabList>
leftIcon={<FaImages />} <Tab
size="sm" onClick={handleClickImages}
isChecked={galleryView === 'images'} sx={{ borderTopRadius: 'base', w: 'full' }}
onClick={handleClickImages} >
sx={{ Images
w: 'full', </Tab>
}} <Tab
> onClick={handleClickAssets}
Images sx={{ borderTopRadius: 'base', w: 'full' }}
</IAIButton> >
<IAIButton Assets
leftIcon={<FaServer />} </Tab>
size="sm" </TabList>
isChecked={galleryView === 'assets'} </Tabs>
onClick={handleClickAssets}
sx={{
w: 'full',
}}
>
Assets
</IAIButton>
</ButtonGroup>
</Flex> </Flex>
{selectedBoardId === 'batch' ? ( {selectedBoardId === 'batch' ? (

View File

@ -21,7 +21,7 @@ export type BoardId = string | undefined;
type GalleryState = { type GalleryState = {
selection: string[]; selection: string[];
shouldAutoSwitch: boolean; shouldAutoSwitch: boolean;
autoAddBoardId: string | null; autoAddBoardId: string | undefined;
galleryImageMinimumWidth: number; galleryImageMinimumWidth: number;
selectedBoardId: BoardId; selectedBoardId: BoardId;
galleryView: GalleryView; galleryView: GalleryView;
@ -32,7 +32,7 @@ type GalleryState = {
export const initialGalleryState: GalleryState = { export const initialGalleryState: GalleryState = {
selection: [], selection: [],
shouldAutoSwitch: true, shouldAutoSwitch: true,
autoAddBoardId: null, autoAddBoardId: undefined,
galleryImageMinimumWidth: 96, galleryImageMinimumWidth: 96,
selectedBoardId: undefined, selectedBoardId: undefined,
galleryView: 'images', galleryView: 'images',
@ -124,7 +124,10 @@ export const gallerySlice = createSlice({
state.batchImageNames = []; state.batchImageNames = [];
state.selection = []; state.selection = [];
}, },
autoAddBoardIdChanged: (state, action: PayloadAction<string | null>) => { autoAddBoardIdChanged: (
state,
action: PayloadAction<string | undefined>
) => {
state.autoAddBoardId = action.payload; state.autoAddBoardId = action.payload;
}, },
galleryViewChanged: (state, action: PayloadAction<GalleryView>) => { galleryViewChanged: (state, action: PayloadAction<GalleryView>) => {
@ -137,10 +140,11 @@ export const gallerySlice = createSlice({
(state, action) => { (state, action) => {
const deletedBoardId = action.meta.arg.originalArgs; const deletedBoardId = action.meta.arg.originalArgs;
if (deletedBoardId === state.selectedBoardId) { if (deletedBoardId === state.selectedBoardId) {
state.selectedBoardId = 'images'; state.selectedBoardId = undefined;
state.galleryView = 'images';
} }
if (deletedBoardId === state.autoAddBoardId) { if (deletedBoardId === state.autoAddBoardId) {
state.autoAddBoardId = null; state.autoAddBoardId = undefined;
} }
} }
); );
@ -153,7 +157,7 @@ export const gallerySlice = createSlice({
} }
if (!boards.map((b) => b.board_id).includes(state.autoAddBoardId)) { if (!boards.map((b) => b.board_id).includes(state.autoAddBoardId)) {
state.autoAddBoardId = null; state.autoAddBoardId = undefined;
} }
} }
); );

View File

@ -190,9 +190,6 @@ export const boardsApi = api.injectEndpoints({
) )
); );
}); });
// after deleting a board, select the 'All Images' board
dispatch(boardIdSelected('images'));
} catch { } catch {
//no-op //no-op
} }
@ -265,9 +262,6 @@ export const boardsApi = api.injectEndpoints({
) )
); );
}); });
// after deleting a board, select the 'All Images' board
dispatch(boardIdSelected('images'));
} catch { } catch {
//no-op //no-op
} }