feat: sidebar badge on new game download option

This commit is contained in:
Moyasee
2025-10-30 23:21:31 +02:00
parent 1bd88e6c6e
commit 101bc35460
16 changed files with 269 additions and 1 deletions

View File

@@ -10,6 +10,7 @@ import {
useToast,
useUserDetails,
} from "@renderer/hooks";
import { useDownloadOptionsListener } from "@renderer/hooks/use-download-options-listener";
import { Outlet, useLocation, useNavigate } from "react-router-dom";
import {
@@ -36,6 +37,9 @@ export function App() {
const contentRef = useRef<HTMLDivElement>(null);
const { updateLibrary, library } = useLibrary();
// Listen for new download options updates
useDownloadOptionsListener();
const { t } = useTranslation("app");
const { clearDownload, setLastPacket } = useDownload();

View File

@@ -80,6 +80,13 @@ export function SidebarGameItem({
<span className="sidebar__menu-item-button-label">
{getGameTitle(game)}
</span>
{game.newDownloadOptionsCount && game.newDownloadOptionsCount > 0 && (
<span className="sidebar__game-badge">
<div className="sidebar__game-badge-plus">+</div>
<div className="sidebar__game-badge-count">{game.newDownloadOptionsCount}</div>
</span>
)}
</button>
</li>

View File

@@ -115,6 +115,26 @@
background-size: cover;
}
&__game-badge {
background: rgba(255, 255, 255, 0.1);;
color: #fff;
font-size: 10px;
font-weight: bold;
padding: 4px 6px;
border-radius: 6px;
display: flex;
margin-left: auto;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
gap: calc(globals.$spacing-unit * 0.35);
}
&__game-badge-plus,
&__game-badge-count {
display: flex;
align-items: center;
justify-content: center;
}
&__section-header {
display: flex;
justify-content: space-between;

View File

@@ -414,6 +414,11 @@ declare global {
openEditorWindow: (themeId: string) => Promise<void>;
onCustomThemeUpdated: (cb: () => void) => () => Electron.IpcRenderer;
closeEditorWindow: (themeId?: string) => Promise<void>;
/* Download Options */
onNewDownloadOptions: (
cb: (gamesWithNewOptions: { gameId: string; count: number }[]) => void
) => () => Electron.IpcRenderer;
}
interface Window {

View File

@@ -18,7 +18,25 @@ export const librarySlice = createSlice({
setLibrary: (state, action: PayloadAction<LibraryState["value"]>) => {
state.value = action.payload;
},
updateGameNewDownloadOptions: (
state,
action: PayloadAction<{ gameId: string; count: number }>
) => {
const game = state.value.find((g) => g.id === action.payload.gameId);
if (game) {
game.newDownloadOptionsCount = action.payload.count;
}
},
clearNewDownloadOptions: (
state,
action: PayloadAction<{ gameId: string }>
) => {
const game = state.value.find((g) => g.id === action.payload.gameId);
if (game) {
game.newDownloadOptionsCount = undefined;
}
},
},
});
export const { setLibrary } = librarySlice.actions;
export const { setLibrary, updateGameNewDownloadOptions, clearNewDownloadOptions } = librarySlice.actions;

View File

@@ -6,3 +6,4 @@ export * from "./redux";
export * from "./use-user-details";
export * from "./use-format";
export * from "./use-feature";
export * from "./use-download-options-listener";

View File

@@ -0,0 +1,19 @@
import { useEffect } from "react";
import { useAppDispatch } from "./redux";
import { updateGameNewDownloadOptions } from "@renderer/features";
export function useDownloadOptionsListener() {
const dispatch = useAppDispatch();
useEffect(() => {
const unsubscribe = window.electron.onNewDownloadOptions(
(gamesWithNewOptions) => {
gamesWithNewOptions.forEach(({ gameId, count }) => {
dispatch(updateGameNewDownloadOptions({ gameId, count }));
});
}
);
return unsubscribe;
}, [dispatch]);
}