feat(ui): rudimentary categorized gallery image fetching

This commit is contained in:
psychedelicious 2023-06-21 19:57:22 +10:00
parent d501986610
commit f560a462a0
2 changed files with 78 additions and 31 deletions

View File

@ -56,36 +56,39 @@ import {
imageCategoriesChanged, imageCategoriesChanged,
selectImagesAll, selectImagesAll,
} from '../store/imagesSlice'; } from '../store/imagesSlice';
import { receivedPageOfImages } from 'services/thunks/image'; import {
IMAGES_PER_PAGE,
receivedImages,
receivedPageOfImages,
} from 'services/thunks/image';
import BoardsList from './Boards/BoardsList'; import BoardsList from './Boards/BoardsList';
import { boardsSelector, selectBoardsById } from '../store/boardSlice'; import { boardsSelector } from '../store/boardSlice';
import { ChevronUpIcon } from '@chakra-ui/icons'; import { ChevronUpIcon } from '@chakra-ui/icons';
import { useListAllBoardsQuery } from 'services/apiSlice'; import { useListAllBoardsQuery } from 'services/apiSlice';
const itemSelector = createSelector( const itemSelector = createSelector(
[(state: RootState) => state], [(state: RootState) => state],
(state) => { (state) => {
const { images, boards } = state; const { categories, total, isLoading } = state.images;
const { selectedBoardId } = state.boards;
const { categories } = images;
const allImages = selectImagesAll(state); const allImages = selectImagesAll(state);
const items = allImages.filter((i) =>
categories.includes(i.image_category)
);
const areMoreAvailable = items.length < images.total;
const isLoading = images.isLoading;
const selectedBoard = boards.selectedBoardId const images = allImages.filter((i) => {
? selectBoardsById(state, boards.selectedBoardId) const isInCategory = categories.includes(i.image_category);
: undefined; const isInSelectedBoard = selectedBoardId
? i.board_id === selectedBoardId
: true;
return isInCategory && isInSelectedBoard;
});
const areMoreAvailable = images.length < total;
return { return {
items, images,
isLoading, isLoading,
areMoreAvailable, areMoreAvailable,
categories: images.categories, categories,
selectedBoard,
}; };
}, },
defaultSelectorOptions defaultSelectorOptions
@ -148,7 +151,7 @@ const ImageGalleryContent = () => {
selectedBoardId, selectedBoardId,
} = useAppSelector(mainSelector); } = useAppSelector(mainSelector);
const { items, areMoreAvailable, isLoading, categories } = const { images, areMoreAvailable, isLoading, categories } =
useAppSelector(itemSelector); useAppSelector(itemSelector);
const { selectedBoard } = useListAllBoardsQuery(undefined, { const { selectedBoard } = useListAllBoardsQuery(undefined, {
@ -158,7 +161,7 @@ const ImageGalleryContent = () => {
}); });
const handleLoadMoreImages = useCallback(() => { const handleLoadMoreImages = useCallback(() => {
dispatch(receivedPageOfImages()); dispatch(receivedPageOfImages({}));
}, [dispatch]); }, [dispatch]);
const handleEndReached = useMemo(() => { const handleEndReached = useMemo(() => {
@ -208,6 +211,17 @@ const ImageGalleryContent = () => {
dispatch(setGalleryView('assets')); dispatch(setGalleryView('assets'));
}, [dispatch]); }, [dispatch]);
useEffect(() => {
if (images.length < 20) {
dispatch(
receivedPageOfImages({
categories,
boardId: selectedBoardId,
})
);
}
}, [categories, dispatch, images.length, selectedBoardId]);
return ( return (
<VStack <VStack
sx={{ sx={{
@ -335,13 +349,13 @@ const ImageGalleryContent = () => {
</Box> </Box>
</Box> </Box>
<Flex direction="column" gap={2} h="full" w="full"> <Flex direction="column" gap={2} h="full" w="full">
{items.length || areMoreAvailable ? ( {images.length || areMoreAvailable ? (
<> <>
<Box ref={rootRef} data-overlayscrollbars="" h="100%"> <Box ref={rootRef} data-overlayscrollbars="" h="100%">
{shouldUseSingleGalleryColumn ? ( {shouldUseSingleGalleryColumn ? (
<Virtuoso <Virtuoso
style={{ height: '100%' }} style={{ height: '100%' }}
data={items} data={images}
endReached={handleEndReached} endReached={handleEndReached}
scrollerRef={(ref) => setScrollerRef(ref)} scrollerRef={(ref) => setScrollerRef(ref)}
itemContent={(index, item) => ( itemContent={(index, item) => (
@ -357,7 +371,7 @@ const ImageGalleryContent = () => {
) : ( ) : (
<VirtuosoGrid <VirtuosoGrid
style={{ height: '100%' }} style={{ height: '100%' }}
data={items} data={images}
endReached={handleEndReached} endReached={handleEndReached}
components={{ components={{
Item: ItemContainer, Item: ItemContainer,

View File

@ -1,5 +1,6 @@
import { createAppAsyncThunk } from 'app/store/storeUtils'; import { createAppAsyncThunk } from 'app/store/storeUtils';
import { selectImagesAll } from 'features/gallery/store/imagesSlice'; import { selectImagesAll } from 'features/gallery/store/imagesSlice';
import { size } from 'lodash-es';
import { ImagesService } from 'services/api'; import { ImagesService } from 'services/api';
type imageUrlsReceivedArg = Parameters< type imageUrlsReceivedArg = Parameters<
@ -121,25 +122,57 @@ type ImagesListedArg = Parameters<
export const IMAGES_PER_PAGE = 20; export const IMAGES_PER_PAGE = 20;
const DEFAULT_IMAGES_LISTED_ARG = {
isIntermediate: false,
limit: IMAGES_PER_PAGE,
};
/** /**
* `ImagesService.listImagesWithMetadata()` thunk * `ImagesService.listImagesWithMetadata()` thunk
*/ */
export const receivedPageOfImages = createAppAsyncThunk( export const receivedPageOfImages = createAppAsyncThunk(
'api/receivedPageOfImages', 'api/receivedPageOfImages',
async (_, { getState }) => { async (arg: ImagesListedArg, { getState }) => {
const state = getState(); const state = getState();
const { categories } = state.images; const { categories } = state.images;
const { selectedBoardId } = state.boards;
const totalImagesInFilter = selectImagesAll(state).filter((i) => const images = selectImagesAll(state).filter((i) => {
categories.includes(i.image_category) const isInCategory = categories.includes(i.image_category);
).length; const isInSelectedBoard = selectedBoardId
? i.board_id === selectedBoardId
const response = await ImagesService.listImagesWithMetadata({ : true;
categories, return isInCategory && isInSelectedBoard;
isIntermediate: false,
offset: totalImagesInFilter,
limit: IMAGES_PER_PAGE,
}); });
let queryArg: ReceivedImagesArg = {};
if (size(arg)) {
queryArg = { ...DEFAULT_IMAGES_LISTED_ARG, ...arg };
} else {
queryArg = {
...DEFAULT_IMAGES_LISTED_ARG,
categories,
offset: images.length,
};
}
const response = await ImagesService.listImagesWithMetadata(queryArg);
return response;
}
);
type ReceivedImagesArg = Parameters<
(typeof ImagesService)['listImagesWithMetadata']
>[0];
/**
* `ImagesService.listImagesWithMetadata()` thunk
*/
export const receivedImages = createAppAsyncThunk(
'api/receivedImages',
async (arg: ReceivedImagesArg, { getState }) => {
const response = await ImagesService.listImagesWithMetadata(arg);
return response; return response;
} }
); );