Implement RecentlyAddedView and LibraryView

This commit is contained in:
grimsi
2025-05-24 19:04:05 +02:00
parent 64f81d1329
commit fe561c42b4
8 changed files with 79 additions and 19 deletions
+4 -3
View File
@@ -141,7 +141,8 @@
"react-markdown": "$react-markdown",
"remark-breaks": "$remark-breaks",
"valtio": "$valtio",
"valtio-reactive": "$valtio-reactive"
"valtio-reactive": "$valtio-reactive",
"fzf": "$fzf"
},
"vaadin": {
"dependencies": {
@@ -202,6 +203,6 @@
"workbox-core": "7.3.0",
"workbox-precaching": "7.3.0"
},
"hash": "dc682332ca36d64f455f6e13888e1ffcca97e888cbad8d356973e830f7463a10"
"hash": "5946db89f1666178a141f4993199185e21c2d2fba31f8cbe64550829f8450c78"
}
}
}
@@ -10,7 +10,7 @@ export default function SearchBar() {
const navigate = useNavigate();
const state = useSnapshot(gameState);
const games = state.sortedByMostRecentlyUpdated as GameDto[];
const games = state.recentlyUpdated as GameDto[];
return <Autocomplete
aria-label="Search for games"
+14 -4
View File
@@ -22,6 +22,8 @@ import {SystemManagement} from "Frontend/components/administration/SystemManagem
import GameView from "Frontend/views/GameView";
import LibraryManagementView from "Frontend/views/LibraryManagementView";
import SearchView from "Frontend/views/SearchView";
import RecentlyAddedView from "Frontend/views/RecentlyAddedView";
import LibraryView from "Frontend/views/LibraryView";
export const routes = protectRoutes([
{
@@ -35,14 +37,22 @@ export const routes = protectRoutes([
{
index: true, element: <HomeView/>
},
{
path: 'game/:gameId',
element: <GameView/>
},
{
path: '/search',
element: <SearchView/>
},
{
path: 'recently-added',
element: <RecentlyAddedView/>
},
{
path: 'library/:libraryId',
element: <LibraryView/>
},
{
path: 'game/:gameId',
element: <GameView/>
},
{
path: 'settings',
element: <ProfileView/>,
@@ -12,8 +12,8 @@ type GameState = {
games: GameDto[];
gamesByLibraryId: Record<number, GameDto[]>;
sortedAlphabetically: GameDto[];
sortedByMostRecentlyAdded: GameDto[];
sortedByMostRecentlyUpdated: GameDto[];
recentlyAdded: GameDto[];
recentlyUpdated: GameDto[];
randomlyOrderedGamesByLibraryId: Record<number, GameDto[]>;
knownPublishers: Set<string>;
knownDevelopers: Set<string>;
@@ -33,7 +33,7 @@ export const gameState = proxy<GameState>({
return Object.values<GameDto>(this.state);
},
get gamesByLibraryId() {
return this.games.reduce((acc: Record<number, GameDto[]>, game: GameDto) => {
return this.sortedAlphabetically.reduce((acc: Record<number, GameDto[]>, game: GameDto) => {
(acc[game.libraryId] ||= []).push(game);
return acc;
}, {});
@@ -42,13 +42,15 @@ export const gameState = proxy<GameState>({
return this.games
.sort((a: GameDto, b: GameDto) => a.title.localeCompare(b.title, undefined, {sensitivity: 'base'}));
},
get sortedByMostRecentlyAdded() {
get recentlyAdded() {
return this.games
.sort((a: GameDto, b: GameDto) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
.sort((a: GameDto, b: GameDto) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
.slice(0, 25);
},
get sortedByMostRecentlyUpdated() {
get recentlyUpdated() {
return this.games
.sort((a: GameDto, b: GameDto) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
.sort((a: GameDto, b: GameDto) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())
.slice(0, 25);
},
get randomlyOrderedGamesByLibraryId() {
const result: Record<number, GameDto[]> = {};
@@ -38,7 +38,7 @@ export default function GameView() {
useEffect(() => {
initializeGameState().then((state) => {
if (!gameId || !state.state[parseInt(gameId)]) {
navigate("/");
navigate("/", {replace: true});
}
});
}, [gameId]);
@@ -3,22 +3,24 @@ 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";
export default function HomeView() {
const navigate = useNavigate();
const librariesState = useSnapshot(libraryState);
const gamesState = useSnapshot(gameState);
const recentlyAddedGames = gamesState.sortedByMostRecentlyAdded as GameDto[];
const recentlyAddedGames = gamesState.recentlyAdded as GameDto[];
const gamesByLibrary = gamesState.gamesByLibraryId as Record<number, GameDto[]>;
return (
<div className="w-full">
<div className="flex flex-col gap-2">
<CoverRow title="Recently added" games={recentlyAddedGames}
onPressShowMore={() => alert("show more of 'Recently added'")}/>
onPressShowMore={() => navigate("/recently-added")}/>
{librariesState.libraries.map((library) => (
<CoverRow key={library.id} title={library.name}
games={gamesByLibrary[library.id] || []}
onPressShowMore={() => alert(`show more of library '${library.name}'`)}
onPressShowMore={() => navigate("/library/" + library.id)}
/>
))}
</div>
@@ -0,0 +1,29 @@
import {useSnapshot} from "valtio/react";
import {initializeLibraryState, libraryState} from "Frontend/state/LibraryState";
import {gameState} from "Frontend/state/GameState";
import React, {useEffect} from "react";
import {useNavigate, useParams} from "react-router";
import CoverGrid from "Frontend/components/general/covers/CoverGrid";
import GameDto from "Frontend/generated/de/grimsi/gameyfin/games/dto/GameDto";
export default function LibraryView() {
const {libraryId} = useParams();
const navigate = useNavigate();
const libraries = useSnapshot(libraryState);
const games = (libraryId ? useSnapshot(gameState).gamesByLibraryId[parseInt(libraryId!!)] || [] : []) as GameDto[];
useEffect(() => {
initializeLibraryState().then((state) => {
if (!libraryId || !state.state[parseInt(libraryId)]) {
navigate("/", {replace: true});
}
});
}, [libraryId]);
return (
<div className="flex flex-col gap-6">
<p className="text-4xl font-bold text-center">{libraries.state[parseInt(libraryId!!)]?.name}</p>
<CoverGrid games={games}/>
</div>
);
}
@@ -0,0 +1,16 @@
import {useSnapshot} from "valtio/react";
import {gameState} from "Frontend/state/GameState";
import GameDto from "Frontend/generated/de/grimsi/gameyfin/games/dto/GameDto";
import React from "react";
import CoverGrid from "Frontend/components/general/covers/CoverGrid";
export default function RecentlyAddedView() {
const games = useSnapshot(gameState).recentlyAdded as GameDto[];
return (
<div className="flex flex-col gap-4">
<p className="text-4xl font-bold text-center">Recently added</p>
<CoverGrid games={games}/>
</div>
);
}