From b689d3d75b7581f1661f3fe530ce4840908c63f9 Mon Sep 17 00:00:00 2001 From: evanwporter Date: Mon, 16 Dec 2024 14:03:45 -0800 Subject: [PATCH] Finished save select. "Complement saves" can be added by modifying the `getComplementarySaves` method in G3RRSAV --- src/app/Home.tsx | 23 +++++-- src/saves/SavesModal.tsx | 38 ++++++++++-- src/saves/SelectPlugin.tsx | 79 ++++++++++++++++++++++++ src/types/SAVTypes/load.ts | 8 ++- src/types/SAVTypes/radicalred/G3RRSAV.ts | 4 ++ src/types/SAVTypes/unbound/G3UBSAV.ts | 7 ++- src/types/SAVTypes/util.ts | 2 + 7 files changed, 148 insertions(+), 13 deletions(-) create mode 100644 src/saves/SelectPlugin.tsx diff --git a/src/app/Home.tsx b/src/app/Home.tsx index 38c521df7..bd5fbad86 100644 --- a/src/app/Home.tsx +++ b/src/app/Home.tsx @@ -18,6 +18,8 @@ import { GameOfOrigin, isGameBoy, isGen3, isGen4, isGen5 } from 'pokemon-resourc import { useCallback, useContext, useEffect, useState } from 'react' import { MdFileOpen } from 'react-icons/md' import SaveNotFoundError from 'src/saves/SaveNotFoundError' +import { SelectPlugin } from 'src/saves/SelectPlugin' +import { SAVClass } from 'src/types/SAVTypes/util' import { Errorable } from 'src/types/types' import { BackendContext } from '../backend/backendProvider' import FilterPanel from '../components/filter/FilterPanel' @@ -48,11 +50,11 @@ const Home = () => { const [errorMessages, setErrorMessages] = useState() const [filesToDelete, setFilesToDelete] = useState([]) const [saveFound, setSaveFound] = useState(false); - // const [saveFound, setSaveFound] = useState<{ - // filePath: PathData; - // fileBytes: Uint8Array; - // createdDate: Date; - // } | null>(); + const [specifySave, setSpecifySave] = useState<{ + supportedSaveTypes: SAVClass[]; + plugins: string[]; + onSelect?: (plugin: string) => void; + } | null>(null); const onViewDrop = (e: React.DragEvent, type: string) => { const processDroppedData = async (file?: File, droppedMon?: PKMInterface) => { @@ -384,9 +386,20 @@ const Home = () => { setOpenSaveDialog(false) }} setSaveFound={setSaveFound} + setSpecifySave={setSpecifySave} /> + {specifySave && ( + { + console.log(`Selected plugin: ${selectedPlugin}`); + specifySave.onSelect?.(selectedPlugin); + setSpecifySave(null); + }} + /> + )} {saveFound && ( setSaveFound(false)} diff --git a/src/saves/SavesModal.tsx b/src/saves/SavesModal.tsx index a54bea42c..6b72afffc 100644 --- a/src/saves/SavesModal.tsx +++ b/src/saves/SavesModal.tsx @@ -12,6 +12,7 @@ import * as E from 'fp-ts/lib/Either' import { useCallback, useContext, useState } from 'react' import 'react-data-grid/lib/styles.css' import { PathData } from 'src/types/SAVTypes/path' +import { SAVClass } from 'src/types/SAVTypes/util' import { getMonFileIdentifier } from 'src/util/Lookup' import { BackendContext } from '../backend/backendProvider' import { CardsIcon, GridIcon } from '../components/Icons' @@ -19,19 +20,25 @@ import { AppInfoContext } from '../state/appInfo' import { LookupContext } from '../state/lookup' import { OpenSavesContext } from '../state/openSaves' import { getSaveRef } from '../types/SAVTypes/SAV' -import { buildSaveFile } from '../types/SAVTypes/load' +import { buildSaveFile, getSaveType } from '../types/SAVTypes/load' import RecentSaves from './RecentSaves' import SaveFolders from './SaveFolders' +import { waitForPluginSelection } from './SelectPlugin' import SuggestedSaves from './SuggestedSaves' import { SaveViewMode } from './util' interface SavesModalProps { onClose: () => void, - setSaveFound: React.Dispatch> + setSaveFound: React.Dispatch>, + setSpecifySave: React.Dispatch void; + } | null>> } const SavesModal = (props: SavesModalProps) => { - const { onClose, setSaveFound } = props + const { onClose, setSaveFound, setSpecifySave } = props const backend = useContext(BackendContext) const [, dispatchOpenSaves] = useContext(OpenSavesContext) const [lookupState] = useContext(LookupContext) @@ -54,11 +61,31 @@ const SavesModal = (props: SavesModalProps) => { backend.loadSaveFile(filePath).then( E.match( (err) => console.error(err), - ({ path, fileBytes, createdDate }) => { + async ({ path, fileBytes, createdDate }) => { if (!filePath) { filePath = path } if (filePath && fileBytes && lookupState.loaded) { + let saveType = getSaveType(fileBytes, getEnabledSaveTypes()) + + console.log(saveType) + + const complementaryPlugins = saveType?.getComplementaryPlugins?.() ?? []; + + if (complementaryPlugins.length > 0) { + setSpecifySave({ + supportedSaveTypes: getEnabledSaveTypes(), + plugins: complementaryPlugins, + }); + + // Wait for user selection + saveType = await waitForPluginSelection(setSpecifySave); + if (!saveType) { + console.error("No save type selected."); + return; + } + } + const saveFile = buildSaveFile( filePath, fileBytes, @@ -68,7 +95,8 @@ const SavesModal = (props: SavesModalProps) => { gen345LookupMap: lookupState.gen345, fileCreatedDate: createdDate, }, - getEnabledSaveTypes(), + undefined, // supported saves + saveType, (updatedMon) => { const identifier = getMonFileIdentifier(updatedMon) diff --git a/src/saves/SelectPlugin.tsx b/src/saves/SelectPlugin.tsx new file mode 100644 index 000000000..5177b8cb9 --- /dev/null +++ b/src/saves/SelectPlugin.tsx @@ -0,0 +1,79 @@ +import { + Button, + DialogContent, + DialogTitle, + ModalClose, + ModalDialog, + Stack, + Typography +} from '@mui/joy'; +import { SAVClass } from 'src/types/SAVTypes/util'; + +export function waitForPluginSelection( + setSpecifySave: React.Dispatch void; + } | null>> + ): Promise { + return new Promise((resolve) => { + setSpecifySave((prevState) => { + if (!prevState) { + throw new Error("SpecifySave state is unexpectedly null."); + } + + return { + ...prevState, + onSelect: (selectedPlugin: string) => { + resolve( + prevState.supportedSaveTypes.find( + (item) => item.saveTypeID === selectedPlugin + ) + ); + setSpecifySave(null); // Close the modal after selection + }, + }; + }); + }); +} + + +interface SelectPluginProps { + plugins: string[]; + onPluginClick: (plugin: string) => void; +} + +export const SelectPlugin = ({ + plugins, + onPluginClick, +}: SelectPluginProps) => { + return ( + + + Complementary Plugins + + Select a plugin to proceed: + + {plugins.map((plugin, index) => ( + + ))} + + + + ); +}; \ No newline at end of file diff --git a/src/types/SAVTypes/load.ts b/src/types/SAVTypes/load.ts index 8791dc0e6..0dc2d4a5e 100644 --- a/src/types/SAVTypes/load.ts +++ b/src/types/SAVTypes/load.ts @@ -67,11 +67,15 @@ export const buildSaveFile = ( gen345LookupMap?: Record fileCreatedDate?: Date }, - supportedSaveTypes: SAVClass[], + supportedSaveTypes?: SAVClass[], + saveType?: SAVClass | undefined, updateMonCallback?: (mon: OHPKM) => void ): SAV | undefined => { const { homeMonMap, gen12LookupMap, gen345LookupMap } = lookupMaps - const saveType = getSaveType(fileBytes, supportedSaveTypes) + + if (!saveType && supportedSaveTypes) { + saveType = getSaveType(fileBytes, supportedSaveTypes) + } if (!saveType) return undefined diff --git a/src/types/SAVTypes/radicalred/G3RRSAV.ts b/src/types/SAVTypes/radicalred/G3RRSAV.ts index 407c30b62..8c050af36 100644 --- a/src/types/SAVTypes/radicalred/G3RRSAV.ts +++ b/src/types/SAVTypes/radicalred/G3RRSAV.ts @@ -344,6 +344,10 @@ export class G3RRSAV implements PluginSAV { getPluginIdentifier() { return 'radical_red' } + + static getComplementaryPlugins() { + return [] // ['G3RRSAV', 'G3UBSAV'] + } } const findFirstSectionOffset = (bytes: Uint8Array): number => { diff --git a/src/types/SAVTypes/unbound/G3UBSAV.ts b/src/types/SAVTypes/unbound/G3UBSAV.ts index a7652dec2..d20e83be0 100644 --- a/src/types/SAVTypes/unbound/G3UBSAV.ts +++ b/src/types/SAVTypes/unbound/G3UBSAV.ts @@ -25,11 +25,16 @@ export class G3UBSAV implements PluginSAV { updatedBoxSlots: BoxCoordinates[] = [] gameColor!: () => string isPlugin: true = true - getPluginIdentifier!: (() => string | undefined) & (() => string) getCurrentBox!: () => Box supportsMon!: (dexNumber: number, formeNumber: number) => boolean prepareBoxesAndGetModified!: () => OHPKM[] calculateChecksum?: (() => number) | undefined getGameName!: () => string getExtraData?: (() => object) | undefined + + getPluginIdentifier() { + return 'unbound' + } + + static saveTypeID = 'G3UBSAV' } diff --git a/src/types/SAVTypes/util.ts b/src/types/SAVTypes/util.ts index 9787a4c6d..40c3a71e0 100644 --- a/src/types/SAVTypes/util.ts +++ b/src/types/SAVTypes/util.ts @@ -39,6 +39,8 @@ export interface SAVClass { saveTypeName: string saveTypeID: string saveTypeAbbreviation: string + getComplementaryPlugins?: () => string[] + getPluginIdentifier?: () => string } export type PKMTypeOf = Type extends SAV ? X : never