- { frame === "account" &&
}
{ frame === "settings" &&
}
{ frame === "editor-conf" &&
}
{ frame === "editor-fs" &&
}
diff --git a/src/i18n.ts b/src/i18n.ts
index d8ab38ae..540a2ae0 100644
--- a/src/i18n.ts
+++ b/src/i18n.ts
@@ -4,6 +4,8 @@ import { State } from "./store";
const translations: {[lang: string]: {[key: string]: string} } = {
ru: {
+ hello: "Привет",
+ hello_guest: "Привет, гость!",
mobile_controls: "Моибльное управление",
mirrored_controls: "Отразить управление",
scale_controls: "Масштаб управления",
@@ -62,7 +64,6 @@ const translations: {[lang: string]: {[key: string]: string} } = {
editor: "Редактор",
download: "Скачать",
changes: "Сохранения",
- account_not_ready: "Пропустить загрузку сохранений",
loading_saves: "Загрузка сохранений",
success: "Успешно",
success_save: "Сохранено в облаке",
@@ -84,10 +85,13 @@ const translations: {[lang: string]: {[key: string]: string} } = {
fat_write: "FAT запись",
play: "Запустить",
system_cursor: "Системный курсор",
- no_cloud_access: "Войдите что бы использовать",
- cloud_storage: "облачное хранилище",
+ no_cloud_access: "Введите",
+ no_cloud_access2: "что бы использовать облачное хранилище",
+ key: "Ваш ключ",
},
en: {
+ hello: "Hello",
+ hello_guest: "Hello, guest!",
play: "Start",
system_cursor: "System cursor",
mobile_controls: "Mobile controls",
@@ -148,7 +152,6 @@ const translations: {[lang: string]: {[key: string]: string} } = {
editor: "Editor",
download: "Download",
changes: "Saves",
- account_not_ready: "Skip loading saves",
loading_saves: "Loading saves",
success: "Success",
success_save: "Saved in cloud",
@@ -168,8 +171,9 @@ const translations: {[lang: string]: {[key: string]: string} } = {
hardware: "Hardware acceleration",
net_drives: "Net drives",
fat_write: "Writeable FAT",
- no_cloud_access: "Please login to use",
- cloud_storage: "cloud storage",
+ no_cloud_access: "Please enter",
+ no_cloud_access2: "to use cloud storage",
+ key: "your key",
},
};
diff --git a/src/index.css b/src/index.css
index dfd071a5..67041052 100644
--- a/src/index.css
+++ b/src/index.css
@@ -2,7 +2,6 @@
@import "./app.css";
@import "./components/components.css";
@import "./sidebar/sidebar.css";
-@import "./login/login.css";
@import "./window/window.css";
@import "./frame/frame.css";
@import "./layers/layers.css";
\ No newline at end of file
diff --git a/src/login/login.css b/src/login/login.css
deleted file mode 100644
index 5a3117fe..00000000
--- a/src/login/login.css
+++ /dev/null
@@ -1,9 +0,0 @@
-.jsdos-rso {
- .login-widget {
- @apply absolute z-40 left-12 top-0 bottom-0 right-0 bg-white;
-
- iframe {
- @apply w-full h-full p-0 m-0 border-0;
- }
- }
-}
\ No newline at end of file
diff --git a/src/login/login.tsx b/src/login/login.tsx
deleted file mode 100644
index 05688e12..00000000
--- a/src/login/login.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-import { Dispatch, useEffect, useRef } from "preact/hooks";
-import { useDispatch, useSelector } from "react-redux";
-import { State } from "../store";
-import { uiSlice } from "../store/ui";
-import { CloseButton } from "../components/close-button";
-import { authentificator } from "../v8/config";
-import { Account, authSlice, getRefreshToken } from "../store/auth";
-import { havePremium } from "../v8/subscriptions";
-import { AnyAction } from "@reduxjs/toolkit";
-
-export function Login() {
- const iframeRef = useRef
(null);
- const activeAccount = useSelector((state: State) => state.auth.account);
- const visible = useSelector((state: State) => state.ui.modal) === "login";
- const loginUrl = useSelector((state: State) => state.auth.loginUrl);
- const dispatch = useDispatch();
-
- useEffect(() => {
- if (activeAccount != null) {
- return;
- }
-
- const iframe = iframeRef.current;
- if (iframe === null) {
- dispatch(authSlice.actions.logout({}));
- dispatch(authSlice.actions.ready());
- console.error("Unable to inialize authentificator!");
- return;
- }
-
- async function onAuthMessage(e: any) {
- if (e.data.action === "auth/ready") {
- postAuthMessage("auth/authenicate", iframe as HTMLIFrameElement, dispatch);
- } else if (e.data.action === "auth/authenicate") {
- const account = e.data.account as Account | null;
- if (account !== null) {
- try {
- account.premium = await havePremium(account.token.access_token);
- } catch (e: any) {
- console.error(e);
- account.premium = false;
- }
- dispatch(authSlice.actions.login(e.data.account));
- } else {
- dispatch(authSlice.actions.logout({}));
- dispatch(authSlice.actions.ready());
- }
- }
- }
-
- window.addEventListener("message", onAuthMessage);
- iframe.src = authentificator;
-
- return () => {
- window.removeEventListener("message", onAuthMessage);
- };
- }, []);
-
- useEffect(() => {
- if (visible) {
- const iframe = iframeRef.current;
- if (iframe === null) {
- return;
- }
-
- postAuthMessage("auth/login", iframe, dispatch, loginUrl);
- }
- }, [visible, loginUrl]);
-
- return
-
- dispatch(uiSlice.actions.modalNone()) }
- />
-
;
-}
-
-function postAuthMessage(action: "auth/login" | "auth/authenicate",
- iframe: HTMLIFrameElement,
- dispatch: Dispatch,
- loginUrl?: string) {
- const message = {
- action,
- refresh_token: action === "auth/authenicate" ? getRefreshToken() : undefined,
- url: action === "auth/login" ? loginUrl : undefined,
- };
- if (!iframe || !iframe.contentWindow) {
- console.error("Can't find authentificator iframe");
- dispatch(authSlice.actions.logout({}));
- dispatch(authSlice.actions.ready());
- } else {
- iframe.contentWindow.postMessage(message, "*");
- }
-}
diff --git a/src/main.tsx b/src/main.tsx
index e12849b0..dcd37165 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -14,8 +14,8 @@ import { loadBundleFromConfg, loadBundleFromUrl } from "./player-api-load";
import { DosOptions, DosProps, DosFn, ImageRendering, RenderBackend } from "./public/types";
import { browserSetFullScreen } from "./host/fullscreen";
import { NonSerializableStore, State, Store, getNonSerializableStore,
+ getState,
makeNonSerializableStore, makeStore, postJsDosEvent } from "./store";
-import { authSlice } from "./store/auth";
import { apiSave } from "./player-api";
export const Dos: DosFn = (element: HTMLDivElement,
@@ -24,7 +24,7 @@ export const Dos: DosFn = (element: HTMLDivElement,
const store = makeStore(nonSerializableStore, options);
- if (store.getState().auth.account?.email === "dz.caiiiycuk@gmail.com") {
+ if (getState(store).auth.account?.email === "dz.caiiiycuk@gmail.com") {
store.dispatch(dosSlice.actions.setSockdriveWrite(false));
}
@@ -33,7 +33,7 @@ export const Dos: DosFn = (element: HTMLDivElement,
let pollStep = "none";
function pollEvents() {
(async () => {
- const state = store.getState();
+ const state = getState(store);
const step = state.dos.step;
if (step === pollStep) {
@@ -212,10 +212,6 @@ export const Dos: DosFn = (element: HTMLDivElement,
setFullScreen(options.fullScreen);
}
- if (options.loginUrl !== undefined) {
- store.dispatch(authSlice.actions.setLoginUrl(options.loginUrl));
- }
-
if (options.autoStart !== undefined) {
setAutoStart(options.autoStart);
}
@@ -303,7 +299,7 @@ export const Dos: DosFn = (element: HTMLDivElement,
setVolume,
save: () => {
- return apiSave(store.getState() as any as State, nonSerializableStore, store.dispatch);
+ return apiSave(getState(store) as any as State, nonSerializableStore, store.dispatch);
},
stop: async () => {
store.dispatch(uiSlice.actions.hidden(true));
@@ -331,12 +327,12 @@ function setupRootElement(root: HTMLDivElement, nonSerializableStore: NonSeriali
const fullscreen = document.fullscreenElement === root;
store.dispatch(uiSlice.actions.setFullScreen(fullscreen));
if (!fullscreen) {
- apiSave(store.getState() as any, nonSerializableStore, store.dispatch);
+ apiSave(getState(store) as any, nonSerializableStore, store.dispatch);
}
});
document.addEventListener("pointerlockchange", () => {
if (document.pointerLockElement === null) {
- apiSave(store.getState() as any, nonSerializableStore, store.dispatch);
+ apiSave(getState(store) as any, nonSerializableStore, store.dispatch);
}
});
function listen() {
diff --git a/src/player-api-load.ts b/src/player-api-load.ts
index 4fcb07fe..98914e06 100644
--- a/src/player-api-load.ts
+++ b/src/player-api-load.ts
@@ -1,4 +1,4 @@
-import { Dispatch, Store, Unsubscribe } from "@reduxjs/toolkit";
+import { Dispatch, Store } from "@reduxjs/toolkit";
import { DosConfig, Emulators, InitFs } from "emulators";
import { dosSlice } from "./store/dos";
import { bundleFromChanges, bundleFromFile, bundleFromUrl } from "./host/bundle-storage";
@@ -6,7 +6,7 @@ import { uiSlice } from "./store/ui";
import { editorSlice } from "./store/editor";
import { getChangesUrl } from "./v8/changes";
import { storageSlice } from "./store/storage";
-import { getNonSerializableStore } from "./store";
+import { getNonSerializableStore, getState } from "./store";
declare const emulators: Emulators;
@@ -54,7 +54,7 @@ export async function loadBundleFromConfg(config: DosConfig, initFs: InitFs | nu
}
export async function loadBundleFromUrl(url: string, store: Store) {
- const owner = store.getState().auth.account?.email ?? "guest";
+ const owner = getState(store).auth.account?.email ?? "guest";
const changesUrl = await getChangesUrl(owner, url);
return doLoadBundle(url,
bundleFromUrl(url, store),
@@ -102,20 +102,7 @@ async function changesProducer(bundleUrl: string, store: Store): Promise<{
url: string,
bundle: Uint8Array | null,
}> {
- if (!store.getState().auth.ready) {
- await new Promise((resolve) => {
- let unsubscirbe: Unsubscribe | null = null;
- const updateFn = () => {
- if (store.getState().auth.ready) {
- unsubscirbe!();
- resolve();
- }
- };
- unsubscirbe = store.subscribe(updateFn);
- });
- }
-
- const account = store.getState().auth.account;
+ const account = getState(store).auth.account;
const owner = account?.email ?? "guest";
const url = getChangesUrl(owner, bundleUrl);
const bundle = await bundleFromChanges(url, account, store);
diff --git a/src/public/types.ts b/src/public/types.ts
index 2154370d..bf569bbe 100644
--- a/src/public/types.ts
+++ b/src/public/types.ts
@@ -37,7 +37,6 @@ export interface DosOptions {
name: string,
host: string,
},
- loginUrl: string,
autoStart: boolean,
kiosk: boolean,
imageRendering: ImageRendering,
diff --git a/src/sidebar/account-button.tsx b/src/sidebar/account-button.tsx
deleted file mode 100644
index 40caf930..00000000
--- a/src/sidebar/account-button.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { useDispatch, useSelector } from "react-redux";
-import { dispatchLoginAction } from "../store/ui";
-import { State } from "../store";
-
-export function AccountButton(props: {
- class?: string,
-}) {
- const account = useSelector((state: State) => state.auth.account);
- const loggedIn = account !== null;
- const hightlight = useSelector((state: State) => state.ui.frame) === "account";
- const dispatch = useDispatch();
-
- function onClick() {
- dispatchLoginAction(account, dispatch);
- }
-
- return ;
-}
diff --git a/src/sidebar/save-buttons.tsx b/src/sidebar/save-buttons.tsx
index 7e17bcd8..70bc5be9 100644
--- a/src/sidebar/save-buttons.tsx
+++ b/src/sidebar/save-buttons.tsx
@@ -1,6 +1,6 @@
import { useDispatch, useSelector, useStore } from "react-redux";
import { DisketteIcon } from "./diskette-icon";
-import { State, useNonSerializableStore } from "../store";
+import { getState, State, useNonSerializableStore } from "../store";
import { useState } from "preact/hooks";
import { apiSave } from "../player-api";
@@ -109,7 +109,7 @@ function CloudSaveButton(props: {
setBusy(true);
- apiSave(store.getState() as State, nonSerializableStore, dispatch)
+ apiSave(getState(store as any), nonSerializableStore, dispatch)
.finally(() =>setBusy(false));
}
diff --git a/src/sidebar/sidebar-button.tsx b/src/sidebar/sidebar-button.tsx
index 5ab3c2bb..3860d259 100644
--- a/src/sidebar/sidebar-button.tsx
+++ b/src/sidebar/sidebar-button.tsx
@@ -1,6 +1,6 @@
import { AnyAction } from "@reduxjs/toolkit";
import { useDispatch, useSelector, useStore } from "react-redux";
-import { State, Store } from "../store";
+import { getState, State, Store } from "../store";
import { Frame, uiSlice } from "../store/ui";
import { useEffect, useRef, useState } from "preact/hooks";
import { dosSlice } from "../store/dos";
@@ -91,7 +91,7 @@ export function HddLed(props: {}) {
const id = setInterval(() => {
if (state.delayLedTo <= Date.now()) {
- const newRecv = store.getState().dos.stats.driveRecv;
+ const newRecv = getState(store).dos.stats.driveRecv;
const newEnabled = state.recv !== newRecv;
if (newEnabled !== state.enabled) {
el.classList.remove("bg-base-300", "bg-green-300", "animate-led");
diff --git a/src/sidebar/sidebar.tsx b/src/sidebar/sidebar.tsx
index 3ba83f25..020e5ed0 100644
--- a/src/sidebar/sidebar.tsx
+++ b/src/sidebar/sidebar.tsx
@@ -1,6 +1,5 @@
import { useSelector } from "react-redux";
import { State } from "../store";
-import { AccountButton } from "./account-button";
import { FullscreenButton } from "./fullscreen-button";
import { NetworkButton } from "./network-button";
import {
@@ -17,7 +16,6 @@ export function SideBar(props: {}) {
const backend = useSelector((state: State) => state.dos.backend);
const kiosk = useSelector((state: State) => state.ui.kiosk);
const networking = !useSelector((state: State) => state.ui.noNetworking);
- const cloud = !useSelector((state: State) => state.ui.noCloud);
if (kiosk) {
return null;
@@ -36,6 +34,5 @@ export function SideBar(props: {}) {
{window === "run" && }
{window === "prerun" && }
{window === "run" && }
- {window !== "run" && cloud && }
;
};
diff --git a/src/store.tsx b/src/store.tsx
index a1d5234b..1b19eea9 100644
--- a/src/store.tsx
+++ b/src/store.tsx
@@ -118,3 +118,7 @@ export function postJsDosEvent(nonSerializableStore: NonSerializableStore, event
}, 4);
}
}
+
+export function getState(store: Store): State {
+ return store.getState() as any;
+}
diff --git a/src/store/auth.ts b/src/store/auth.ts
index 442dd6e6..2a58fb46 100644
--- a/src/store/auth.ts
+++ b/src/store/auth.ts
@@ -2,49 +2,33 @@ import { createSlice } from "@reduxjs/toolkit";
import { getCache } from "../host/lcache";
import { lStorage } from "../host/lstorage";
import { DosAction, getNonSerializableStore } from "../store";
+import { tokenGet } from "../v8/config";
import { dosSlice } from "./dos";
-const cachedAccount = "cached.account";
-
-export interface Token {
- access_token: string,
- refresh_token: string,
- scope: string,
- expires_in: number,
- validUntilMs: number;
-};
+const cachedAccount = "cached.jsdos.account";
export interface Account {
+ token: string,
+ name: string,
email: string,
- name: null | string,
- picture: null | string,
- token: Token,
premium: boolean,
};
const initAccount = (() => {
- const params = new URLSearchParams(location.search);
- const refreshToken = params.get("jsdos_token");
- if (refreshToken !== null) {
- setRefreshToken(refreshToken);
- history.replaceState(null, "", location.href.substring(0, location.href.indexOf("?")));
- return null;
- } else {
- const json = lStorage.getItem(cachedAccount);
- const account = json !== null ? JSON.parse(json) : null;
- return account !== null && account.token.validUntilMs - Date.now() > 1 * 60 * 60 * 1000 ?
- account : null;
+ const json = lStorage.getItem(cachedAccount);
+ if (json) {
+ const account = JSON.parse(json);
+ if (account.email && account.email.length > 0 && account.token && account.token.length === 5) {
+ return account;
+ }
}
+ return null;
})();
const initialState: {
- loginUrl: string,
account: Account | null,
- ready: boolean,
} = {
- loginUrl: location.href,
account: initAccount,
- ready: initAccount !== null,
};
export type AuthState = typeof initialState;
@@ -53,56 +37,46 @@ export const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
- login: (state, action: { payload: Account }) => {
- setRefreshToken(action.payload.token.refresh_token);
- state.account = action.payload;
- state.account.premium = state.account.premium ||
- state.account.email === "dz.caiiiycuk@gmail.com";
- lStorage.setItem(cachedAccount, JSON.stringify(action.payload));
- (action as unknown as DosAction).asyncStore((store) => {
- if (action.payload.email === "dz.caiiiycuk@gmail.com") {
- store.dispatch(dosSlice.actions.setSockdriveWrite(false));
- }
- getCache(action.payload.email)
- .then((cache) => getNonSerializableStore(store).cache = cache)
- .catch(console.error)
- .finally(() => {
- store.dispatch(authSlice.actions.ready());
- });
- });
- },
- logout: (state, action) => {
- clearRefreshToken();
- lStorage.removeItem(cachedAccount);
- (action as unknown as DosAction).asyncStore((store) => {
- getCache("guest")
- .then((cache) => getNonSerializableStore(store).cache = cache)
- .catch(console.error);
- });
- state.account = null;
- },
- ready: (state) => {
- state.ready = true;
- },
- setLoginUrl: (state, action: { payload: string }) => {
- state.loginUrl = action.payload;
+ setAccount: (state, action: { payload: Account | null }) => {
+ const account = action.payload;
+ console.log("SET~!", account);
+ if (account !== null) {
+ lStorage.setItem(cachedAccount, JSON.stringify(account));
+ (action as unknown as DosAction).asyncStore((store) => {
+ if (account.email === "dz.caiiiycuk@gmail.com") {
+ store.dispatch(dosSlice.actions.setSockdriveWrite(false));
+ }
+ getCache(account.email)
+ .then((cache) => getNonSerializableStore(store).cache = cache)
+ .catch(console.error);
+ });
+ } else {
+ lStorage.removeItem(cachedAccount);
+ (action as unknown as DosAction).asyncStore((store) => {
+ getCache("guest")
+ .then((cache) => getNonSerializableStore(store).cache = cache)
+ .catch(console.error);
+ });
+ }
+ state.account = account;
},
},
});
-export function getRefreshToken(): string | null {
- return lStorage.getItem("cached.rt");
-}
+export async function loadAccount(token: string) {
+ if (!token || token.length !== 5) {
+ return { token, account: null };
+ }
-function setRefreshToken(refreshToken: string | null) {
- if (refreshToken === null) {
- clearRefreshToken();
- return;
+ for (let i = 0; i < token.length; ++i) {
+ const code = token.charCodeAt(i);
+ if (!(code > 96 && code < 123)) { // lower alpha (a-z)
+ return { token, account: null };
+ }
}
- return lStorage.setItem("cached.rt", refreshToken);
-}
+ const account = await (await fetch(tokenGet + "?id=" + token)).json();
+ delete account.success;
-function clearRefreshToken() {
- lStorage.removeItem("cached.rt");
+ return { token, account: account.email ? account : null };
}
diff --git a/src/store/ui.ts b/src/store/ui.ts
index 87d70ef7..8189c2cd 100644
--- a/src/store/ui.ts
+++ b/src/store/ui.ts
@@ -1,8 +1,8 @@
-import { Dispatch, createAction, createSlice } from "@reduxjs/toolkit";
+import { createAction, createSlice, Dispatch } from "@reduxjs/toolkit";
import { lStorage } from "../host/lstorage";
-import { Account } from "./auth";
import { DosAction } from "../store";
import { dosSlice } from "./dos";
+import { Account } from "./auth";
export const ThemeValues =