infinite scroll explore media

This commit is contained in:
BFlat committed 2023-12-21 19:15:19 +07:00
1 parent a63468a5a9
commit 5c6d0e0da3
3 files changed
+120 -66

No files matched your search

@@ -0,0 +1,26 @@
import MakeRequest from "../MakeRequest";
import { MediaFile } from "./GetMedia";
// import { MediaFileType } from "../_common/enums/MediaFileType";
import { Pagination } from "../_common/SharedFetchTypes";
export interface ListMediaFilesRequest {}
export interface ListMediaFilesResponse {
pagination: Pagination;
success: boolean;
results: MediaFile[];
}
export interface ListMediaFilesParams {
page_index: number;
}
export const ListMediaFiles = MakeRequest<
string,
ListMediaFilesRequest,
ListMediaFilesResponse,
ListMediaFilesParams
>({
method: "GET",
routingFunction: () => `/v1/media_files/list`,
});
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useEffect, useState } from "react";
import { useSession } from "hooks";
interface Props {
@@ -8,9 +8,18 @@ interface Props {
listSet: any;
pagePreset?: number;
requestList?: boolean;
isInfiniteScroll?: boolean;
}
export default function useListContent({ addQueries, fetcher, list, listSet, pagePreset = 0, requestList = false }: Props) {
export default function useListContent({
addQueries,
fetcher,
list,
listSet,
pagePreset = 0,
requestList = false,
isInfiniteScroll = false,
}: Props) {
const { user } = useSession();
const [filter, filterSet] = useState("all");
const [page, pageSet] = useState(pagePreset);
@@ -20,12 +29,17 @@ export default function useListContent({ addQueries, fetcher, list, listSet, pag
const pageChange = (page: number) => {
pageSet(page);
if (!isInfiniteScroll || page === 1) {
listSet([]); // Reset list if not in infinite scroll mode or starting from first page
}
statusSet(1);
};
const onChange = ({ target }: { target: { name: string, value: any} }) => {
const todo: { [ key: string]: (x: any) => void } = { filterSet, sortSet };
const onChange = ({ target }: { target: { name: string; value: any } }) => {
const todo: { [key: string]: (x: any) => void } = { filterSet, sortSet };
todo[target.name + "Set"](target.value);
pageSet(1); // Reset to first page on filter/sort change
listSet([]); // Reset list on filter/sort change
statusSet(1);
};
@@ -33,22 +47,40 @@ export default function useListContent({ addQueries, fetcher, list, listSet, pag
if (user && user.username) {
if (status === 1) {
statusSet(2);
fetcher(user.username, {}, {
page_index: page,
...addQueries, // eventually we should provide a way to type this ... or not. It works
...filter !== "all" ? { filter_media_type: filter } : {},
...sort !== "newest" ? { sort_ascending: true } : {},
}).then((res: any) => {
fetcher(
user.username,
{},
{
page_index: page,
...addQueries, // eventually we should provide a way to type this ... or not. It works
...(filter !== "all" ? { filter_media_type: filter } : {}),
...(sort !== "newest" ? { sort_ascending: true } : {}),
}
).then((res: any) => {
statusSet(3);
console.log("🧶",res);
console.log("🧶", res);
if (res.results && res.pagination) {
pageCountSet(res.pagination.total_page_count)
listSet(res.results);
pageCountSet(res.pagination.total_page_count);
if (isInfiniteScroll && page !== 1) {
listSet((prevList: any) => [...prevList, ...res.results]); // Append to list for infinite scroll
} else {
listSet(res.results); // Replace list for regular pagination
}
}
});
}
}
}, [addQueries, fetcher, filter, listSet, page, user, sort, status]);
}, [
addQueries,
fetcher,
filter,
listSet,
page,
user,
sort,
status,
isInfiniteScroll,
]);
return {
filter,
@@ -63,6 +95,6 @@ export default function useListContent({ addQueries, fetcher, list, listSet, pag
sort,
sortSet,
status,
statusSet
statusSet,
};
};
}
@@ -1,39 +1,40 @@
import React, { useRef, useState } from "react";
import React, { useCallback, useRef, useState } from "react";
import MasonryGrid from "components/common/MasonryGrid/MasonryGrid";
import mockMediaData from "./mockMediaData";
import AudioCard from "components/common/Card/AudioCard";
import ImageCard from "components/common/Card/ImageCard";
import VideoCard from "components/common/Card/VideoCard";
import Select from "components/common/Select";
import { TempSelect } from "components/common";
import {
faArrowDownWideShort,
faFilter,
} from "@fortawesome/pro-solid-svg-icons";
import AudioPlayerProvider from "components/common/AudioPlayer/AudioPlayerContext";
import SkeletonCard from "components/common/Card/SkeletonCard";
import { ListMediaFiles } from "@storyteller/components/src/api/media_files/ListMediaFiles";
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
import { useListContent } from "hooks";
import InfiniteScroll from "react-infinite-scroll-component";
export default function MediaTab() {
const gridContainerRef = useRef<HTMLDivElement | null>(null);
const [data, setData] = useState(mockMediaData);
const [isLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const fetchMoreData = () => {
// Simulate a delay (like fetching API)
setTimeout(() => {
const moreData = data.concat(
// Here, create more dummy data
mockMediaData.map(item => ({ ...item, id: item.token + data.length }))
);
const [list, listSet] = useState<MediaFile[]>([]);
const media = useListContent({
fetcher: ListMediaFiles,
list,
listSet,
pagePreset: 1,
requestList: true,
isInfiniteScroll: true,
});
setData(moreData);
if (moreData.length >= mockMediaData.length * 2) {
setHasMore(false);
}
}, 1500);
};
// Fetch more data for infinite scroll
const fetchMoreData = useCallback(() => {
if (media.page < media.pageCount) {
media.pageChange(media.page + 1);
}
}, [media]);
const filterOptions = [
{ value: "all", label: "All Media" },
@@ -52,17 +53,23 @@ export default function MediaTab() {
<>
<div className="d-flex flex-wrap gap-3 mb-3">
<div className="d-flex gap-2 flex-grow-1">
<Select
icon={faArrowDownWideShort}
options={sortOptions}
defaultValue={sortOptions[0]}
isSearchable={false}
<TempSelect
{...{
icon: faArrowDownWideShort,
options: sortOptions,
name: "sort",
onChange: media.onChange,
value: media.sort,
}}
/>
<Select
icon={faFilter}
options={filterOptions}
defaultValue={filterOptions[0]}
isSearchable={false}
<TempSelect
{...{
icon: faFilter,
options: filterOptions,
name: "filter",
onChange: media.onChange,
value: media.filter,
}}
/>
</div>
</div>
@@ -75,8 +82,9 @@ export default function MediaTab() {
</div>
) : (
<InfiniteScroll
dataLength={list.length}
next={fetchMoreData}
hasMore={hasMore}
hasMore={media.page < media.pageCount}
loader={
<div className="mt-4 d-flex justify-content-center">
<div className="spinner-border text-light" role="status">
@@ -84,41 +92,29 @@ export default function MediaTab() {
</div>
</div>
}
dataLength={data.length}
endMessage={
<p className="text-center mt-4 opacity-75">No more results.</p>
}
className="overflow-hidden"
>
<MasonryGrid
gridRef={gridContainerRef}
onLayoutComplete={() => console.log("Layout complete!")}
>
{data.map((data, index) => {
{media.list.map((data: MediaFile, index: number) => {
let card;
switch (data.media_type) {
case "audio":
card = (
<AudioCard
key={index}
data={data}
type="media"
showCreator={true}
/>
);
card = <AudioCard data={data} type="media" />;
break;
case "image":
card = (
<ImageCard
key={index}
data={data}
type="media"
showCreator={true}
/>
);
card = <ImageCard data={data} type="media" />;
break;
case "video":
card = <VideoCard key={index} data={data} type="media" />;
card = <VideoCard data={data} type="media" />;
break;
default:
card = <div key={index}>Unsupported media type</div>;
card = <div>Unsupported media type</div>;
}
return (
<div