Skip to content

Commit fab50da

Browse files
authored
Merge pull request #2376 from hydralauncher/fix/add-big-picture-missing-locales
Fix/add big picture missing locales and nav overrides
2 parents 5fb3d4b + b6c6a91 commit fab50da

15 files changed

Lines changed: 371 additions & 334 deletions

File tree

src/big-picture/src/components/pages/game/game-settings-modal/cloud-tab.scss

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858

5959
.game-cloud-settings-tab__saves-shell {
6060
display: flex;
61+
flex-direction: column;
6162
flex: 1;
6263
min-height: 0;
6364
min-width: 0;
@@ -71,14 +72,16 @@
7172
gap: calc(var(--spacing-unit) * 4);
7273
width: 100%;
7374
min-width: 0;
75+
max-height: 320px;
76+
overflow-y: auto;
7477
}
7578

7679
.game-cloud-settings-tab__saves-viewport {
80+
display: flex;
81+
flex-direction: column;
7782
flex: 1;
7883
min-height: 0;
79-
overflow-x: hidden;
80-
overflow-y: auto;
81-
scrollbar-width: none;
84+
overflow: hidden;
8285

8386
&::-webkit-scrollbar {
8487
display: none;
@@ -170,7 +173,7 @@
170173
&__targets {
171174
list-style: none;
172175
margin: 0;
173-
padding: 0;
176+
padding: 4px;
174177
display: flex;
175178
flex-direction: column;
176179
gap: 8px;

src/big-picture/src/components/pages/game/game-settings-modal/cloud-tab.tsx

Lines changed: 14 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import type {
99
GameArtifact,
1010
LibraryGame,
1111
LudusaviBackup,
12-
MemcardRestoreTarget,
1312
MemoryCardSaveRecord,
1413
} from "@types";
1514
import { useCallback, useEffect, useMemo, useState } from "react";
@@ -18,14 +17,13 @@ import { platformToSystem } from "@renderer/helpers";
1817
import {
1918
Button,
2019
Checkbox,
21-
FocusItem,
2220
HorizontalFocusGroup,
23-
Modal,
2421
VerticalFocusGroup,
2522
} from "../../../common";
26-
import { useBigPictureToast, useNavigation } from "../../../../hooks";
23+
import { useBigPictureToast } from "../../../../hooks";
2724
import { useUserDetails } from "../../../../hooks/use-user-details.hook";
2825
import { SettingsSection } from "../../../../pages/settings/settings-section";
26+
import { EmulationCloudRestoreModal } from "../../../../pages/settings/emulation/emulation-cloud-restore-modal";
2927
import { CloudSavesList } from "./cloud-saves-list";
3028

3129
import "./cloud-tab.scss";
@@ -49,17 +47,6 @@ const RESTORE_MODAL_ACTIONS_REGION_ID = "emu-saves-restore-modal-actions";
4947
const RESTORE_MODAL_PICK_BUTTON_ID = "emu-saves-restore-pick-button";
5048
const RESTORE_MODAL_CONFIRM_BUTTON_ID = "emu-saves-restore-confirm";
5149

52-
const PICK_FILTERS: Record<
53-
EmulationSavePlatform,
54-
{ name: string; extensions: string[] }
55-
> = {
56-
ps1: {
57-
name: "PS1 Memory Card",
58-
extensions: ["mcd", "mcr", "mc", "gme", "vgs", "vmp"],
59-
},
60-
ps2: { name: "PS2 Memory Card", extensions: ["ps2", "mcd", "mc2"] },
61-
};
62-
6350
const recordKey = (record: MemoryCardSaveRecord): string =>
6451
`${record.cardFilePath}::${record.folderName}`;
6552

@@ -88,143 +75,20 @@ function EmulationRestoreModal({
8875
onClose: () => void;
8976
onRestored: () => void;
9077
}>) {
91-
const { t } = useTranslation("settings");
92-
const { setFocus } = useNavigation();
9378
const { showErrorToast, showSuccessToast } = useBigPictureToast();
94-
const [targets, setTargets] = useState<MemcardRestoreTarget[]>([]);
95-
const [selectedTarget, setSelectedTarget] = useState<string | null>(null);
96-
const [isBusy, setIsBusy] = useState(false);
97-
98-
useEffect(() => {
99-
if (!save) return;
100-
void globalThis.window.electron
101-
.getMemcardRestoreTargets(platform)
102-
.then((foundTargets) => {
103-
setTargets(foundTargets);
104-
setSelectedTarget(foundTargets[0]?.cardFilePath ?? null);
105-
});
106-
}, [platform, save]);
107-
108-
useEffect(() => {
109-
if (!save) return;
110-
const frameId = globalThis.window.requestAnimationFrame(() => {
111-
setFocus(
112-
selectedTarget
113-
? `emu-saves-target-${selectedTarget}`
114-
: RESTORE_MODAL_PICK_BUTTON_ID
115-
);
116-
});
117-
return () => globalThis.window.cancelAnimationFrame(frameId);
118-
}, [save, selectedTarget, setFocus]);
119-
120-
const handlePickFile = useCallback(async () => {
121-
const result = await globalThis.window.electron.showOpenDialog({
122-
properties: ["openFile"],
123-
filters: [PICK_FILTERS[platform]],
124-
});
125-
if (result.canceled || result.filePaths.length === 0) return;
126-
const chosenPath = result.filePaths[0];
127-
setTargets((current) =>
128-
current.some((t) => t.cardFilePath === chosenPath)
129-
? current
130-
: [
131-
...current,
132-
{
133-
cardFilePath: chosenPath,
134-
cardLabel: chosenPath.split("/").pop() ?? chosenPath,
135-
},
136-
]
137-
);
138-
setSelectedTarget(chosenPath);
139-
}, [platform]);
140-
141-
const handleRestore = useCallback(async () => {
142-
if (!save || !selectedTarget) return;
143-
setIsBusy(true);
144-
try {
145-
const result = await globalThis.window.electron.restoreEmulationSave(
146-
platform,
147-
save.id,
148-
selectedTarget
149-
);
150-
if (result.ok) {
151-
showSuccessToast("Cloud save restored");
152-
onRestored();
153-
onClose();
154-
} else {
155-
showErrorToast("Failed to restore cloud save");
156-
}
157-
} finally {
158-
setIsBusy(false);
159-
}
160-
}, [
161-
onClose,
162-
onRestored,
163-
platform,
164-
save,
165-
selectedTarget,
166-
showErrorToast,
167-
showSuccessToast,
168-
]);
169-
17079
return (
171-
<Modal
172-
visible={save !== null}
173-
title={t("cloud_restore_title")}
174-
description={t("cloud_restore_description")}
80+
<EmulationCloudRestoreModal
81+
save={save}
82+
platform={platform}
17583
onClose={onClose}
176-
>
177-
<VerticalFocusGroup regionId={RESTORE_MODAL_REGION_ID}>
178-
<div className="emu-save-modal__targets">
179-
{targets.length === 0 ? (
180-
<div className="emu-save-modal__empty">
181-
{t("cloud_restore_no_cards")}
182-
</div>
183-
) : (
184-
targets.map((target) => {
185-
const targetId = `emu-saves-target-${target.cardFilePath}`;
186-
const isSelected = selectedTarget === target.cardFilePath;
187-
return (
188-
<FocusItem key={target.cardFilePath} id={targetId} asChild>
189-
<button
190-
type="button"
191-
className={`emu-save-modal__target${
192-
isSelected ? " emu-save-modal__target--selected" : ""
193-
}`}
194-
onClick={() => setSelectedTarget(target.cardFilePath)}
195-
>
196-
<span className="emu-save-modal__target-name">
197-
{target.cardLabel}
198-
</span>
199-
<span className="emu-save-modal__target-path">
200-
{target.cardFilePath}
201-
</span>
202-
</button>
203-
</FocusItem>
204-
);
205-
})
206-
)}
207-
</div>
208-
<HorizontalFocusGroup regionId={RESTORE_MODAL_ACTIONS_REGION_ID}>
209-
<Button
210-
focusId={RESTORE_MODAL_PICK_BUTTON_ID}
211-
variant="secondary"
212-
disabled={isBusy}
213-
onClick={handlePickFile}
214-
>
215-
{t("cloud_restore_pick_file")}
216-
</Button>
217-
<Button
218-
focusId={RESTORE_MODAL_CONFIRM_BUTTON_ID}
219-
loading={isBusy}
220-
disabled={!selectedTarget}
221-
onClick={handleRestore}
222-
>
223-
{t("cloud_restore_confirm")}
224-
</Button>
225-
</HorizontalFocusGroup>
226-
</VerticalFocusGroup>
227-
</Modal>
84+
onRestored={onRestored}
85+
onRestoreSuccess={() => showSuccessToast("Cloud save restored")}
86+
onRestoreError={() => showErrorToast("Failed to restore cloud save")}
87+
regionId={RESTORE_MODAL_REGION_ID}
88+
actionsRegionId={RESTORE_MODAL_ACTIONS_REGION_ID}
89+
pickButtonId={RESTORE_MODAL_PICK_BUTTON_ID}
90+
confirmButtonId={RESTORE_MODAL_CONFIRM_BUTTON_ID}
91+
/>
22892
);
22993
}
23094

@@ -627,7 +491,7 @@ export function GameCloudSettingsTab({
627491
<HorizontalFocusGroup asChild>
628492
<div className="game-cloud-settings-tab__save-actions">
629493
<Button
630-
focusId={GAME_CLOUD_SETTINGS_PRIMARY_CONTROL_ID}
494+
focusId={`${GAME_CLOUD_SETTINGS_PRIMARY_CONTROL_ID}-${key}`}
631495
variant="secondary"
632496
className="game-cloud-settings-tab__save-restore-button"
633497
loading={uploading}

src/big-picture/src/components/pages/library/game-context-menu-items.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ export interface LibraryGameContextMenuHandlers {
2727
export function buildLibraryGameContextMenuItems(
2828
game: LibraryGame,
2929
handlers: LibraryGameContextMenuHandlers,
30-
isFavoriteLoading: boolean
30+
isFavoriteLoading: boolean,
31+
t?: (key: string) => string
3132
): ContextMenuItem[] {
3233
const {
3334
onLaunchOrDownload,
@@ -82,7 +83,7 @@ export function buildLibraryGameContextMenuItems(
8283
if (onOptions) {
8384
nextItems.push({
8485
id: "options",
85-
label: "Game Options",
86+
label: t ? t("game_options") : "Game Options",
8687
icon: <GearIcon size={18} />,
8788
onSelect: () => onOptions(game),
8889
});

src/big-picture/src/components/pages/library/game-context-menu.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { LibraryGame } from "@types";
22
import { useMemo } from "react";
3+
import { useTranslation } from "react-i18next";
34

45
import { ContextMenu } from "../../common";
56
import { buildLibraryGameContextMenuItems } from "./game-context-menu-items";
@@ -35,6 +36,7 @@ export function LibraryGameContextMenu({
3536
onUninstall,
3637
onRemoveFromLibrary,
3738
}: Readonly<LibraryGameContextMenuProps>) {
39+
const { t } = useTranslation("big_picture");
3840
const items = useMemo(() => {
3941
if (!game) return [];
4042

@@ -49,7 +51,8 @@ export function LibraryGameContextMenu({
4951
onUninstall,
5052
onRemoveFromLibrary,
5153
},
52-
isFavoriteLoading
54+
isFavoriteLoading,
55+
t
5356
);
5457
}, [
5558
game,
@@ -61,6 +64,7 @@ export function LibraryGameContextMenu({
6164
onToggleFavorite,
6265
onUninstall,
6366
onViewAchievements,
67+
t,
6468
]);
6569

6670
if (!game) {

src/big-picture/src/locales/en/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,7 @@
701701
"backup_info_unknown_device": "Unknown device",
702702
"freeze_backup": "Freeze backup",
703703
"unfreeze_backup": "Unfreeze backup",
704+
"game_options": "Game Options",
704705
"delete_backup": "Delete backup",
705706
"cloud_backup_options": "Backup options",
706707
"uploading_backup": "Uploading backup…",

src/big-picture/src/locales/es/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,7 @@
701701
"backup_info_unknown_device": "Dispositivo desconocido",
702702
"freeze_backup": "Fijar copia",
703703
"unfreeze_backup": "Desfijar copia",
704+
"game_options": "Opciones del juego",
704705
"delete_backup": "Eliminar copia",
705706
"cloud_backup_options": "Opciones de la copia",
706707
"cloud_delete_backup_description": "¿Eliminar de tus copias en la nube?"

src/big-picture/src/locales/fr/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,7 @@
693693
"backup_info_unknown_device": "Appareil inconnu",
694694
"freeze_backup": "Épingler la sauvegarde",
695695
"unfreeze_backup": "Désépingler la sauvegarde",
696+
"game_options": "Options du jeu",
696697
"delete_backup": "Supprimer la sauvegarde",
697698
"cloud_backup_options": "Options de sauvegarde",
698699
"cloud_delete_backup_description": "Supprimer de vos sauvegardes cloud ?"

src/big-picture/src/locales/pt-BR/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,7 @@
701701
"backup_info_unknown_device": "Dispositivo desconhecido",
702702
"freeze_backup": "Fixar backup",
703703
"unfreeze_backup": "Desafixar backup",
704+
"game_options": "Opções do jogo",
704705
"delete_backup": "Excluir backup",
705706
"cloud_backup_options": "Opções do backup",
706707
"cloud_delete_backup_description": "Excluir dos seus backups na nuvem?",

src/big-picture/src/locales/ru/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,7 @@
701701
"backup_info_unknown_device": "Неизвестное устройство",
702702
"freeze_backup": "Закрепить копию",
703703
"unfreeze_backup": "Открепить копию",
704+
"game_options": "Настройки игры",
704705
"delete_backup": "Удалить копию",
705706
"cloud_backup_options": "Параметры копии",
706707
"cloud_delete_backup_description": "Удалить из ваших облачных резервных копий?"

src/big-picture/src/pages/home/home.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { LibraryGame, ShopAssets } from "@types";
22
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
33
import type { MouseEventHandler } from "react";
44
import { useNavigate } from "react-router-dom";
5+
import { useTranslation } from "react-i18next";
56
import { logger } from "@renderer/logger";
67

78
import { HomePageHero } from "./hero";
@@ -83,6 +84,7 @@ export default function Home() {
8384
const navigate = useNavigate();
8485
const { setFocus } = useNavigation();
8586
const { showSuccessToast } = useBigPictureToast();
87+
const { t } = useTranslation("big_picture");
8688
const { library, updateLibrary } = useLibrary();
8789
const { loadCollections } = useGameCollections();
8890

@@ -302,7 +304,8 @@ export default function Home() {
302304
onUninstall: handleRequestRemoveFilesFromMenu,
303305
onRemoveFromLibrary: handleRequestRemoveFromLibraryFromMenu,
304306
},
305-
favoriteLoadingGameId === game.id
307+
favoriteLoadingGameId === game.id,
308+
t
306309
);
307310
}
308311

@@ -320,6 +323,7 @@ export default function Home() {
320323
}, [
321324
addingCatalogKey,
322325
favoriteLoadingGameId,
326+
t,
323327
handleAddCatalogGameToLibrary,
324328
handleOpenCatalogDownloadOptions,
325329
handleCatalogShareFromMenu,

0 commit comments

Comments
 (0)