@@ -9,7 +9,6 @@ import type {
99 GameArtifact ,
1010 LibraryGame ,
1111 LudusaviBackup ,
12- MemcardRestoreTarget ,
1312 MemoryCardSaveRecord ,
1413} from "@types" ;
1514import { useCallback , useEffect , useMemo , useState } from "react" ;
@@ -18,14 +17,13 @@ import { platformToSystem } from "@renderer/helpers";
1817import {
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" ;
2724import { useUserDetails } from "../../../../hooks/use-user-details.hook" ;
2825import { SettingsSection } from "../../../../pages/settings/settings-section" ;
26+ import { EmulationCloudRestoreModal } from "../../../../pages/settings/emulation/emulation-cloud-restore-modal" ;
2927import { CloudSavesList } from "./cloud-saves-list" ;
3028
3129import "./cloud-tab.scss" ;
@@ -49,17 +47,6 @@ const RESTORE_MODAL_ACTIONS_REGION_ID = "emu-saves-restore-modal-actions";
4947const RESTORE_MODAL_PICK_BUTTON_ID = "emu-saves-restore-pick-button" ;
5048const 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-
6350const 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 }
0 commit comments