Add download button to GameView

This commit is contained in:
grimsi
2025-05-12 17:22:21 +02:00
parent 4ad1b8525a
commit 7725179fe3
2 changed files with 117 additions and 7 deletions
@@ -0,0 +1,87 @@
import {useEffect, useState} from "react";
import {
Button,
ButtonGroup,
Dropdown,
DropdownItem,
DropdownMenu,
DropdownTrigger,
SharedSelection
} from "@heroui/react";
import {CaretDown} from "@phosphor-icons/react";
import {UserPreferenceService} from "Frontend/util/user-preference-service";
export interface ComboButtonOption {
label: string;
description: string;
action: () => void;
isDisabled?: boolean;
}
export interface ComboButtonProps {
options: Record<string, ComboButtonOption>;
preferredOptionKey?: string;
}
export default function ComboButton({options, preferredOptionKey}: ComboButtonProps) {
const [selectedOption, setSelectedOption] = useState(new Set([Object.keys(options)[0]]));
const [disabledOptions] = useState<string[]>(getDisabledKeys(options));
const selectedOptionValue = Array.from(selectedOption)[0];
useEffect(() => {
if (!preferredOptionKey) return;
UserPreferenceService.get(preferredOptionKey).then((key) => {
if (key && options[key]) {
setSelectedOption(new Set([key]));
} else {
setSelectedOption(new Set([Object.keys(options)[0]]));
}
})
}, []);
async function onSelectionChange(keys: SharedSelection) {
if (!keys.currentKey) return;
if (preferredOptionKey) {
await UserPreferenceService.set(preferredOptionKey, keys.currentKey);
}
setSelectedOption(new Set([keys.currentKey]));
}
function getDisabledKeys(options: Record<string, ComboButtonOption>): string[] {
return Object.keys(options).filter(key => options[key].isDisabled);
}
return (
<ButtonGroup className="gap-[1px]">
<Button color="primary" className="font-semibold w-52"
onPress={options[selectedOptionValue].action}>{options[selectedOptionValue].label}
</Button>
<Dropdown placement="bottom-end">
<DropdownTrigger>
<Button isIconOnly color="primary">
<CaretDown/>
</Button>
</DropdownTrigger>
<DropdownMenu
disallowEmptySelection
aria-label="Merge options"
selectedKeys={selectedOption}
disabledKeys={disabledOptions}
selectionMode="single"
/*@ts-ignore*/
onSelectionChange={onSelectionChange}
className="w-60"
>
{Object.entries(options).map(([key, option]) => (
<DropdownItem key={key} description={option.description}>
{option.label}
</DropdownItem>
))}
</DropdownMenu>
</Dropdown>
</ButtonGroup>
);
}
+30 -7
View File
@@ -3,11 +3,31 @@ import GameDto from "Frontend/generated/de/grimsi/gameyfin/games/dto/GameDto";
import {GameEndpoint} from "Frontend/generated/endpoints";
import {useParams} from "react-router";
import {GameCover} from "Frontend/components/general/covers/GameCover";
import ComboButton, {ComboButtonOption} from "Frontend/components/general/input/ComboButton";
export default function GameView() {
const {gameId} = useParams();
const [game, setGame] = useState<GameDto>();
const [selectedDownloadOption, setSelectedDownloadOption] = useState<string | null>(null);
const downloadOptions: Record<string, ComboButtonOption> = {
browser: {
label: "Direct Download",
description: "Download the game in this browser",
action: () => {
alert("Direct download not yet implemented")
}
},
torrent: {
label: "Torrent Download",
description: "Download the game as a torrent",
action: () => {
alert("Torrent download not yet implemented")
},
isDisabled: true
}
}
useEffect(() => {
if (gameId) {
@@ -24,14 +44,17 @@ export default function GameView() {
</div>
}
<div className="flex flex-col gap-4 mx-24">
<div className="flex flex-row gap-4">
<div className="mt-[-16.25rem]">
<GameCover game={game} size={320} radius="none"/>
</div>
<div className="flex flex-col gap-1">
<p className="font-semibold text-3xl">{game.title}</p>
<p className="text-foreground/60">{game.release !== undefined ? new Date(game.release).getFullYear() : "unknown"}</p>
<div className="flex flex-row justify-between">
<div className="flex flex-row gap-4">
<div className="mt-[-16.25rem]">
<GameCover game={game} size={320} radius="none"/>
</div>
<div className="flex flex-col gap-1">
<p className="font-semibold text-3xl">{game.title}</p>
<p className="text-foreground/60">{game.release !== undefined ? new Date(game.release).getFullYear() : "unknown"}</p>
</div>
</div>
<ComboButton options={downloadOptions} preferredOptionKey="preferred-download-method"/>
</div>
<div className="flex flex-col gap-2">
<p className="text-foreground/60">Summary</p>