mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-16 08:15:48 +00:00
Release 2.3.0 (#804)
* chore: bump version to v2.3.0-preview * Customize start page (#803) * Update ConfigService to support complex Objects Implemented tests for ConfigService * Added DB migration for config table * Fixed version in banner.txt not being displayed * Implement Library ordering Implement "Show recently added games on homepage" * Fix build.gradle.kts * FIx bug when creating libraries * Fix TypeScript errors Fix library sorting * Bump actions/checkout from 5 to 6 (#811) Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Added automatic scanning using file system watchers (#813) * Implement collections (#814) * Backend implementation for collections * Fix database schema and migration script * Refactor some config values Fix ArrayInput not being deactivatable * Remove "AutoRegisterNewUsers" config option * Fix bug when removing ignored paths * Add UI for collections (WIP) * Fix table actions not synced with state Fix tests * Finish implementation of collection feature * Fix tests * Bump actions/checkout from 5 to 6 (#815) Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix "allow guests to create game requests" not being enabled when guest access is activated * Fix: Disable loading of EditGameMetadataModal and MatchGameModal in GameView when user is not admin * Bump actions/checkout from 5 to 6 (#819) Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Overhaul startpage (#823) * WIP: Update start page layout * Performance improvements (lazy loading and virtualized grids/lists) Fix various smaller issues * Implement use of blurhash for all images in backend and covers in frontend * Fix bugs and test * Fix code analysis issues * Remove "UI settings" since they have been made obsolete * Remove length limit from "image.originalUrl" (#824) * Remove alpine based image (#825) * Fix bug when games from library are still in a collection, thus prevention deletion of said library * Delete image files in background * Fix layout --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1,16 +1,110 @@
|
||||
import GameDto from "Frontend/generated/org/gameyfin/app/games/dto/GameDto";
|
||||
import {GameCover} from "Frontend/components/general/covers/GameCover";
|
||||
import type {CellComponentProps} from "react-window";
|
||||
import {Grid} from "react-window";
|
||||
import {useEffect, useRef, useState} from "react";
|
||||
|
||||
interface CoverGridProps {
|
||||
games: GameDto[];
|
||||
}
|
||||
|
||||
// Constants for grid layout
|
||||
const MIN_COLUMN_WIDTH = 180; // Minimum width per item (minmax value from original)
|
||||
const MAX_COLUMN_WIDTH = 212; // Maximum width per item (minmax value from original)
|
||||
const GAP = 16; // gap-4 = 1rem = 16px
|
||||
const ASPECT_RATIO = 12 / 17; // Game cover aspect ratio (width/height)
|
||||
|
||||
export default function CoverGrid({games}: CoverGridProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [containerWidth, setContainerWidth] = useState(0);
|
||||
|
||||
// Update container width on resize
|
||||
useEffect(() => {
|
||||
const updateDimensions = () => {
|
||||
if (containerRef.current) {
|
||||
setContainerWidth(containerRef.current.offsetWidth);
|
||||
}
|
||||
};
|
||||
|
||||
const resizeObserver = new ResizeObserver(updateDimensions);
|
||||
if (containerRef.current) {
|
||||
resizeObserver.observe(containerRef.current);
|
||||
}
|
||||
|
||||
updateDimensions();
|
||||
|
||||
return () => resizeObserver.disconnect();
|
||||
}, []);
|
||||
|
||||
// Calculate how many columns can fit
|
||||
const columnCount = Math.max(1, Math.floor((containerWidth + GAP) / (MIN_COLUMN_WIDTH + GAP)));
|
||||
|
||||
// Calculate actual column width to distribute space evenly (up to MAX_COLUMN_WIDTH)
|
||||
const actualColumnWidth = Math.min(
|
||||
MAX_COLUMN_WIDTH,
|
||||
Math.floor((containerWidth - (columnCount - 1) * GAP) / columnCount)
|
||||
);
|
||||
|
||||
// Calculate cover height based on width and aspect ratio
|
||||
// GameCover's size prop is the height, so we need to calculate height from width
|
||||
const coverHeight = Math.floor(actualColumnWidth / ASPECT_RATIO);
|
||||
|
||||
// Calculate row count
|
||||
const rowCount = Math.ceil(games.length / columnCount);
|
||||
|
||||
// Cell renderer for react-window Grid
|
||||
const Cell = ({
|
||||
columnIndex,
|
||||
rowIndex,
|
||||
style
|
||||
}: CellComponentProps<{}>) => {
|
||||
const gameIndex = rowIndex * columnCount + columnIndex;
|
||||
|
||||
// Return empty cell if we're past the end of the games array
|
||||
if (gameIndex >= games.length) {
|
||||
return <div style={style}/>;
|
||||
}
|
||||
|
||||
const game = games[gameIndex];
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
...style,
|
||||
paddingBottom: GAP,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
boxSizing: 'border-box'
|
||||
}}
|
||||
>
|
||||
<GameCover game={game} interactive={true} size={coverHeight} lazy={true}/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Column width function to handle the last column differently
|
||||
const getColumnWidth = (index: number) => {
|
||||
// Last column doesn't need gap after it
|
||||
if (index === columnCount - 1) {
|
||||
return actualColumnWidth;
|
||||
}
|
||||
return actualColumnWidth + GAP;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-[repeat(auto-fill,minmax(180px,212px))] gap-4 justify-center">
|
||||
{games.map((game) => (
|
||||
<GameCover key={game.id} game={game} interactive={true}/>
|
||||
))}
|
||||
<div ref={containerRef} className="w-full">
|
||||
{containerWidth > 0 && (
|
||||
<Grid<{}>
|
||||
columnCount={columnCount}
|
||||
columnWidth={getColumnWidth}
|
||||
rowCount={rowCount}
|
||||
rowHeight={coverHeight + GAP}
|
||||
defaultWidth={containerWidth}
|
||||
cellComponent={Cell}
|
||||
cellProps={{}}
|
||||
style={{overflowX: 'hidden'}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user