js-dos 7.4.9 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/.github/workflows/build.yml +51 -0
  2. package/.gitmodules +4 -0
  3. package/README.deployment.md +11 -0
  4. package/README.md +29 -89
  5. package/index.html +106 -0
  6. package/package.json +46 -71
  7. package/postcss.config.cjs +8 -0
  8. package/src/app.css +14 -0
  9. package/src/base.css +10 -0
  10. package/src/components/checkbox.tsx +41 -0
  11. package/src/components/close-button.tsx +17 -0
  12. package/src/components/components.css +28 -0
  13. package/src/components/dos-option-checkbox.tsx +94 -0
  14. package/src/components/dos-option-select.tsx +108 -0
  15. package/src/components/dos-option-slider.tsx +35 -0
  16. package/src/components/error.tsx +16 -0
  17. package/src/components/loading.tsx +26 -0
  18. package/src/components/lock.tsx +10 -0
  19. package/src/components/select.tsx +33 -0
  20. package/src/components/slider.tsx +69 -0
  21. package/src/download-file.ts +18 -0
  22. package/src/frame/account-frame.tsx +156 -0
  23. package/src/frame/editor/defaults.ts +419 -0
  24. package/src/frame/editor/editor-conf-frame.tsx +226 -0
  25. package/src/frame/editor/editor-frame.css +55 -0
  26. package/src/frame/editor/editor-fs-frame.tsx +314 -0
  27. package/src/frame/fat-drives-frame.tsx +135 -0
  28. package/src/frame/frame.css +119 -0
  29. package/src/frame/frame.tsx +34 -0
  30. package/src/frame/image-rendering-frame.tsx +8 -0
  31. package/src/frame/network-frame.tsx +157 -0
  32. package/src/frame/quick-save-frame.tsx +96 -0
  33. package/src/frame/settings-frame.tsx +14 -0
  34. package/src/frame/stats-frame.tsx +87 -0
  35. package/src/host/bundle-storage.ts +91 -0
  36. package/src/host/fullscreen.ts +35 -0
  37. package/src/host/lcache.ts +203 -0
  38. package/src/host/lstorage.ts +156 -0
  39. package/src/i18n.ts +179 -0
  40. package/src/index.css +7 -0
  41. package/src/load.ts +132 -0
  42. package/src/login/login.css +9 -0
  43. package/src/login/login.tsx +77 -0
  44. package/src/main.tsx +197 -0
  45. package/src/public/types.ts +41 -0
  46. package/src/sidebar/account-button.tsx +33 -0
  47. package/src/sidebar/cloud-save-button.tsx +89 -0
  48. package/src/sidebar/diskette-icon.tsx +14 -0
  49. package/src/sidebar/fullscreen-button.tsx +44 -0
  50. package/src/sidebar/network-button.tsx +39 -0
  51. package/src/sidebar/sidebar-button.tsx +244 -0
  52. package/src/sidebar/sidebar.css +47 -0
  53. package/src/sidebar/sidebar.tsx +30 -0
  54. package/src/sockdrive/LICENSE +674 -0
  55. package/src/sockdrive/README.md +45 -0
  56. package/src/sockdrive/include/sockdrive.h +23 -0
  57. package/src/sockdrive/js/.eslintrc.json +43 -0
  58. package/src/sockdrive/js/package.json +29 -0
  59. package/src/sockdrive/js/src/fatfs/chains.js +318 -0
  60. package/src/sockdrive/js/src/fatfs/dir.js +385 -0
  61. package/src/sockdrive/js/src/fatfs/helpers.js +210 -0
  62. package/src/sockdrive/js/src/fatfs/index.js +606 -0
  63. package/src/sockdrive/js/src/fatfs/structs.js +255 -0
  64. package/src/sockdrive/js/src/fatfs/vol.js +166 -0
  65. package/src/sockdrive/js/src/sockdrive/cache.ts +133 -0
  66. package/src/sockdrive/js/src/sockdrive/drive.ts +457 -0
  67. package/src/sockdrive/js/src/sockdrive/lru.ts +221 -0
  68. package/src/sockdrive/js/src/sockdrive/types.ts +14 -0
  69. package/src/sockdrive/js/src/sockdrive-fat.d.ts +64 -0
  70. package/src/sockdrive/js/src/sockdrive-fat.ts +226 -0
  71. package/src/sockdrive/js/src/sockdrive-native.ts +100 -0
  72. package/src/sockdrive/js/src/test/test.ts +288 -0
  73. package/src/sockdrive/js/tsconfig.json +11 -0
  74. package/src/sockdrive/js/webpack.config.js +68 -0
  75. package/src/sockdrive/js/yarn.lock +1808 -0
  76. package/src/store/auth.ts +115 -0
  77. package/src/store/dos.ts +305 -0
  78. package/src/store/editor.ts +61 -0
  79. package/src/store/init.ts +48 -0
  80. package/src/store/storage.ts +32 -0
  81. package/src/store/ui.ts +186 -0
  82. package/src/store.tsx +108 -0
  83. package/src/ui.tsx +104 -0
  84. package/src/v8/changes.ts +56 -0
  85. package/src/v8/config.ts +18 -0
  86. package/src/v8/subscriptions.ts +62 -0
  87. package/src/vite-env.d.ts +2 -0
  88. package/src/window/dos/controls/keyboard.ts +32 -0
  89. package/src/window/dos/controls/keys.ts +331 -0
  90. package/src/window/dos/controls/mouse/mount.ts +99 -0
  91. package/src/window/dos/controls/mouse/mouse-locked.ts +51 -0
  92. package/src/window/dos/controls/mouse/mouse-not-locked.ts +84 -0
  93. package/src/window/dos/controls/mouse/mouse-swipe.ts +57 -0
  94. package/src/window/dos/controls/mouse/pointer.ts +85 -0
  95. package/src/window/dos/controls/mouse.ts +21 -0
  96. package/src/window/dos/dos-dhry2.tsx +191 -0
  97. package/src/window/dos/dos-runtime.tsx +214 -0
  98. package/src/window/dos/dos-window.tsx +84 -0
  99. package/src/window/dos/render/canvas.ts +65 -0
  100. package/src/window/dos/render/resize.ts +31 -0
  101. package/src/window/dos/render/webgl.ts +173 -0
  102. package/src/window/dos/sound/audio-node.ts +148 -0
  103. package/src/window/error-window.tsx +9 -0
  104. package/src/window/loading-window.tsx +54 -0
  105. package/src/window/prerun-window.tsx +123 -0
  106. package/src/window/select-window.tsx +118 -0
  107. package/src/window/window.css +78 -0
  108. package/src/window/window.tsx +49 -0
  109. package/src/ws/ws-sockdrive.ts +101 -0
  110. package/src/ws/ws-transport-layer.ts +455 -0
  111. package/tailwind.config.cjs +48 -0
  112. package/tsconfig.json +32 -0
  113. package/tsconfig.node.json +9 -0
  114. package/vite.config.ts +22 -0
  115. package/LICENSE +0 -339
  116. package/dist/emulators-ui-loader.png +0 -0
  117. package/dist/js-dos.css +0 -1690
  118. package/dist/js-dos.js +0 -17
  119. package/dist/js-dos.js.map +0 -1
  120. package/dist/types/src/backend/v7/latency.d.ts +0 -8
  121. package/dist/types/src/backend/v7/personal.d.ts +0 -2
  122. package/dist/types/src/backend/v7/v7-config.d.ts +0 -16
  123. package/dist/types/src/components/action-bar.d.ts +0 -2
  124. package/dist/types/src/components/action-hide.d.ts +0 -4
  125. package/dist/types/src/components/action-save-or-exit.d.ts +0 -4
  126. package/dist/types/src/components/client.d.ts +0 -6
  127. package/dist/types/src/components/controls.d.ts +0 -8
  128. package/dist/types/src/components/cpu-control.d.ts +0 -2
  129. package/dist/types/src/components/horizontal-slider.d.ts +0 -13
  130. package/dist/types/src/components/region.d.ts +0 -6
  131. package/dist/types/src/components/scale-control.d.ts +0 -3
  132. package/dist/types/src/components/sensitivity-control.d.ts +0 -3
  133. package/dist/types/src/components/sidebar/latency-info.d.ts +0 -7
  134. package/dist/types/src/components/sidebar/main.d.ts +0 -2
  135. package/dist/types/src/components/sidebar/networking.d.ts +0 -2
  136. package/dist/types/src/components/sidebar/token/token-add-time.d.ts +0 -2
  137. package/dist/types/src/components/sidebar/token/token-select.d.ts +0 -2
  138. package/dist/types/src/components/sidebar/token/token.d.ts +0 -17
  139. package/dist/types/src/components/sidebar.d.ts +0 -2
  140. package/dist/types/src/components/sync-control.d.ts +0 -2
  141. package/dist/types/src/components/tip.d.ts +0 -2
  142. package/dist/types/src/components/vertical-slider.d.ts +0 -12
  143. package/dist/types/src/components/volume-control.d.ts +0 -3
  144. package/dist/types/src/dom.d.ts +0 -8
  145. package/dist/types/src/hardware-transport-layer.d.ts +0 -18
  146. package/dist/types/src/icons.d.ts +0 -95
  147. package/dist/types/src/player-app.d.ts +0 -50
  148. package/dist/types/src/player.d.ts +0 -29
  149. package/dist/types/src/request.d.ts +0 -1
  150. package/dist/types/src/xhr.d.ts +0 -6
  151. package/dist/types/v7-services/src/personal.d.ts +0 -3
  152. package/dist/wdosbox.js +0 -55
  153. package/dist/wdosbox.js.symbols +0 -3174
  154. package/dist/wdosbox.wasm +0 -0
  155. package/examples/digger/index.html +0 -71
@@ -0,0 +1,156 @@
1
+ const MAX_VALUE_SIZE = 1024;
2
+ const NEXT_PART_SYMBOL = "@";
3
+ const NEXT_PART_SYFFIX = ".";
4
+
5
+ class MemStorage implements Storage {
6
+ length = 0;
7
+
8
+ private storage: {[key: string]: string} = {};
9
+
10
+ setItem(key: string, value: string): void {
11
+ this.storage[key] = value;
12
+ this.length = Object.keys(this.storage).length;
13
+ }
14
+
15
+ getItem(key: string): string | null {
16
+ const value = this.storage[key];
17
+ return value === undefined ? null : value;
18
+ }
19
+
20
+ removeItem(key: string): void {
21
+ delete this.storage[key];
22
+ this.length = Object.keys(this.storage).length;
23
+ }
24
+
25
+ key(index: number): string | null {
26
+ const keys = Object.keys(this.storage);
27
+ return keys[index] === undefined ? null : keys[index];
28
+ }
29
+
30
+ clear() {
31
+ this.length = 0;
32
+ this.storage = {};
33
+ }
34
+ }
35
+
36
+
37
+ class LStorage implements Storage {
38
+ private backend: Storage;
39
+ length: number;
40
+ prefix: string;
41
+
42
+ constructor(backend: Storage | undefined, prefix: string) {
43
+ this.prefix = prefix;
44
+
45
+ try {
46
+ this.backend = backend || localStorage;
47
+ this.testBackend();
48
+ } catch (e) {
49
+ this.backend = new MemStorage();
50
+ }
51
+
52
+ this.length = this.backend.length;
53
+
54
+ if (typeof this.backend.sync === "function") {
55
+ (this as any).sync = (callback: any) => {
56
+ this.backend.sync(callback);
57
+ };
58
+ }
59
+ }
60
+
61
+ private testBackend() {
62
+ const testKey = this.prefix + ".test.record";
63
+ const testValue = "123";
64
+ this.backend.setItem(testKey, testValue);
65
+ const readedValue = this.backend.getItem(testKey);
66
+ this.backend.removeItem(testKey);
67
+ const valid = readedValue === testValue && this.backend.getItem(testKey) === null;
68
+
69
+ if (!valid) {
70
+ throw new Error("Storage backend is not working properly");
71
+ }
72
+ }
73
+
74
+ setLocalStoragePrefix(prefix: string) {
75
+ this.prefix = prefix;
76
+ }
77
+
78
+ clear(): void {
79
+ if (!this.backend.length) {
80
+ return;
81
+ }
82
+
83
+ const toRemove: string[] = [];
84
+ for (let i = 0; i < this.backend.length; ++i) {
85
+ const next = this.backend.key(i);
86
+ if (next && next.startsWith(this.prefix)) {
87
+ toRemove.push(next);
88
+ }
89
+ }
90
+
91
+ for (const next of toRemove) {
92
+ this.backend.removeItem(next);
93
+ }
94
+ this.length = this.backend.length;
95
+ }
96
+
97
+ key(index: number): string | null {
98
+ return this.backend.key(index);
99
+ }
100
+
101
+ setItem(key: string, value: string): void {
102
+ if (!value || value.length === undefined || value.length === 0) {
103
+ this.writeStringToKey(key, "");
104
+ return;
105
+ }
106
+
107
+ let offset = 0;
108
+ while (offset < value.length) {
109
+ let substr = value.substr(offset, MAX_VALUE_SIZE);
110
+ offset += substr.length;
111
+
112
+ if (offset < value.length) {
113
+ substr += NEXT_PART_SYMBOL;
114
+ }
115
+
116
+ this.writeStringToKey(key, substr);
117
+ key += NEXT_PART_SYFFIX;
118
+ }
119
+ }
120
+
121
+ getItem(key: string): string | null {
122
+ let value = this.readStringFromKey(key);
123
+ if (value === null) {
124
+ return null;
125
+ }
126
+
127
+ if (value.length === 0) {
128
+ return value;
129
+ }
130
+
131
+ while (value[value.length - 1] === NEXT_PART_SYMBOL) {
132
+ value = value.substr(0, value.length - 1);
133
+ key += NEXT_PART_SYFFIX;
134
+ const next = this.readStringFromKey(key);
135
+ value += next === null ? "" : next;
136
+ }
137
+
138
+ return value;
139
+ }
140
+
141
+ removeItem(key: string): void {
142
+ this.backend.removeItem(this.prefix + key);
143
+ this.length = this.backend.length;
144
+ }
145
+
146
+ private writeStringToKey(key: string, value: string) {
147
+ this.backend.setItem(this.prefix + key, value);
148
+ this.length = this.backend.length;
149
+ }
150
+
151
+ private readStringFromKey(key: string) {
152
+ return this.backend.getItem(this.prefix + key);
153
+ }
154
+ }
155
+
156
+ export const lStorage = new LStorage(undefined, "jsdos.8.");
package/src/i18n.ts ADDED
@@ -0,0 +1,179 @@
1
+ import { createSlice } from "@reduxjs/toolkit";
2
+ import { useSelector } from "react-redux";
3
+ import { State } from "./store";
4
+
5
+ const translations: {[lang: string]: {[key: string]: string} } = {
6
+ ru: {
7
+ preloading_sockdrive: "Загрузка FAT",
8
+ filter: "Фильтр",
9
+ add: "Добавить",
10
+ logout: "Выйти",
11
+ please_login: "Войдите в аккаунт",
12
+ features: "Функции",
13
+ premium: "Премиум",
14
+ buy: "Купить",
15
+ experimental_features: "Доступ к экспериентальным функциям",
16
+ cloud_saves: "Облачные сохранения",
17
+ writeable_fat32: "FAT32 диски",
18
+ net_no_limits: "Выбор сервера для стевой игры",
19
+ unlock_options: "Доступ ко всем настройкам",
20
+ error: "Упс... Что-то пошло не так...",
21
+ consult_logs: "Проверьте логи браузера",
22
+ bundle_loading: "Загрузка бандла",
23
+ bundle_config: "Чтение конфигурации",
24
+ try_free: "дней бесплатно",
25
+ upload_file: "Выбирите архив jsdos/zip что бы начать...",
26
+ emulation_backend: "Эмуляция",
27
+ render_backend: "Рендер",
28
+ render_aspect: "Соотношение сторон",
29
+ worker: "Фоновый поток",
30
+ mouse_sensitivity: "Чувст. мыши",
31
+ mouse_lock: "Захват мыши",
32
+ volume: "Громкость",
33
+ pause: "Пауза",
34
+ dosboxconf_template: "dosbox.conf шаблон:",
35
+ preview: "Превью",
36
+ continue: "Продолжить",
37
+ skip: "Пропустить",
38
+ loading: "Загрузка",
39
+ extract_loading: "Распаковка архива",
40
+ extract_long_time: "Может занять длительное время, пожалуйста ждите...",
41
+ theme: "Тема",
42
+ create_empty: "Создать пустой бандл",
43
+ uploading_file: "Загрузка файла",
44
+ fs_restart: "Вы хотите пересобрать бандл используя эту FS?",
45
+ please_wait: "Пожалуйста подождите",
46
+ making_bundle: "Создаем бандл",
47
+ size: "Размер",
48
+ room: "Команта",
49
+ server: "Сервер",
50
+ load_by_url: "Загрузить по ссылке",
51
+ enter_url: "Введите ссылку",
52
+ load: "Загрузить",
53
+ stored: "Сохраненные",
54
+ delete: "Удалить",
55
+ editor: "Редактор",
56
+ download: "Скачать",
57
+ changes: "Сохранения",
58
+ account_not_ready: "Пропустить загрузку сохранений",
59
+ loading_saves: "Загрузка сохранений",
60
+ success: "Успешно",
61
+ unable_to_save: "Ошибка записи",
62
+ not_premium: "Подключить премиум",
63
+ copy_net_link: "Отправьте ссылку на подключение",
64
+ copied: "Скопировано",
65
+ image_rendering: "Обработка изображения",
66
+ read_only_access: "Диск FAT32 открыт только для чтения",
67
+ fix: "Исправить",
68
+ close: "Закрыть",
69
+ cancle: "Отказаться",
70
+ manage: "Управлять",
71
+ unable_to_cancle_subscription: "Не удалось отменить подписку",
72
+ subscription_cancelled: "Подписка отменена",
73
+ hardware: "Аппаратное ускорение",
74
+ net_drives: "Сетевые диски",
75
+ fat_write: "FAT запись",
76
+ },
77
+ en: {
78
+ preloading_sockdrive: "Preloading FAT",
79
+ filter: "Filter",
80
+ add: "Add",
81
+ logout: "Logout",
82
+ please_login: "Please login",
83
+ features: "Features",
84
+ premium: "Premium",
85
+ buy: "Buy",
86
+ experimental_features: "Access to all experimental features",
87
+ cloud_saves: "Cloud saves",
88
+ writeable_fat32: "Writable FAT32",
89
+ net_no_limits: "Change the network region",
90
+ unlock_options: "Unlock all configuration options",
91
+ error: "Oops... Something went wrong...",
92
+ consult_logs: "Please check browser logs",
93
+ bundle_loading: "Bundle loading",
94
+ bundle_config: "Reading config",
95
+ try_free: "days for free",
96
+ upload_file: "Select jsdos/zip file to start...",
97
+ emulation_backend: "Emulation",
98
+ render_backend: "Render",
99
+ render_aspect: "Aspect",
100
+ worker: "Worker thread",
101
+ mouse_sensitivity: "Mouse sens...",
102
+ mouse_lock: "Capture mouse",
103
+ volume: "Volume",
104
+ pause: "Pause",
105
+ dosboxconf_template: "dosbox.conf template:",
106
+ preview: "Preview",
107
+ continue: "Continue",
108
+ skip: "Skip",
109
+ loading: "Loading",
110
+ extract_loading: "Unpacking archive",
111
+ extract_long_time: "Can take long time, please be patient...",
112
+ theme: "Theme",
113
+ create_empty: "Create empty bundle",
114
+ uploading_file: "Uploading file",
115
+ fs_restart: "Do you want to rebuild the bundle using this fs?",
116
+ please_wait: "Please wait",
117
+ making_bundle: "Creating bundle",
118
+ size: "Size",
119
+ room: "Room",
120
+ server: "Server",
121
+ load_by_url: "Load by url",
122
+ enter_url: "Enter url",
123
+ load: "Load",
124
+ stored: "Stored",
125
+ delete: "Delete",
126
+ editor: "Editor",
127
+ download: "Download",
128
+ changes: "Saves",
129
+ account_not_ready: "Skip loading saves",
130
+ loading_saves: "Loading saves",
131
+ success: "Success",
132
+ unable_to_save: "Unable to save",
133
+ not_premium: "Subscribe to premium",
134
+ copy_net_link: "Share this link to connect",
135
+ copied: "Copied",
136
+ image_rendering: "Image rendering",
137
+ read_only_access: "The FAT32 drive is open in read-only mode",
138
+ fix: "Fix",
139
+ close: "Close",
140
+ cancle: "Cancle",
141
+ manage: "Manage",
142
+ unable_to_cancle_subscription: "Unable to cancle subscription",
143
+ subscription_cancelled: "Subscription cancelled",
144
+ hardware: "Hardware acceleration",
145
+ net_drives: "Net drives",
146
+ fat_write: "Writeable FAT",
147
+ },
148
+ };
149
+
150
+ const initialLang = navigator.language.startsWith("ru") ? "ru" : "en";
151
+
152
+ const initialState: {
153
+ lang: "ru" | "en",
154
+ keys: {[key: string]: string},
155
+ } = {
156
+ lang: initialLang,
157
+ keys: translations[initialLang],
158
+ };
159
+
160
+ export type I18NState = typeof initialState;
161
+
162
+ export const i18nSlice = createSlice({
163
+ name: "i18n",
164
+ initialState,
165
+ reducers: {
166
+ setLang: (state, action: { payload: "ru" | "en" }) => {
167
+ state.lang = action.payload;
168
+ state.keys = translations[action.payload];
169
+ },
170
+ },
171
+ });
172
+
173
+ export function useT() {
174
+ const keys = useSelector((state: State) => state.i18n.keys);
175
+ return (key: string) => {
176
+ return keys[key] ?? key;
177
+ };
178
+ };
179
+
package/src/index.css ADDED
@@ -0,0 +1,7 @@
1
+ @import "./base.css";
2
+ @import "./app.css";
3
+ @import "./components/components.css";
4
+ @import "./sidebar/sidebar.css";
5
+ @import "./login/login.css";
6
+ @import "./window/window.css";
7
+ @import "./frame/frame.css";
package/src/load.ts ADDED
@@ -0,0 +1,132 @@
1
+ import { Dispatch, Store, Unsubscribe } from "@reduxjs/toolkit";
2
+ import { DosConfig, Emulators } from "emulators";
3
+ import { dosSlice } from "./store/dos";
4
+ import { bundleFromChanges, bundleFromFile, bundleFromUrl } from "./host/bundle-storage";
5
+ import { uiSlice } from "./store/ui";
6
+ import { editorSlice } from "./store/editor";
7
+ import { getChangesUrl } from "./v8/changes";
8
+ import { storageSlice } from "./store/storage";
9
+ import { getNonSerializableStore } from "./store";
10
+
11
+ declare const emulators: Emulators;
12
+
13
+ export async function loadEmptyBundle(store: Store) {
14
+ await doLoadBundle("empty.jsdos",
15
+ (async () => {
16
+ const bundle = await emulators.bundle();
17
+ return bundle.toUint8Array();
18
+ })(), null, null, store);
19
+
20
+ store.dispatch(uiSlice.actions.frameConf());
21
+ store.dispatch(uiSlice.actions.setEditor(true));
22
+ }
23
+
24
+ export async function loadBundle(bundle: Uint8Array, openConfig: boolean, store: Store) {
25
+ await doLoadBundle("bundle.jsdos", Promise.resolve(bundle),
26
+ null, null, store);
27
+ if (openConfig) {
28
+ store.dispatch(uiSlice.actions.frameConf());
29
+ }
30
+ }
31
+
32
+ export function loadBundleFromFile(file: File, store: Store) {
33
+ return doLoadBundle(file.name,
34
+ bundleFromFile(file, store),
35
+ null, null, store);
36
+ }
37
+
38
+ export async function loadBundleFromConfg(config: DosConfig, store: Store) {
39
+ const nonSerializableStore = getNonSerializableStore(store);
40
+ const dispatch = store.dispatch;
41
+ nonSerializableStore.loadedBundle = null;
42
+
43
+ dispatch(editorSlice.actions.init(config));
44
+ syncWithConfig(config, dispatch);
45
+
46
+ nonSerializableStore.loadedBundle = {
47
+ bundleUrl: null,
48
+ bundleChangesUrl: null,
49
+ bundle: config,
50
+ bundleChanges: null,
51
+ };
52
+ dispatch(dosSlice.actions.bndReady({}));
53
+ }
54
+
55
+ export async function loadBundleFromUrl(url: string, store: Store) {
56
+ const owner = store.getState().auth.account?.email ?? "guest";
57
+ const changesUrl = await getChangesUrl(owner, url);
58
+ return doLoadBundle(url,
59
+ bundleFromUrl(url, store),
60
+ changesProducer(changesUrl, store),
61
+ url,
62
+ store);
63
+ }
64
+
65
+ async function doLoadBundle(bundleName: string,
66
+ bundlePromise: Promise<Uint8Array>,
67
+ bundleChangesPromise: (ReturnType<typeof changesProducer>) | null,
68
+ bundleUrl: string | null,
69
+ store: Store) {
70
+ const nonSerializableStore = getNonSerializableStore(store);
71
+ const dispatch = store.dispatch;
72
+ nonSerializableStore.loadedBundle = null;
73
+
74
+
75
+ dispatch(dosSlice.actions.bndLoad(bundleName));
76
+
77
+ const bundle = await bundlePromise;
78
+ dispatch(storageSlice.actions.ready());
79
+ const bundleChanges = await bundleChangesPromise;
80
+ dispatch(dosSlice.actions.bndConfig());
81
+
82
+ const config = await emulators.bundleConfig(bundle);
83
+ dispatch(editorSlice.actions.init(config));
84
+ if (config === null) {
85
+ dispatch(uiSlice.actions.frameConf());
86
+ } else {
87
+ syncWithConfig(config, dispatch);
88
+ }
89
+
90
+ nonSerializableStore.loadedBundle = {
91
+ bundleUrl,
92
+ bundleChangesUrl: bundleChanges?.url ?? null,
93
+ bundle,
94
+ bundleChanges: bundleChanges?.bundle ?? null,
95
+ };
96
+ dispatch(dosSlice.actions.bndReady({}));
97
+ }
98
+
99
+ async function changesProducer(bundleUrl: string, store: Store): Promise<{
100
+ url: string,
101
+ bundle: Uint8Array | null,
102
+ }> {
103
+ if (!store.getState().auth.ready) {
104
+ await new Promise<void>((resolve) => {
105
+ let unsubscirbe: Unsubscribe | null = null;
106
+ const updateFn = () => {
107
+ if (store.getState().auth.ready) {
108
+ unsubscirbe!();
109
+ resolve();
110
+ }
111
+ };
112
+ unsubscirbe = store.subscribe(updateFn);
113
+ });
114
+ }
115
+
116
+ const account = store.getState().auth.account;
117
+ const owner = account?.email ?? "guest";
118
+ const url = getChangesUrl(owner, bundleUrl);
119
+ const bundle = await bundleFromChanges(url, account, store);
120
+ return {
121
+ url,
122
+ bundle,
123
+ };
124
+ }
125
+
126
+ function syncWithConfig(config: DosConfig, dispatch: Dispatch) {
127
+ if (config.dosboxConf.indexOf("sockdrive") >= 0) {
128
+ dispatch(dosSlice.actions.dosBackendLocked(true));
129
+ dispatch(dosSlice.actions.dosBackend("dosboxX"));
130
+ }
131
+ dispatch(dosSlice.actions.mouseCapture(config.dosboxConf.indexOf("autolock=true") >= 0));
132
+ }
@@ -0,0 +1,9 @@
1
+ .jsdos-rso {
2
+ .login-widget {
3
+ @apply absolute z-40 left-12 top-0 bottom-0 right-0 bg-white;
4
+
5
+ iframe {
6
+ @apply w-full h-full p-0 m-0 border-0;
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,77 @@
1
+ import { useEffect, useRef } from "preact/hooks";
2
+ import { useDispatch, useSelector } from "react-redux";
3
+ import { State } from "../store";
4
+ import { uiSlice } from "../store/ui";
5
+ import { CloseButton } from "../components/close-button";
6
+ import { authentificator } from "../v8/config";
7
+ import { Account, authSlice, postAuthMessage } from "../store/auth";
8
+ import { havePremium } from "../v8/subscriptions";
9
+
10
+ export function Login() {
11
+ const iframeRef = useRef<HTMLIFrameElement>(null);
12
+ const activeAccount = useSelector((state: State) => state.auth.account);
13
+ const visible = useSelector((state: State) => state.ui.modal) === "login";
14
+ const dispatch = useDispatch();
15
+
16
+ useEffect(() => {
17
+ if (activeAccount != null) {
18
+ return;
19
+ }
20
+
21
+ const iframe = iframeRef.current;
22
+ if (iframe === null) {
23
+ dispatch(authSlice.actions.logout({}));
24
+ dispatch(authSlice.actions.ready());
25
+ console.error("Unable to inialize authentificator!");
26
+ return;
27
+ }
28
+
29
+ async function onAuthMessage(e: any) {
30
+ if (e.data.action === "auth/ready") {
31
+ postAuthMessage("auth/authenicate");
32
+ } else if (e.data.action === "auth/authenicate") {
33
+ const account = e.data.account as Account | null;
34
+ if (account !== null) {
35
+ try {
36
+ account.premium = await havePremium(account.token.access_token);
37
+ } catch (e: any) {
38
+ console.error(e);
39
+ account.premium = false;
40
+ }
41
+ dispatch(authSlice.actions.login(e.data.account));
42
+ } else {
43
+ dispatch(authSlice.actions.logout({}));
44
+ dispatch(authSlice.actions.ready());
45
+ }
46
+ }
47
+ }
48
+
49
+ window.addEventListener("message", onAuthMessage);
50
+ iframe.src = authentificator;
51
+
52
+ return () => {
53
+ window.removeEventListener("message", onAuthMessage);
54
+ };
55
+ }, []);
56
+
57
+ useEffect(() => {
58
+ const iframe = iframeRef.current;
59
+ if (iframe === null) {
60
+ return;
61
+ }
62
+
63
+ postAuthMessage("auth/login");
64
+ }, [visible]);
65
+
66
+ return <div
67
+ id={"login"}
68
+ class={
69
+ "login-widget " + (visible ? "block" : "hidden")
70
+ }>
71
+ <iframe id="authentificator" ref={iframeRef} />
72
+ <CloseButton
73
+ class="absolute z-40 right-24 top-10"
74
+ onClose={() => dispatch(uiSlice.actions.modalNone()) }
75
+ />
76
+ </div>;
77
+ }