diff --git a/package.json b/package.json
index 0cd43776..69fd0eb8 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,7 @@
"@typescript-eslint/parser": "^6.20.0",
"autoprefixer": "^10.4.17",
"daisyui": "^3.9.3",
- "emulators": "8.1.9",
+ "emulators": "8.2.0",
"eslint": "^8.56.0",
"eslint-config-google": "^0.14.0",
"postcss": "^8.4.33",
diff --git a/src/frame/frame.css b/src/frame/frame.css
index 90b6f436..aa42fde4 100644
--- a/src/frame/frame.css
+++ b/src/frame/frame.css
@@ -102,11 +102,4 @@
}
}
}
-
- .quick-save-frame {
- .text-badge {
- @apply absolute left-0 top-0 w-3 h-3 font-bold rounded-full flex items-center justify-center;
- font-size: 0.5rem;
- }
- }
}
\ No newline at end of file
diff --git a/src/frame/frame.tsx b/src/frame/frame.tsx
index be122642..7e260ff6 100644
--- a/src/frame/frame.tsx
+++ b/src/frame/frame.tsx
@@ -5,7 +5,6 @@ import { EditorConf } from "./editor/editor-conf-frame";
import { EditorFsFrame } from "./editor/editor-fs-frame";
import { FatDrivesFrame } from "./fat-drives-frame";
import { NetworkFrame } from "./network-frame";
-import { QuickSaveFrame } from "./quick-save-frame";
import { SettingsFrame } from "./settings-frame";
import { StatsFrame } from "./stats-frame";
import { PreRunFrame } from "./prerun-frame";
@@ -28,7 +27,6 @@ export function Frame(props: {}) {
{ frame === "network" && }
{ frame === "stats" && }
{ frame === "fat-drives" && }
- { frame === "quick-save" && }
{ frame === "prerun" && }
;
};
diff --git a/src/frame/quick-save-frame.tsx b/src/frame/quick-save-frame.tsx
deleted file mode 100644
index 008ea6d0..00000000
--- a/src/frame/quick-save-frame.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { useDispatch, useSelector } from "react-redux";
-import { LockBadge } from "../components/lock";
-import { CloudSaveButton } from "../sidebar/cloud-save-button";
-import { DisketteIcon } from "../sidebar/diskette-icon";
-import { State, useNonSerializableStore } from "../store";
-import { dispatchLoginAction } from "../store/ui";
-
-export function QuickSaveFrame() {
- const premium = useSelector((state: State) => state.auth.account)?.premium && false;
- return
-
-
-
- {premium && <>
-
-
-
-
- >}
-
;
-}
-
-function SaveButton(props: {
- label: number | string,
- bgcolor: string,
- textcolor: string,
- disabled?: boolean,
-}) {
- const account = useSelector((state: State) => state.auth.account);
- const dispatch = useDispatch();
- const nonSerializableStore = useNonSerializableStore();
- function onClick() {
- if (props.disabled === true) {
- dispatchLoginAction(account, dispatch);
- }
-
- const ci = nonSerializableStore.ci;
- if (ci === null) {
- return;
- }
-
- ci.sendBackendEvent({
- type: "wc-trigger-event",
- event: "hand_savestate",
- });
- }
-
- return ;
-}
-
-function LoadButton(props: {
- label: number | string,
- bgcolor: string,
- disabled?: boolean,
-}) {
- const account = useSelector((state: State) => state.auth.account);
- const dispatch = useDispatch();
- const nonSerializableStore = useNonSerializableStore();
- function onClick() {
- if (props.disabled === true) {
- dispatchLoginAction(account, dispatch);
- }
-
- const ci = nonSerializableStore.ci;
- if (ci === null) {
- return;
- }
-
- ci.sendBackendEvent({
- type: "wc-trigger-event",
- event: "hand_loadstate",
- });
- }
-
- return ;
-}
diff --git a/src/i18n.ts b/src/i18n.ts
index 4a3befe4..7bbe0981 100644
--- a/src/i18n.ts
+++ b/src/i18n.ts
@@ -64,6 +64,8 @@ const translations: {[lang: string]: {[key: string]: string} } = {
account_not_ready: "Пропустить загрузку сохранений",
loading_saves: "Загрузка сохранений",
success: "Успешно",
+ success_save: "Сохранено",
+ warn_save: "Вы не вошли в аккаунт, сохранено в браузере",
unable_to_save: "Ошибка записи",
not_premium: "Подключить подписку",
copy_net_link: "Отправьте ссылку на подключение",
@@ -145,6 +147,8 @@ const translations: {[lang: string]: {[key: string]: string} } = {
account_not_ready: "Skip loading saves",
loading_saves: "Loading saves",
success: "Success",
+ success_save: "Saved",
+ warn_save: "You are not logged, saved in browser",
unable_to_save: "Unable to save",
not_premium: "Subscribe",
copy_net_link: "Share this link to connect",
diff --git a/src/player-api.ts b/src/player-api.ts
index ee078053..f2c46e73 100644
--- a/src/player-api.ts
+++ b/src/player-api.ts
@@ -28,10 +28,17 @@ export async function apiSave(state: State,
}
}
- dispatch(uiSlice.actions.showToast({
- message: t("success"),
- intent: "success",
- }));
+ if (account === null || account.email === null) {
+ dispatch(uiSlice.actions.showToast({
+ message: t("warn_save"),
+ intent: "warning",
+ }));
+ } else {
+ dispatch(uiSlice.actions.showToast({
+ message: t("success_save"),
+ intent: "success",
+ }));
+ }
return true;
} catch (e: any) {
diff --git a/src/sidebar/cloud-save-button.tsx b/src/sidebar/cloud-save-button.tsx
deleted file mode 100644
index 28044a86..00000000
--- a/src/sidebar/cloud-save-button.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import { useState } from "preact/hooks";
-import { useDispatch, useSelector, useStore } from "react-redux";
-import { State, useNonSerializableStore } from "../store";
-import { DisketteIcon } from "./diskette-icon";
-import { apiSave } from "../player-api";
-
-export function CloudSaveButton(props: {
- class?: string,
-}) {
- const [busy, setBusy] = useState(false);
- const dispatch = useDispatch();
- const cloudSaves = useSelector((state: State) => state.ui.cloudSaves);
- const nonSerializableStore = useNonSerializableStore();
- const store = useStore();
-
- if (!cloudSaves ||
- nonSerializableStore.loadedBundle === null ||
- nonSerializableStore.loadedBundle.bundleChangesUrl === null) {
- return null;
- }
-
- function onClick() {
- if (busy) {
- return;
- }
-
-
- setBusy(true);
- apiSave(store.getState() as State, nonSerializableStore, dispatch)
- .finally(() =>setBusy(false));
- }
-
- return ;
-}
diff --git a/src/sidebar/save-buttons.tsx b/src/sidebar/save-buttons.tsx
new file mode 100644
index 00000000..7e17bcd8
--- /dev/null
+++ b/src/sidebar/save-buttons.tsx
@@ -0,0 +1,130 @@
+import { useDispatch, useSelector, useStore } from "react-redux";
+import { DisketteIcon } from "./diskette-icon";
+import { State, useNonSerializableStore } from "../store";
+import { useState } from "preact/hooks";
+import { apiSave } from "../player-api";
+
+export function SaveButtons() {
+ const [showLoad, setShowLoad] = useState(false);
+ const dosboxX = useSelector((state: State) => state.dos.backend) === "dosboxX";
+ return
+
+ { dosboxX && setShowLoad(true)} /> }
+ { dosboxX && showLoad && }
+
;
+}
+
+function SaveButton(props: {
+ label: number | string,
+ bgcolor: string,
+ textcolor: string,
+ onClick: () => void,
+}) {
+ const nonSerializableStore = useNonSerializableStore();
+ function onClick() {
+ const ci = nonSerializableStore.ci;
+ if (ci === null) {
+ return;
+ }
+
+ ci.sendBackendEvent({
+ type: "wc-trigger-event",
+ event: "hand_savestate",
+ });
+
+ props.onClick();
+ }
+
+ return ;
+}
+
+function LoadButton(props: {
+ label: number | string,
+ bgcolor: string,
+}) {
+ const nonSerializableStore = useNonSerializableStore();
+ function onClick() {
+ const ci = nonSerializableStore.ci;
+ if (ci === null) {
+ return;
+ }
+
+ ci.sendBackendEvent({
+ type: "wc-trigger-event",
+ event: "hand_loadstate",
+ });
+ }
+
+ return ;
+}
+
+
+function CloudSaveButton(props: {
+ class?: string,
+}) {
+ const [busy, setBusy] = useState(false);
+ const dispatch = useDispatch();
+ const cloudSaves = useSelector((state: State) => state.ui.cloudSaves);
+ const nonSerializableStore = useNonSerializableStore();
+ const store = useStore();
+
+ if (!cloudSaves ||
+ nonSerializableStore.loadedBundle === null ||
+ nonSerializableStore.loadedBundle.bundleChangesUrl === null) {
+ return null;
+ }
+
+ function onClick() {
+ if (busy) {
+ return;
+ }
+
+
+ setBusy(true);
+ apiSave(store.getState() as State, nonSerializableStore, dispatch)
+ .finally(() =>setBusy(false));
+ }
+
+ return ;
+}
diff --git a/src/sidebar/sidebar-button.tsx b/src/sidebar/sidebar-button.tsx
index 4e7ec2df..5ab3c2bb 100644
--- a/src/sidebar/sidebar-button.tsx
+++ b/src/sidebar/sidebar-button.tsx
@@ -1,29 +1,10 @@
import { AnyAction } from "@reduxjs/toolkit";
import { useDispatch, useSelector, useStore } from "react-redux";
-import { LockBadge } from "../components/lock";
import { State, Store } from "../store";
import { Frame, uiSlice } from "../store/ui";
-import { DisketteIcon } from "./diskette-icon";
import { useEffect, useRef, useState } from "preact/hooks";
import { dosSlice } from "../store/dos";
-export function QuickSaveButton(props: {
- class?: string,
-}) {
- const disabled = useSelector((state: State) => state.auth.account) === null && false;
- const action = disabled ?
- uiSlice.actions.modalLogin() :
- uiSlice.actions.frameQuickSave();
- return
-
- {disabled && }
- ;
-}
-
export function FatDrivesButton(props: {
class?: string,
}) {
diff --git a/src/sidebar/sidebar.css b/src/sidebar/sidebar.css
index 2fdb403b..df4d0a39 100644
--- a/src/sidebar/sidebar.css
+++ b/src/sidebar/sidebar.css
@@ -44,4 +44,11 @@
animation: pulse 300ms cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
+ .save-buttons {
+ .text-badge {
+ @apply absolute left-0 top-0 w-3 h-3 font-bold rounded-full flex items-center justify-center;
+ font-size: 0.5rem;
+ }
+ }
+
}
\ No newline at end of file
diff --git a/src/sidebar/sidebar.tsx b/src/sidebar/sidebar.tsx
index 940339ed..c3c510f2 100644
--- a/src/sidebar/sidebar.tsx
+++ b/src/sidebar/sidebar.tsx
@@ -3,13 +3,13 @@ import { State } from "../store";
import { AccountButton } from "./account-button";
import { FullscreenButton } from "./fullscreen-button";
import { NetworkButton } from "./network-button";
-import { CloudSaveButton } from "./cloud-save-button";
import {
DosboxConfButton, SettingsButton, CyclesButton, FsButton,
- FatDrivesButton, QuickSaveButton, HddLed,
+ FatDrivesButton, HddLed,
SoftKeyboardButton,
PreRunButton,
} from "./sidebar-button";
+import { SaveButtons } from "./save-buttons";
export function SideBar(props: {}) {
const window = useSelector((state: State) => state.ui.window);
@@ -25,8 +25,7 @@ export function SideBar(props: {}) {
}
return