From 9f788cf90f697ea5ddcef31a1a2165f09c5f7522 Mon Sep 17 00:00:00 2001 From: Jared Date: Mon, 4 May 2026 15:11:46 -0500 Subject: [PATCH 1/2] fix(library): correct owned-library sort keys Use playtime store for last played and most popular; preserve server order for last added until addedAt exists. --- opennow-stable/src/renderer/src/App.tsx | 48 ++++++++++++++++++------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/opennow-stable/src/renderer/src/App.tsx b/opennow-stable/src/renderer/src/App.tsx index a05fe3fd0..ca123b834 100644 --- a/opennow-stable/src/renderer/src/App.tsx +++ b/opennow-stable/src/renderer/src/App.tsx @@ -422,9 +422,29 @@ function areStringArraysEqual(left: string[], right: string[]): boolean { return left.every((value, index) => value === right[index]); } -function sortLibraryGames(games: GameInfo[], sortId: string): GameInfo[] { +function sortLibraryGames( + games: GameInfo[], + sortId: string, + playtimeData: Record, +): GameInfo[] { const copy = [...games]; const compareTitle = (left: GameInfo, right: GameInfo) => left.title.localeCompare(right.title); + const playtimeLastPlayedMs = (gameId: string): number => { + const raw = playtimeData[gameId]?.lastPlayedAt; + if (!raw) return 0; + const ms = Date.parse(raw); + return Number.isFinite(ms) ? ms : 0; + }; + const legacyLastPlayedMs = (game: GameInfo): number => { + if (!game.lastPlayed) return 0; + const ms = Date.parse(game.lastPlayed); + return Number.isFinite(ms) ? ms : 0; + }; + const mostPopularScore = (gameId: string): number => { + const totalSeconds = Math.max(0, playtimeData[gameId]?.totalSeconds ?? 0); + const sessions = Math.max(0, playtimeData[gameId]?.sessionCount ?? 0); + return (totalSeconds * 1000) + sessions; + }; if (sortId === "z_to_a") { return copy.sort((left, right) => right.title.localeCompare(left.title)); } @@ -433,22 +453,22 @@ function sortLibraryGames(games: GameInfo[], sortId: string): GameInfo[] { } if (sortId === "last_played") { return copy.sort((left, right) => { - const leftTime = left.lastPlayed ? new Date(left.lastPlayed).getTime() : 0; - const rightTime = right.lastPlayed ? new Date(right.lastPlayed).getTime() : 0; + const leftTime = playtimeLastPlayedMs(left.id) || legacyLastPlayedMs(left); + const rightTime = playtimeLastPlayedMs(right.id) || legacyLastPlayedMs(right); if (leftTime === rightTime) return compareTitle(left, right); return rightTime - leftTime; }); } if (sortId === "last_added") { - return copy.sort((left, right) => { - const leftTime = left.isInLibrary ? new Date(left.lastPlayed ?? 0).getTime() : 0; - const rightTime = right.isInLibrary ? new Date(right.lastPlayed ?? 0).getTime() : 0; - if (leftTime === rightTime) return compareTitle(left, right); - return rightTime - leftTime; - }); + // Preserve server-provided order. We do not currently have a trustworthy local "addedAt" field. + return copy; } if (sortId === "most_popular") { - return copy.sort((left, right) => (right.membershipTierLabel ? 1 : 0) - (left.membershipTierLabel ? 1 : 0) || compareTitle(left, right)); + return copy.sort((left, right) => { + const d = mostPopularScore(right.id) - mostPopularScore(left.id); + if (d !== 0) return d; + return compareTitle(left, right); + }); } return copy.sort(compareTitle); } @@ -4393,8 +4413,12 @@ export function App(): JSX.Element { const filteredLibraryGames = useMemo(() => { const query = searchQuery.trim(); const searched = query ? libraryGames.filter((game) => matchesGameSearch(game, query)) : libraryGames; - return sortLibraryGames(searched, catalogSelectedSortId === "relevance" ? "last_played" : catalogSelectedSortId); - }, [libraryGames, searchQuery, catalogSelectedSortId]); + return sortLibraryGames( + searched, + catalogSelectedSortId === "relevance" ? "last_played" : catalogSelectedSortId, + playtime, + ); + }, [libraryGames, searchQuery, catalogSelectedSortId, playtime]); const activeSessionGameTitle = useMemo(() => { if (!navbarActiveSession) return null; From 83401e3fca03b09f04459fcd674e2c7098a47f88 Mon Sep 17 00:00:00 2001 From: Jared Date: Tue, 5 May 2026 03:55:19 -0500 Subject: [PATCH 2/2] fix(library): improve sorting logic for owned library by playtime --- opennow-stable/src/renderer/src/App.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/opennow-stable/src/renderer/src/App.tsx b/opennow-stable/src/renderer/src/App.tsx index ca123b834..2b79de778 100644 --- a/opennow-stable/src/renderer/src/App.tsx +++ b/opennow-stable/src/renderer/src/App.tsx @@ -440,11 +440,6 @@ function sortLibraryGames( const ms = Date.parse(game.lastPlayed); return Number.isFinite(ms) ? ms : 0; }; - const mostPopularScore = (gameId: string): number => { - const totalSeconds = Math.max(0, playtimeData[gameId]?.totalSeconds ?? 0); - const sessions = Math.max(0, playtimeData[gameId]?.sessionCount ?? 0); - return (totalSeconds * 1000) + sessions; - }; if (sortId === "z_to_a") { return copy.sort((left, right) => right.title.localeCompare(left.title)); } @@ -465,8 +460,12 @@ function sortLibraryGames( } if (sortId === "most_popular") { return copy.sort((left, right) => { - const d = mostPopularScore(right.id) - mostPopularScore(left.id); - if (d !== 0) return d; + const leftSeconds = Math.max(0, playtimeData[left.id]?.totalSeconds ?? 0); + const rightSeconds = Math.max(0, playtimeData[right.id]?.totalSeconds ?? 0); + if (leftSeconds !== rightSeconds) return rightSeconds - leftSeconds; + const leftSessions = Math.max(0, playtimeData[left.id]?.sessionCount ?? 0); + const rightSessions = Math.max(0, playtimeData[right.id]?.sessionCount ?? 0); + if (leftSessions !== rightSessions) return rightSessions - leftSessions; return compareTitle(left, right); }); }