Overhaul startpage (#821)

* 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

* 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

---------

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:
Simon
2025-12-10 00:22:58 +01:00
committed by GitHub
parent 38b95ae102
commit 8d8dca32d8
137 changed files with 5218 additions and 809 deletions
+62 -10
View File
@@ -1,26 +1,78 @@
import GameDto from "Frontend/generated/org/gameyfin/app/games/dto/GameDto";
import {CoverRow} from "Frontend/components/general/covers/CoverRow";
import {useSnapshot} from "valtio/react";
import {libraryState} from "Frontend/state/LibraryState";
import {gameState} from "Frontend/state/GameState";
import {useNavigate} from "react-router";
import React, {useEffect, useState} from "react";
import LibraryDto from "Frontend/generated/org/gameyfin/app/libraries/dto/LibraryDto";
import {collectionState} from "Frontend/state/CollectionState";
import CollectionDto from "Frontend/generated/org/gameyfin/app/collections/dto/CollectionDto";
import {StartPageDisplayCard} from "Frontend/components/general/cards/StartPageDisplayCard";
import {Link} from "@heroui/react";
import {CaretRightIcon} from "@phosphor-icons/react";
export default function HomeView() {
const navigate = useNavigate();
const librariesState = useSnapshot(libraryState);
const collectionsState = useSnapshot(collectionState);
const gamesState = useSnapshot(gameState);
const recentlyAddedGames = gamesState.recentlyAdded as GameDto[];
const gamesByLibrary = gamesState.gamesByLibraryId as Record<number, GameDto[]>;
const gamesByLibrary = gamesState.gamesByLibraryId;
const gamesByCollection = gamesState.gamesByCollectionId;
const [filteredAndSortedLibraries, setFilteredAndSortedLibraries] = useState<LibraryDto[]>([]);
const [filteredAndSortedCollections, setFilteredAndSortedCollections] = useState<CollectionDto[]>([]);
useEffect(() => {
const libraries = librariesState.sorted
.filter(library => library.metadata!.displayOnHomepage)
.filter(library =>
gamesByLibrary[library.id] && gamesByLibrary[library.id].length > 0
);
setFilteredAndSortedLibraries(libraries);
const collections = collectionsState.sorted
.filter(collection => collection.metadata!.displayOnHomepage)
.filter(collection =>
gamesByCollection[collection.id] && gamesByCollection[collection.id].length > 0
);
setFilteredAndSortedCollections(collections);
}, [librariesState.sorted, collectionsState.sorted, gamesByLibrary, gamesByCollection]);
return (
<div className="w-full">
<div className="flex flex-col gap-2">
<CoverRow title="Recently added" games={recentlyAddedGames}
onPressShowMore={() => navigate("/recently-added")}/>
{librariesState.libraries.map((library) => (
<div className="flex flex-col gap-4">
{(filteredAndSortedLibraries.length + filteredAndSortedCollections.length > 0) &&
<div className="flex flex-col gap-2">
<Link href="/search" className="flex flex-row gap-1 w-fit items-baseline" color="foreground"
underline="hover">
<p className="text-2xl font-bold mb-4">Your games</p>
<CaretRightIcon weight="bold" size={16}/>
</Link>
<div className="grid gap-4 grid-cols-[repeat(auto-fill,minmax(353px,1fr))]">
{filteredAndSortedLibraries.length > 0 &&
filteredAndSortedLibraries.map((library: LibraryDto) => (
<StartPageDisplayCard key={library.id} item={library}/>
))
}
{filteredAndSortedCollections.length > 0 &&
filteredAndSortedCollections.map((collection: CollectionDto) => (
<StartPageDisplayCard key={collection.id} item={collection}/>
))
}
</div>
</div>
}
{filteredAndSortedLibraries.map((library) => (
<CoverRow key={library.id} title={library.name}
games={gamesByLibrary[library.id] || []}
onPressShowMore={() => navigate("/library/" + library.id)}
link={"/library/" + library.id}
/>
))}
{filteredAndSortedCollections.map((collection) => (
<CoverRow key={collection.id} title={collection.name}
games={gamesByCollection[collection.id] || []}
link={"/collection/" + collection.id}
/>
))}
</div>