mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-16 16:20:04 +00:00
Add download button to GameView
This commit is contained in:
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,11 +3,31 @@ import GameDto from "Frontend/generated/de/grimsi/gameyfin/games/dto/GameDto";
|
|||||||
import {GameEndpoint} from "Frontend/generated/endpoints";
|
import {GameEndpoint} from "Frontend/generated/endpoints";
|
||||||
import {useParams} from "react-router";
|
import {useParams} from "react-router";
|
||||||
import {GameCover} from "Frontend/components/general/covers/GameCover";
|
import {GameCover} from "Frontend/components/general/covers/GameCover";
|
||||||
|
import ComboButton, {ComboButtonOption} from "Frontend/components/general/input/ComboButton";
|
||||||
|
|
||||||
export default function GameView() {
|
export default function GameView() {
|
||||||
const {gameId} = useParams();
|
const {gameId} = useParams();
|
||||||
|
|
||||||
const [game, setGame] = useState<GameDto>();
|
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(() => {
|
useEffect(() => {
|
||||||
if (gameId) {
|
if (gameId) {
|
||||||
@@ -24,14 +44,17 @@ export default function GameView() {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div className="flex flex-col gap-4 mx-24">
|
<div className="flex flex-col gap-4 mx-24">
|
||||||
<div className="flex flex-row gap-4">
|
<div className="flex flex-row justify-between">
|
||||||
<div className="mt-[-16.25rem]">
|
<div className="flex flex-row gap-4">
|
||||||
<GameCover game={game} size={320} radius="none"/>
|
<div className="mt-[-16.25rem]">
|
||||||
</div>
|
<GameCover game={game} size={320} radius="none"/>
|
||||||
<div className="flex flex-col gap-1">
|
</div>
|
||||||
<p className="font-semibold text-3xl">{game.title}</p>
|
<div className="flex flex-col gap-1">
|
||||||
<p className="text-foreground/60">{game.release !== undefined ? new Date(game.release).getFullYear() : "unknown"}</p>
|
<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>
|
</div>
|
||||||
|
<ComboButton options={downloadOptions} preferredOptionKey="preferred-download-method"/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<p className="text-foreground/60">Summary</p>
|
<p className="text-foreground/60">Summary</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user