web: [WIP] Add Save Manager to context menu
This commit is contained in:
parent
7c280fc171
commit
824cc1689c
|
@ -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);
|
||||
|
||||
|
|
|
@ -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">×</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>
|
||||
`;
|
||||
|
|
Loading…
Reference in New Issue