Skip to content

Commit eb294ec

Browse files
authored
Force Attachment Gallery to load all attachments on screen (#6400)
* Use Gallery column count in loading skeleton * Lint code with ESLint and Prettier Triggered by d5f6dcf on branch refs/heads/issue-3567 * Force loading if not complete and container can't scroll * Force fetch on scale change and window change * Always load attachments if skeleton is on screen * Lint code with ESLint and Prettier Triggered by 1442d51 on branch refs/heads/issue-3567
1 parent 108eafe commit eb294ec

2 files changed

Lines changed: 53 additions & 15 deletions

File tree

specifyweb/frontend/js_src/lib/components/Attachments/Gallery.tsx

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22

33
import { attachmentsText } from '../../localization/attachments';
4+
import { listen } from '../../utils/events';
45
import type { RA } from '../../utils/types';
56
import { replaceItem } from '../../utils/utils';
67
import { Container } from '../Atoms';
@@ -11,11 +12,13 @@ import type { Attachment } from '../DataModel/types';
1112
import { raise } from '../Errors/Crash';
1213
import { ErrorBoundary } from '../Errors/ErrorBoundary';
1314
import { ResourceView } from '../Forms/ResourceView';
15+
import { getPref } from '../InitialContext/remotePrefs';
1416
import { AttachmentGallerySkeleton } from '../SkeletonLoaders/AttachmentGallery';
1517
import { AttachmentCell } from './Cell';
1618
import { AttachmentDialog } from './Dialog';
1719

18-
const preFetchDistance = 200;
20+
const defaultPreFetchDistance = 200;
21+
const attachmentSkeletonRows = 2;
1922

2023
export function AttachmentGallery({
2124
attachments,
@@ -37,6 +40,32 @@ export function AttachmentGallery({
3740
}): JSX.Element {
3841
const containerRef = React.useRef<HTMLElement | null>(null);
3942

43+
const [preFetchDistance, setPreFetchDistance] = React.useState<number>(
44+
defaultPreFetchDistance
45+
);
46+
const [columns, setColumns] = React.useState<number>(3);
47+
const attachmentHeight = getPref('attachment.preview_size');
48+
React.useEffect(() => {
49+
const calculateColumns = (ref: React.RefObject<HTMLElement | null>) => {
50+
if (ref.current) {
51+
const rootFontSize = Number.parseFloat(
52+
window.getComputedStyle(document.documentElement).fontSize
53+
); // Equivalent to 1rem
54+
const gap = rootFontSize;
55+
const columnWidth = scale * rootFontSize + gap;
56+
setPreFetchDistance(
57+
Math.max(
58+
defaultPreFetchDistance,
59+
(attachmentHeight + gap) * attachmentSkeletonRows + gap
60+
)
61+
);
62+
setColumns(Math.floor((ref.current.clientWidth - gap) / columnWidth));
63+
}
64+
};
65+
calculateColumns(containerRef);
66+
return listen(window, 'resize', () => calculateColumns(containerRef));
67+
}, [scale]);
68+
4069
const rawFillPage = React.useCallback(
4170
async () =>
4271
// Fetch more attachments when within 200px of the bottom
@@ -50,15 +79,16 @@ export function AttachmentGallery({
5079

5180
const fillPage = handleFetchMore === undefined ? undefined : rawFillPage;
5281

53-
React.useEffect(
54-
() =>
55-
// Fetch attachments while scroll bar is not visible
56-
void (containerRef.current?.scrollHeight ===
57-
containerRef.current?.clientHeight
58-
? fillPage?.().catch(raise)
59-
: undefined),
60-
[fillPage, attachments]
61-
);
82+
React.useEffect(() => {
83+
// Fetch attachments while scroll bar is not visible
84+
const noScrollFetch = () => {
85+
setTimeout(() => {
86+
fillPage?.().catch(raise);
87+
}, 10); // Wait for container to re-render before checking if scrolling is disabled
88+
};
89+
noScrollFetch();
90+
return listen(window, 'resize', noScrollFetch);
91+
}, [fillPage, attachments, scale]);
6292

6393
const [viewRecord, setViewRecord] = React.useState<
6494
SpecifyResource<AnySchema> | undefined
@@ -75,11 +105,11 @@ export function AttachmentGallery({
75105
return (
76106
<>
77107
<Container.Base
78-
className="grid flex-1 grid-cols-[repeat(auto-fit,minmax(var(--scale),1fr))]
79-
items-center gap-4 shadow-none"
108+
className="grid flex-1 items-center gap-4 shadow-none"
80109
forwardRef={containerRef}
81110
style={
82111
{
112+
gridTemplateColumns: `repeat(${columns}, minmax(0px, 1fr))`,
83113
'--scale': `${scale}rem`,
84114
} as React.CSSProperties
85115
}
@@ -106,7 +136,11 @@ export function AttachmentGallery({
106136
{isComplete ? (
107137
attachments.length === 0 && <p>{attachmentsText.noAttachments()}</p>
108138
) : (
109-
<AttachmentGallerySkeleton />
139+
<AttachmentGallerySkeleton
140+
fetchNumber={
141+
columns * attachmentSkeletonRows - (attachments.length % columns)
142+
}
143+
/>
110144
)}
111145
</Container.Base>
112146
{typeof viewRecord === 'object' && (

specifyweb/frontend/js_src/lib/components/SkeletonLoaders/AttachmentGallery.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ import React from 'react';
33
import { DEFAULT_FETCH_LIMIT } from '../DataModel/collection';
44
import { Skeleton } from './Skeleton';
55

6-
export function AttachmentGallerySkeleton() {
6+
export function AttachmentGallerySkeleton({
7+
fetchNumber = DEFAULT_FETCH_LIMIT,
8+
}: {
9+
readonly fetchNumber?: number;
10+
}): JSX.Element {
711
return (
812
<Skeleton.Root className="contents">
9-
{Array.from({ length: DEFAULT_FETCH_LIMIT }, (_, index) => (
13+
{Array.from({ length: fetchNumber }, (_, index) => (
1014
<Skeleton.Square key={index} />
1115
))}
1216
</Skeleton.Root>

0 commit comments

Comments
 (0)