From c3fd5d6a639436778ce51657b3431fba38210313 Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Thu, 5 Sep 2024 10:23:11 +0300 Subject: [PATCH] Refactor save/load ui --- package.json | 2 +- src/frame/frame.css | 7 -- src/frame/frame.tsx | 2 - src/frame/quick-save-frame.tsx | 98 ---------------------- src/i18n.ts | 4 + src/player-api.ts | 15 +++- src/sidebar/cloud-save-button.tsx | 47 ----------- src/sidebar/save-buttons.tsx | 130 ++++++++++++++++++++++++++++++ src/sidebar/sidebar-button.tsx | 19 ----- src/sidebar/sidebar.css | 7 ++ src/sidebar/sidebar.tsx | 7 +- src/store/ui.ts | 8 +- src/ui.tsx | 26 +++++- yarn.lock | 8 +- 14 files changed, 186 insertions(+), 194 deletions(-) delete mode 100644 src/frame/quick-save-frame.tsx delete mode 100644 src/sidebar/cloud-save-button.tsx create mode 100644 src/sidebar/save-buttons.tsx 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
- - {props.disabled !== true && -
{props.label}
} - {props.disabled === true && } - - - - -
; -} - -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
- - {props.disabled === true && } - - - -
; -} 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
-
- - {busy && -
; -} 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
+ +
{props.label}
+ + + +
; +} + +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
+
+ + {busy && +
; +} 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