[web] Reduce flicker when resizing gallery window (#5519)

This commit is contained in:
Manav Rathi
2025-04-03 12:28:55 +05:30
committed by GitHub
4 changed files with 40 additions and 78 deletions

View File

@@ -19,7 +19,7 @@
"react-dom": "^19.0.0",
"react-select": "^5.10.1",
"react-top-loading-bar": "^3.0.2",
"react-virtualized-auto-sizer": "^1.0.25",
"react-virtualized-auto-sizer": "^1.0.26",
"react-window": "^1.8.11",
"sanitize-filename": "^1.6.3",
"similarity-transformation": "^0.0.1",

View File

@@ -21,15 +21,11 @@ import {
import type { CollectionSummary } from "ente-new/photos/services/collection/ui";
import { CollectionsSortBy } from "ente-new/photos/services/collection/ui";
import { FlexWrapper, FluidContainer } from "ente-shared/components/Container";
import useWindowSize from "ente-shared/hooks/useWindowSize";
import { t } from "i18next";
import memoize from "memoize-one";
import React, { useEffect, useRef, useState } from "react";
import {
areEqual,
FixedSizeList as List,
ListChildComponentProps,
} from "react-window";
import AutoSizer from "react-virtualized-auto-sizer";
import { areEqual, FixedSizeList, ListChildComponentProps } from "react-window";
interface AllAlbums {
open: boolean;
@@ -84,7 +80,7 @@ export const AllAlbums: React.FC<AllAlbums> = ({
);
};
export const AllCollectionMobileBreakpoint = 559;
const Column3To2Breakpoint = 559;
const AllAlbumsDialog = styled(Dialog)(({ theme }) => ({
"& .MuiDialog-container": { justifyContent: "flex-end" },
@@ -94,7 +90,7 @@ const AllAlbumsDialog = styled(Dialog)(({ theme }) => ({
paddingRight: theme.spacing(1),
},
"& .MuiDialogContent-root": { padding: theme.spacing(2) },
[theme.breakpoints.down(AllCollectionMobileBreakpoint)]: {
[theme.breakpoints.down(Column3To2Breakpoint)]: {
"& .MuiPaper-root": { width: "324px" },
"& .MuiDialogContent-root": { padding: 6 },
},
@@ -142,20 +138,8 @@ const Title = ({
</DialogTitle>
);
const MobileColumns = 2;
const DesktopColumns = 3;
const CollectionRowItemSize = 154;
const getCollectionRowListHeight = (
collectionRowList: CollectionSummary[][],
windowSize: { height: number; width: number },
) =>
Math.min(
collectionRowList.length * CollectionRowItemSize + 32,
windowSize?.height - 177,
) || 0;
interface ItemData {
collectionRowList: CollectionSummary[][];
onCollectionClick: (id?: number) => void;
@@ -211,15 +195,21 @@ const AllAlbumsContent: React.FC<AllAlbumsContentProps> = ({
collectionSummaries,
onCollectionClick,
}) => {
const isTwoColumn = useMediaQuery(`(width < ${Column3To2Breakpoint}px)`);
const refreshInProgress = useRef(false);
const shouldRefresh = useRef(false);
const [collectionRowList, setCollectionRowList] = useState([]);
const windowSize = useWindowSize();
const columns = isTwoColumn ? 2 : 3;
const maxListContentHeight =
Math.ceil(collectionSummaries.length / columns) *
CollectionRowItemSize +
32; /* padding above first and below last row */
useEffect(() => {
if (!windowSize.width || !collectionSummaries) {
if (!collectionSummaries) {
return;
}
const main = async () => {
@@ -231,10 +221,6 @@ const AllAlbumsContent: React.FC<AllAlbumsContentProps> = ({
const collectionRowList: CollectionSummary[][] = [];
let index = 0;
const columns =
windowSize.width > AllCollectionMobileBreakpoint
? DesktopColumns
: MobileColumns;
while (index < collectionSummaries.length) {
const collectionRow: CollectionSummary[] = [];
for (
@@ -254,7 +240,7 @@ const AllAlbumsContent: React.FC<AllAlbumsContentProps> = ({
}
};
main();
}, [collectionSummaries, windowSize]);
}, [collectionSummaries, columns]);
// Bundle additional data to list items using the "itemData" prop.
// It will be accessible to item renderers as props.data.
@@ -262,19 +248,29 @@ const AllAlbumsContent: React.FC<AllAlbumsContentProps> = ({
const itemData = createItemData(collectionRowList, onCollectionClick);
return (
<DialogContent sx={{ "&&": { padding: 0 } }}>
<List
height={getCollectionRowListHeight(
collectionRowList,
windowSize,
<DialogContent
sx={{
"&&": { padding: 0 },
height: "min(80svh, var(--et-max-list-content-height))",
}}
style={
{
"--et-max-list-content-height": `${maxListContentHeight}px`,
} as React.CSSProperties
}
>
<AutoSizer>
{({ width, height }) => (
<FixedSizeList
{...{ width, height }}
itemCount={collectionRowList.length}
itemSize={CollectionRowItemSize}
itemData={itemData}
>
{AlbumsRow}
</FixedSizeList>
)}
width={"100%"}
itemCount={collectionRowList.length}
itemSize={CollectionRowItemSize}
itemData={itemData}
>
{AlbumsRow}
</List>
</AutoSizer>
</DialogContent>
);
};

View File

@@ -1,34 +0,0 @@
// https://usehooks.com/useWindowSize/
import { useEffect, useState } from "react";
// Hook
export default function useWindowSize() {
// Initialize state with undefined width/height so server and client renders match
// Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/
const [windowSize, setWindowSize] = useState<{
width: number;
height: number;
}>({ width: undefined, height: undefined });
useEffect(() => {
// Handler to call on window resize
function handleResize() {
// Set window width/height to state
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
// Add event listener
window.addEventListener("resize", handleResize);
// Call handler right away so state gets updated with initial window size
handleResize();
// Remove event listener on cleanup
return () => window.removeEventListener("resize", handleResize);
}, []); // Empty array ensures that effect is only run on mount
return windowSize;
}

View File

@@ -3381,10 +3381,10 @@ react-transition-group@^4.3.0, react-transition-group@^4.4.5:
loose-envify "^1.4.0"
prop-types "^15.6.2"
react-virtualized-auto-sizer@^1.0.25:
version "1.0.25"
resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.25.tgz#b13cbc528ac200be2bd1ffa40c8bb19bcc60ac3f"
integrity sha512-YHsksEGDfsHbHuaBVDYwJmcktblcHGafz4ZVuYPQYuSHMUGjpwmUCrAOcvMSGMwwk1eFWj1M/1GwYpNPuyhaBg==
react-virtualized-auto-sizer@^1.0.26:
version "1.0.26"
resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.26.tgz#e9470ef6a778dc4f1d5fd76305fa2d8b610c357a"
integrity sha512-CblNyiNVw2o+hsa5/49NH2ogGxZ+t+3aweRvNSq7TVjDIlwk7ir4lencEg5HxHeSzwNarSkNkiu0qJSOXtxm5A==
react-window@^1.8.11:
version "1.8.11"