Skip to content

Commit

Permalink
Fix flickers in collection page (#650)
Browse files Browse the repository at this point in the history
  • Loading branch information
harshithmohan authored Oct 21, 2023
1 parent f4053dd commit c74e5b6
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 36 deletions.
4 changes: 3 additions & 1 deletion src/components/Collection/CollectionTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ const CollectionTitle = ({ count, filterOrGroup }: Props) => (
)}
<span>|</span>
<span className="text-panel-important">
{count}
{/* Count is set to -1 when series data is empty and is used as a flag to signify that in other places */}
{/* But ideally we should 0 to the user */}
{count === -1 ? 0 : count}
&nbsp;Items
</span>
</div>
Expand Down
75 changes: 50 additions & 25 deletions src/components/Collection/CollectionView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import { useGetSettingsQuery } from '@/core/rtkQuery/splitV3Api/settingsApi';
import { useLazyGetGroupViewQuery } from '@/core/rtkQuery/splitV3Api/webuiApi';
import { initialSettings } from '@/pages/settings/SettingsPage';

import type { InfiniteResultType } from '@/core/types/api';
import type { SeriesType } from '@/core/types/api/series';

type Props = {
mode: string;
setGroupTotal: (total: number) => void;
setTimelineSeries: (series: SeriesType[]) => void;
type: 'collection' | 'group';
isSidebarOpen: boolean;
};

Expand All @@ -40,7 +40,7 @@ export const listItemSize = {
gap: 32,
};

const CollectionView = ({ isSidebarOpen, mode, setGroupTotal, setTimelineSeries, type }: Props) => {
const CollectionView = ({ isSidebarOpen, mode, setGroupTotal, setTimelineSeries }: Props) => {
const { filterId, groupId } = useParams();

const settingsQuery = useGetSettingsQuery();
Expand All @@ -64,30 +64,35 @@ const CollectionView = ({ isSidebarOpen, mode, setGroupTotal, setTimelineSeries,
const [fetchingPage, setFetchingPage] = useState(false);

const [fetchGroups, groupsData] = useLazyGetGroupsQuery();
const [fetchSeries, seriesData] = useLazyGetGroupSeriesQuery();

const pages = useMemo(
() => (type === 'collection' ? groupsData.data?.pages : seriesData.data?.pages) ?? {},
[groupsData, seriesData, type],
const [fetchSeries, seriesDataResult] = useLazyGetGroupSeriesQuery();
const [seriesData, setSeriesData] = useState<InfiniteResultType<SeriesType[]>>({ pages: [], total: -1 });

const [pages, total] = useMemo(
() => {
const data = groupId ? seriesData : groupsData?.data;
return [data?.pages ?? {}, data?.total ?? 0];
},
[groupId, groupsData, seriesData],
);
const total = (type === 'collection' ? groupsData.data?.total : seriesData.data?.total) ?? 0;

const [fetchGroupExtras, groupExtrasData] = useLazyGetGroupViewQuery();
const groupExtras = groupExtrasData.data ?? [];

useEffect(() => {
setGroupTotal(total);
if (type === 'group' && pages[1]) {
if (groupId && pages[1]) {
setTimelineSeries(pages[1] as SeriesType[]);
} else {
setTimelineSeries([]);
}
}, [total, setGroupTotal, type, pages, setTimelineSeries]);
}, [total, setGroupTotal, groupId, pages, setTimelineSeries]);

// 999 to make it effectively infinite since Group/{id}/Series is not paginated
const pageSize = useMemo(() => (type === 'collection' ? defaultPageSize : 999), [type]);
const pageSize = useMemo(() => (groupId ? 999 : defaultPageSize), [groupId]);

const fetchPage = useMemo(() =>
debounce((page: number) => {
if (type === 'collection') {
if (!groupId) {
fetchGroups({ page, pageSize, filterId: filterId ?? '0', randomImages: showRandomPoster }).then(
(result) => {
if (!result.data) return;
Expand All @@ -101,25 +106,38 @@ const CollectionView = ({ isSidebarOpen, mode, setGroupTotal, setTimelineSeries,
},
).catch(error => console.error(error)).finally(() => setFetchingPage(false));
} else {
fetchSeries({ groupId, randomImages: showRandomPoster }).then().catch(error => console.error(error));
fetchSeries({ groupId, randomImages: showRandomPoster })
.then(result => result.data && setSeriesData(result.data))
.catch(error => console.error(error)).finally(() => setFetchingPage(false));
}
}, 200), [filterId, fetchGroups, fetchGroupExtras, fetchSeries, groupId, pageSize, showRandomPoster, type]);
}, 200), [filterId, fetchGroups, fetchGroupExtras, fetchSeries, groupId, pageSize, showRandomPoster]);

useEffect(() => {
fetchPage.cancel();
setFetchingPage(false);

fetchPage(1);
const shouldFetch = groupId ? true : groupsData.isUninitialized;

if (shouldFetch) {
fetchPage(1);
}

return () => fetchPage.cancel();
}, [filterId, fetchPage]);
}, [filterId, groupId, groupsData.isUninitialized, fetchPage]);

useEffect(() => {
if (!groupId) setSeriesData({ pages: [], total: -1 });
}, [groupId]);

const { scrollRef } = useOutletContext<{ scrollRef: React.RefObject<HTMLDivElement> }>();

const [gridContainerRef, gridContainerBounds] = useMeasure();

const itemsPerRow = Math.max(1, Math.floor(gridContainerBounds.width / itemWidth));
const count = useMemo(() => Math.ceil(total / itemsPerRow), [total, itemsPerRow]);
const [itemsPerRow, count] = useMemo(() => {
const tempItemsPerRow = Math.max(1, Math.floor((gridContainerBounds.width) / itemWidth));
const tempCount = total === -1 ? 0 : Math.ceil(total / tempItemsPerRow);
return [tempItemsPerRow, tempCount];
}, [gridContainerBounds.width, itemWidth, total]);

const virtualizer = useVirtualizer({
count,
Expand All @@ -128,17 +146,24 @@ const CollectionView = ({ isSidebarOpen, mode, setGroupTotal, setTimelineSeries,
overscan: 2,
});

if (total === 0) {
if (total <= 0) {
const isLoading = groupId
? (seriesDataResult.isUninitialized || seriesDataResult.isLoading)
: (groupsData.isUninitialized || groupsData.isLoading);
return (
<div
className={cx(
'flex grow rounded-md items-center font-semibold justify-center',
mode === 'poster' && 'px-6 py-8 bg-panel-background border-panel-border border',
)}
>
{groupsData.isUninitialized || groupsData.isLoading
? <Icon path={mdiLoading} size={3} className="text-panel-primary" spin />
: 'No series/groups available!'}
{/* This is always equal width to the actual grid container so we are using the ref here */}
{/* Otherwise we would need two refs to remove flicker */}
<div className="flex w-full justify-center" ref={gridContainerRef}>
{isLoading || seriesData.total === -1
? <Icon path={mdiLoading} size={3} className="text-panel-primary" spin />
: 'No series/groups available!'}
</div>
</div>
);
}
Expand All @@ -150,7 +175,7 @@ const CollectionView = ({ isSidebarOpen, mode, setGroupTotal, setTimelineSeries,
mode === 'poster' && 'px-6 py-8 bg-panel-background border-panel-border border',
)}
>
<div className="relative w-full" style={{ height: virtualizer.getTotalSize() }} ref={gridContainerRef}>
<div className="relative w-full" style={{ height: virtualizer.getTotalSize() }}>
{/* Each row is considered a virtual item here instead of each group */}
{virtualizer.getVirtualItems().map((virtualRow) => {
const { index } = virtualRow;
Expand Down Expand Up @@ -204,13 +229,13 @@ const CollectionView = ({ isSidebarOpen, mode, setGroupTotal, setTimelineSeries,
} else if (item) {
items.push(
mode === 'poster'
? <PosterViewItem item={item} key={`group-${item.IDs.ID}`} isSeries={type === 'group'} />
? <PosterViewItem item={item} key={`group-${item.IDs.ID}`} isSeries={!!groupId} />
: (
<ListViewItem
item={item}
mainSeries={groupExtras.find(extra => extra.ID === item.IDs.ID)}
key={`group-${item.IDs.ID}`}
isSeries={type === 'group'}
isSeries={!!groupId}
isSidebarOpen={isSidebarOpen}
/>
),
Expand Down
6 changes: 3 additions & 3 deletions src/core/router/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ const router = createBrowserRouter(
</Route>
<Route path="log" element={<LogsPage />} />
<Route path="collection">
<Route index element={<Collection type="collection" />} />
<Route path="filter/:filterId" element={<Collection type="collection" />} />
<Route path="group/:groupId" element={<Collection type="group" />} />
<Route index element={<Collection />} />
<Route path="filter/:filterId" element={<Collection />} />
<Route path="group/:groupId" element={<Collection />} />
<Route path="series/:seriesId" element={<Series />}>
<Route index element={<Navigate to="overview" replace />} />
<Route path="overview" element={<SeriesOverview />} />
Expand Down
9 changes: 2 additions & 7 deletions src/pages/collection/Collection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,12 @@ const TimelineSidebar = ({ series }: { series: SeriesType[] }) => (
</div>
);

type Props = {
type: 'collection' | 'group';
};

function Collection({ type }: Props) {
function Collection() {
const { filterId, groupId } = useParams();

const filterData = useGetFilterQuery({ filterId }, { skip: !filterId });
const groupData = useGetGroupQuery({ groupId: groupId! }, { skip: !groupId });
const subsectionName = type === 'collection' ? filterData?.data?.Name : groupData?.data?.Name;
const subsectionName = groupId ? groupData?.data?.Name : filterData?.data?.Name;

const settingsQuery = useGetSettingsQuery();
const settings = useMemo(() => settingsQuery?.data ?? initialSettings, [settingsQuery]);
Expand Down Expand Up @@ -127,7 +123,6 @@ function Collection({ type }: Props) {
mode={mode}
setGroupTotal={setGroupTotal}
setTimelineSeries={setTimelineSeries}
type={type}
isSidebarOpen={showFilterSidebar}
/>
<div
Expand Down

0 comments on commit c74e5b6

Please sign in to comment.