web: [WIP] Add Save Manager to context menu

This commit is contained in:
Daniel Jacobs 2023-03-10 17:09:12 -05:00 committed by Nathan Adams
parent 7c280fc171
commit 824cc1689c
2 changed files with 136 additions and 0 deletions

View File

@ -122,6 +122,7 @@ export class RufflePlayer extends HTMLElement {
private readonly unmuteOverlay: HTMLElement;
private readonly splashScreen: HTMLElement;
private readonly virtualKeyboard: HTMLInputElement;
private readonly saveManager: HTMLDialogElement;
// Firefox has a read-only "contextMenu" property,
// so avoid shadowing it.
@ -228,6 +229,27 @@ export class RufflePlayer extends HTMLElement {
"input",
this.virtualKeyboardInput.bind(this)
);
this.saveManager = <HTMLDialogElement>(
this.shadow.getElementById("save-manager")!
);
const closeSaveManager = this.saveManager.querySelector("#close-modal");
if (closeSaveManager) {
closeSaveManager.addEventListener("click", () =>
this.saveManager.close()
);
}
const backupSaves = this.saveManager.querySelector("#backup-saves");
if (backupSaves) {
backupSaves.addEventListener("click", this.backupSaves.bind(this));
}
const restoreSave = this.saveManager.querySelector("#restore-save");
if (restoreSave) {
restoreSave.addEventListener("change", this.restoreSave.bind(this));
}
const deleteSave = this.saveManager.querySelector("#delete-save");
if (deleteSave) {
deleteSave.addEventListener("click", this.deleteSave.bind(this));
}
this.contextMenuElement = this.shadow.getElementById("context-menu")!;
window.addEventListener("pointerdown", this.pointerDown.bind(this));
@ -817,6 +839,90 @@ export class RufflePlayer extends HTMLElement {
}
}
/**
* Download base-64 string as file
*
* @param bytesBase64 The base-64 encoded SOL string
* @param mimeType The MIME type
* @param fileName The name to give the file
*/
saveFile(bytesBase64: string, mimeType: string, fileName: string): void {
const fileUrl = "data:" + mimeType + ";base64," + bytesBase64;
fetch(fileUrl)
.then((response) => response.blob())
.then((blob) => {
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.style.display = "none";
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
});
}
/**
* @returns If the string represent a base-64 encoded SOL file
* Check if string is a base-64 encoded SOL file
* @param solData The base-64 encoded SOL string
*/
isB64SOL(solData: string): boolean {
try {
const decodedData = atob(solData);
return decodedData.slice(6, 10) === "TCSO";
} catch (e) {
return false;
}
}
/**
* Restore save from SOL file.
*
* @param event The change event fired
*/
async restoreSave(event: Event): Promise<void> {
const fileInput = <HTMLInputElement>(event.target);
if (
fileInput &&
fileInput.files &&
fileInput.files.length > 0 &&
fileInput.files[0]
) {
const solData = await fileInput.files[0].text();
console.log(solData);
// Encoding this fails
//const encodedData = btoa(solData);
}
this.saveManager.close();
}
/**
* Delete local saves.
*/
deleteSave(): void {
console.log("TESTING DELETE");
this.saveManager.close();
}
/**
* Gets the local save information as SOL files and downloads them.
*/
backupSaves(): void {
Object.keys(localStorage).forEach((key) => {
const solName = key.split("/").pop();
const solData = localStorage.getItem(key);
if (solData && this.isB64SOL(solData)) {
this.saveFile(
solData,
"application/octet-stream",
solName + ".sol"
);
}
});
this.saveManager.close();
}
/**
* Fetches the loaded SWF and downloads it.
*/
@ -939,6 +1045,10 @@ export class RufflePlayer extends HTMLElement {
navigator.clipboard.writeText(this.getPanicData()),
});
}
items.push({
text: "Open Save Manager",
onClick: () => this.saveManager.showModal(),
});
items.push(null);

View File

@ -282,6 +282,23 @@ ruffleShadowTemplate.innerHTML = `
width: 1px;
height: 1px;
}
#restore-save {
display: none;
}
.save-option {
padding: 3px 10px;
margin: 5px 2px;
cursor: pointer;
border-radius: 50px;
background-color: var(--ruffle-blue);
color: white;
}
#close-modal {
position: absolute;
top: 5px;
right: 5px;
cursor: pointer;
}
</style>
<style id="dynamic_styles"></style>
@ -298,5 +315,14 @@ ruffleShadowTemplate.innerHTML = `
<div class="loadbar"><div class="loadbar-inner"></div></div>
</div>
<dialog id="save-manager">
<span id="close-modal">&times;</span>
<span class="save-option" id="backup-saves">Backup saves (download sols)</span>
<p id="restore-save-paragraph">
<input type="file" accept=".sol" id="restore-save">
<label class="save-option" for="restore-save" id="restore-save-label">Restore save (upload sol) and reload page</label>
</p>
<span class="save-option" id="delete-save">Delete local save and reload page</span>
</dialog>
<ul id="context-menu"></ul>
`;