mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-12 23:04:52 +09:00
Merge pull request #524 from Hornwitser/typescript-plugin-web
Port plugin web code to TypeScript
This commit is contained in:
80 files changed
+312
-193
No files matched your search
+11
-2
@@ -22,6 +22,7 @@ module.exports = {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-useless-constructor": "error",
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"@typescript-eslint/triple-slash-reference": "off",
|
||||
"prefer-const": "off",
|
||||
},
|
||||
},
|
||||
@@ -39,7 +40,11 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
{
|
||||
"files": ["packages/web_ui/src/**/*.ts?(x)", "packages/lib/browser.js", "plugins/*/web/**/*.ts?(x)"],
|
||||
"files": [
|
||||
"packages/web_ui/src/**/*.ts?(x)",
|
||||
"packages/lib/browser.ts",
|
||||
"{packages,plugins}/*/web/**/*.ts?(x)",
|
||||
],
|
||||
"env": {
|
||||
"browser": true,
|
||||
},
|
||||
@@ -315,7 +320,11 @@ module.exports = {
|
||||
"space-in-parens": ["error", "never"],
|
||||
"space-infix-ops": "off",
|
||||
"space-unary-ops": "error",
|
||||
"spaced-comment": ["error", "always"],
|
||||
"spaced-comment": ["error", "always", {
|
||||
"line": {
|
||||
"markers": ["/"],
|
||||
},
|
||||
}],
|
||||
"strict": "error",
|
||||
"switch-colon-spacing": "error",
|
||||
"symbol-description": "error",
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
webpack.config.js
|
||||
dist/web/build
|
||||
@@ -93,20 +93,8 @@ async function getMetrics(req: Request, res: Response, next: any) {
|
||||
}
|
||||
|
||||
|
||||
export type PluginWebApi = {
|
||||
name: string;
|
||||
version: string;
|
||||
enabled: boolean;
|
||||
loaded: boolean;
|
||||
web: {
|
||||
main: string;
|
||||
error?: string;
|
||||
};
|
||||
requirePath: string;
|
||||
}
|
||||
|
||||
function getPlugins(req: Request, res: Response) {
|
||||
let plugins: PluginWebApi[] = [];
|
||||
let plugins: lib.PluginWebApi[] = [];
|
||||
for (let pluginInfo of req.app.locals.controller.pluginInfos) {
|
||||
let name = pluginInfo.name;
|
||||
let loaded = req.app.locals.controller.plugins.has(name);
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"references": [
|
||||
{ "path": "../lib" },
|
||||
{ "path": "../web_ui/tsconfig.web.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"rootDir": "..",
|
||||
"outDir": "dist/web/build",
|
||||
}
|
||||
}
|
||||
@@ -17,22 +17,33 @@
|
||||
margin: auto;
|
||||
margin-top: 1em;
|
||||
width: fit-content;
|
||||
white-space: pre;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
window.webRoot = new URL("__WEB_ROOT__", document.location).pathname;
|
||||
window.staticRoot = new URL("__STATIC_ROOT__", document.location).pathname;
|
||||
function scriptError() {
|
||||
let bootstrapMsg = document.getElementById("bootstrap-msg");
|
||||
bootstrapMsg.textContent = "Error loading main bundle, see web console for details.";
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root">
|
||||
<div id="bootstrap-msg">Loading web interface...</div>
|
||||
</div>
|
||||
<script src="__STATIC_ROOT____MAIN_BUNDLE__" defer onerror="scriptError()"></script>
|
||||
<script id="script-bundle" src="__STATIC_ROOT____MAIN_BUNDLE__" defer></script>
|
||||
<script>
|
||||
webRoot = new URL("__WEB_ROOT__", document.location).pathname;
|
||||
staticRoot = new URL("__STATIC_ROOT__", document.location).pathname;
|
||||
function scriptError(err) {
|
||||
console.error(err);
|
||||
let bootstrapMsg = document.getElementById("bootstrap-msg");
|
||||
if (bootstrapMsg) {
|
||||
let errorMsg = err;
|
||||
if (err?.message) {
|
||||
errorMsg = `${err?.constructor?.name ?? "Error"}: ${err.message}`;
|
||||
}
|
||||
bootstrapMsg.textContent = `${errorMsg}\nSee web console for details.`;
|
||||
}
|
||||
}
|
||||
const bundleScript = document.getElementById("script-bundle");
|
||||
bundleScript.addEventListener("error", () => scriptError("Main bundle failed to load."));
|
||||
window.addEventListener("error", event => scriptError(event.error));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,3 +0,0 @@
|
||||
import("@clusterio/web_ui").then(webUi => {
|
||||
return webUi.bootstrap();
|
||||
}).catch(console.error);
|
||||
@@ -0,0 +1,5 @@
|
||||
declare function scriptError(err: any): void;
|
||||
import("@clusterio/web_ui")
|
||||
.then(webUi => webUi.bootstrap())
|
||||
.catch(scriptError)
|
||||
;
|
||||
@@ -7,7 +7,7 @@ const common = require("@clusterio/web_ui/webpack.common");
|
||||
|
||||
module.exports = (env = {}) => merge(common(env), {
|
||||
context: __dirname,
|
||||
entry: "./web/index.jsx",
|
||||
entry: "./web/index.tsx",
|
||||
devServer: {
|
||||
contentBase: "./dist/web",
|
||||
},
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/src/data/message_validate.js
|
||||
File renamed without changes.
@@ -1,16 +0,0 @@
|
||||
export * from "./dist/src/config";
|
||||
export * from "./dist/src/data";
|
||||
export * from "./dist/src/errors";
|
||||
export * from "./dist/src/factorio/exchange_string";
|
||||
export * from "./dist/src/helpers";
|
||||
export * from "./dist/src/ini";
|
||||
export * from "./dist/src/link";
|
||||
export * from "./dist/src/logging";
|
||||
export * from "./dist/src/plugin";
|
||||
export * from "./dist/src/schema";
|
||||
export * from "./dist/src/users";
|
||||
export * from "./dist/src/subscriptions";
|
||||
|
||||
export { default as ExponentialBackoff } from "./dist/src/ExponentialBackoff";
|
||||
export { default as PlayerStats } from "./dist/src/PlayerStats";
|
||||
export { default as RateLimiter } from "./dist/src/RateLimiter";
|
||||
@@ -0,0 +1,17 @@
|
||||
export * from "./src/api";
|
||||
export * from "./src/config";
|
||||
export * from "./src/data";
|
||||
export * from "./src/errors";
|
||||
export * from "./src/factorio/exchange_string";
|
||||
export * from "./src/helpers";
|
||||
export * from "./src/ini";
|
||||
export * from "./src/link";
|
||||
export * from "./src/logging";
|
||||
export * from "./src/plugin";
|
||||
export * from "./src/schema";
|
||||
export * from "./src/users";
|
||||
export * from "./src/subscriptions";
|
||||
|
||||
export { default as ExponentialBackoff } from "./src/ExponentialBackoff";
|
||||
export { default as PlayerStats } from "./src/PlayerStats";
|
||||
export { default as RateLimiter } from "./src/RateLimiter";
|
||||
@@ -3,6 +3,7 @@
|
||||
* @module lib
|
||||
*/
|
||||
export * from "./build_mod";
|
||||
export * from "./src/api";
|
||||
export * from "./src/command";
|
||||
export * from "./src/config";
|
||||
export * from "./src/data";
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"compile_validator": "node scripts/compile_validator.js"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"browser": "browser.js",
|
||||
"browser": "browser.ts",
|
||||
"keywords": [
|
||||
"factorio"
|
||||
],
|
||||
|
||||
@@ -22,7 +22,11 @@ const ajv = new Ajv({
|
||||
});
|
||||
const validate = ajv.compile(Message.jsonSchema);
|
||||
let moduleCode = standaloneCode(ajv, validate);
|
||||
fs.outputFileSync(
|
||||
path.join(__dirname, "../dist/src/data/message_validate.js"),
|
||||
`// generated by scripts/compile_validator.js\n// @ts-nocheck\n/* eslint-disable */\n${moduleCode}`
|
||||
);
|
||||
function write(output) {
|
||||
fs.outputFileSync(
|
||||
path.join(__dirname, output),
|
||||
`// generated by scripts/compile_validator.js\n// @ts-nocheck\n/* eslint-disable */\n${moduleCode}`
|
||||
);
|
||||
}
|
||||
write("../dist/src/data/message_validate.js");
|
||||
write("../src/data/message_validate.js");
|
||||
@@ -0,0 +1,11 @@
|
||||
export type PluginWebApi = {
|
||||
name: string;
|
||||
version: string;
|
||||
enabled: boolean;
|
||||
loaded: boolean;
|
||||
web: {
|
||||
main: string;
|
||||
error?: string;
|
||||
};
|
||||
requirePath: string;
|
||||
}
|
||||
Vendored
+3
-5
@@ -1,12 +1,10 @@
|
||||
declare module "*.png" {
|
||||
const value: import("react-native").ImageSourcePropType;
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
|
||||
declare interface Window {
|
||||
webRoot: string;
|
||||
staticRoot: string;
|
||||
}
|
||||
declare const webRoot: string;
|
||||
declare const staticRoot: string;
|
||||
|
||||
declare const __webpack_init_sharing__: (shareScope: string) => Promise<void>;
|
||||
declare const __webpack_share_scopes__: { default: any };
|
||||
@@ -5,6 +5,7 @@
|
||||
"license": "MIT",
|
||||
"version": "2.0.0-alpha.13",
|
||||
"browser": "src/index.ts",
|
||||
"main": "dist/src/index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type React from "react";
|
||||
import type { AccountRole, Link, Logger, PluginWebpackEnvInfo } from "@clusterio/lib";
|
||||
import type { AccountRole, Logger, PluginWebpackEnvInfo } from "@clusterio/lib";
|
||||
import type { Control } from "./util/websocket";
|
||||
|
||||
/**
|
||||
* Plugin supplied login form
|
||||
@@ -19,7 +20,7 @@ export interface PluginLoginForm {
|
||||
* supplied the setToken function via its props which should be called
|
||||
* when an authentication token is aquired via this form.
|
||||
*/
|
||||
Component: typeof React.Component;
|
||||
Component: React.ComponentType<{ setToken(token: string): void }>;
|
||||
};
|
||||
|
||||
export type UserAccount = {
|
||||
@@ -77,11 +78,6 @@ export default class BaseWebPlugin {
|
||||
* Contents of the plugin's package.json file
|
||||
*/
|
||||
package: any;
|
||||
/**
|
||||
* Control link to the controller, not available until the
|
||||
* connect event in onControllerConnectionEvent is signaled.
|
||||
*/
|
||||
control?: Link;
|
||||
/**
|
||||
* Logger for this plugin
|
||||
*
|
||||
@@ -152,6 +148,12 @@ export default class BaseWebPlugin {
|
||||
* The plugin's own info module
|
||||
*/
|
||||
public info: PluginWebpackEnvInfo,
|
||||
/**
|
||||
* Control link to the controller
|
||||
*
|
||||
* Not connected at the time init is invoked.
|
||||
*/
|
||||
public control: Control,
|
||||
logger: Logger,
|
||||
) {
|
||||
this.package = packageData;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import "./index.css";
|
||||
import "./style.css";
|
||||
|
||||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
|
||||
import * as lib from "@clusterio/lib";
|
||||
import type { PluginWebApi } from "@clusterio/controller/src/routes";
|
||||
|
||||
import App from "./components/App";
|
||||
import BaseWebPlugin from "./BaseWebPlugin";
|
||||
@@ -28,8 +27,8 @@ async function loadScript(url: string) {
|
||||
}
|
||||
|
||||
async function loadPluginInfos(): Promise<lib.PluginWebpackEnvInfo[]> {
|
||||
let response = await fetch(`${window.webRoot}api/plugins`);
|
||||
let pluginList: PluginWebApi[];
|
||||
let response = await fetch(`${webRoot}api/plugins`);
|
||||
let pluginList: lib.PluginWebApi[];
|
||||
if (response.ok) {
|
||||
pluginList = await response.json();
|
||||
|
||||
@@ -46,7 +45,7 @@ async function loadPluginInfos(): Promise<lib.PluginWebpackEnvInfo[]> {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
await loadScript(`${window.webRoot}${meta.web.main}`);
|
||||
await loadScript(`${webRoot}${meta.web.main}`);
|
||||
let container: any = (window as { [key: string]: any })[`plugin_${meta.name}`];
|
||||
if (!container) {
|
||||
throw new Error(`Plugin did not expose its container via plugin_${meta.name}`);
|
||||
@@ -68,8 +67,8 @@ async function loadPluginInfos(): Promise<lib.PluginWebpackEnvInfo[]> {
|
||||
return pluginInfos;
|
||||
}
|
||||
|
||||
async function loadPlugins(pluginInfos: lib.PluginWebpackEnvInfo[]) {
|
||||
let plugins = new Map();
|
||||
async function loadPlugins(pluginInfos: lib.PluginWebpackEnvInfo[], control: Control) {
|
||||
let plugins = new Map<string, BaseWebPlugin>();
|
||||
for (let pluginInfo of pluginInfos) {
|
||||
if (!pluginInfo.enabled) {
|
||||
continue;
|
||||
@@ -84,7 +83,7 @@ async function loadPlugins(pluginInfos: lib.PluginWebpackEnvInfo[]) {
|
||||
WebPluginClass = webModule.WebPlugin;
|
||||
}
|
||||
|
||||
let plugin = new WebPluginClass(pluginInfo.container, pluginInfo.package, pluginInfo, logger);
|
||||
let plugin = new WebPluginClass(pluginInfo.container, pluginInfo.package, pluginInfo, control, logger);
|
||||
await plugin.init();
|
||||
plugins.set(pluginInfo.name, plugin);
|
||||
|
||||
@@ -107,11 +106,11 @@ export default async function bootstrap() {
|
||||
lib.registerPluginMessages(pluginInfos);
|
||||
lib.registerPluginConfigGroups(pluginInfos);
|
||||
lib.finalizeConfigs();
|
||||
let plugins = await loadPlugins(pluginInfos);
|
||||
|
||||
let wsUrl = new URL(window.webRoot, document.location.href);
|
||||
let wsUrl = new URL(webRoot, document.location.href);
|
||||
let controlConnector = new ControlConnector(wsUrl.href, 120, undefined);
|
||||
let control = new Control(controlConnector, plugins);
|
||||
let control = new Control(controlConnector);
|
||||
control.plugins = await loadPlugins(pluginInfos, control);
|
||||
|
||||
const root = createRoot(document.getElementById("root") as HTMLDivElement);
|
||||
root.render(<App control={control}/>);
|
||||
|
||||
@@ -109,7 +109,7 @@ export default function App(props: AppProps) {
|
||||
theme={{ algorithm: theme.darkAlgorithm }}
|
||||
>
|
||||
<ControlContext.Provider value={props.control}>
|
||||
<BrowserRouter basename={window.webRoot}>
|
||||
<BrowserRouter basename={webRoot}>
|
||||
{page}
|
||||
</BrowserRouter>
|
||||
</ControlContext.Provider>
|
||||
|
||||
@@ -3,7 +3,6 @@ import { useNavigate } from "react-router-dom";
|
||||
import { Button, Form, InputNumber, Modal, Table, Tag, Typography } from "antd";
|
||||
import CopyOutlined from "@ant-design/icons/lib/icons/CopyOutlined";
|
||||
|
||||
import type { PluginWebApi } from "@clusterio/controller/src/routes";
|
||||
import * as lib from "@clusterio/lib";
|
||||
|
||||
import { useAccount } from "../model/account";
|
||||
@@ -23,10 +22,10 @@ function GenerateHostTokenButton() {
|
||||
let [token, setToken] = useState<string|null>(null);
|
||||
let [hostId, setHostId] = useState<number|null>(null);
|
||||
let [form] = Form.useForm();
|
||||
let [pluginList, setPluginList] = useState<PluginWebApi[]>([]);
|
||||
let [pluginList, setPluginList] = useState<lib.PluginWebApi[]>([]);
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
let response = await fetch(`${window.webRoot}api/plugins`);
|
||||
let response = await fetch(`${webRoot}api/plugins`);
|
||||
if (response.ok) {
|
||||
const plugins = await response.json();
|
||||
setPluginList(plugins);
|
||||
|
||||
@@ -623,7 +623,7 @@ function useExportedAsset(modPack: lib.ModPack, asset: "settings"|"locale"): Map
|
||||
return;
|
||||
}
|
||||
|
||||
let response = await fetch(`${window.staticRoot}static/${assetFilename}`);
|
||||
let response = await fetch(`${staticRoot}static/${assetFilename}`);
|
||||
if (response.ok) {
|
||||
let data = await response.json();
|
||||
setAssetData(asset === "locale" ? new Map(data) : data);
|
||||
|
||||
@@ -140,7 +140,7 @@ export default function ModsPage() {
|
||||
control.send(
|
||||
new lib.ModDownloadRequest(mod.name, mod.version)
|
||||
).then((streamId: string) => {
|
||||
let url = new URL(window.webRoot, document.location.origin);
|
||||
let url = new URL(webRoot, document.location.origin);
|
||||
url.pathname += `api/stream/${streamId}`;
|
||||
document.location = url.href;
|
||||
}).catch(
|
||||
@@ -175,7 +175,7 @@ export default function ModsPage() {
|
||||
headers={{
|
||||
"X-Access-Token": control.connector.token||"",
|
||||
}}
|
||||
action={`${window.webRoot}api/upload-mod`}
|
||||
action={`${webRoot}api/upload-mod`}
|
||||
>
|
||||
<Button icon={<UploadOutlined/>}>Upload</Button>
|
||||
</Upload>;
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useEffect, useContext, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { Alert, Descriptions, Spin } from "antd";
|
||||
|
||||
import type { PluginWebApi } from "@clusterio/controller/src/routes";
|
||||
import type { PluginWebApi } from "@clusterio/lib";
|
||||
|
||||
import ControlContext from "./ControlContext";
|
||||
import PageLayout from "./PageLayout";
|
||||
@@ -16,7 +16,7 @@ export default function PluginViewPage() {
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
let response = await fetch(`${window.webRoot}api/plugins`);
|
||||
let response = await fetch(`${webRoot}api/plugins`);
|
||||
if (response.ok) {
|
||||
setPluginList(await response.json());
|
||||
} else {
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useContext, useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Table } from "antd";
|
||||
|
||||
import type { PluginWebApi } from "@clusterio/controller/src/routes";
|
||||
import type { PluginWebApi } from "@clusterio/lib";
|
||||
|
||||
import notify from "../util/notify";
|
||||
import ControlContext from "./ControlContext";
|
||||
@@ -18,7 +18,7 @@ export default function PluginsPage() {
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
let response = await fetch(`${window.webRoot}api/plugins`);
|
||||
let response = await fetch(`${webRoot}api/plugins`);
|
||||
if (response.ok) {
|
||||
setPluginList(await response.json());
|
||||
} else {
|
||||
|
||||
@@ -268,7 +268,7 @@ export default function SavesList(props: { instance: InstanceState }) {
|
||||
control.send(
|
||||
new lib.InstanceDownloadSaveRequest(props.instance.id!, save.name)
|
||||
).then(streamId => {
|
||||
let url = new URL(window.webRoot, document.location.href);
|
||||
let url = new URL(webRoot, document.location.href);
|
||||
url.pathname += `api/stream/${streamId}`;
|
||||
document.location.assign(url);
|
||||
}).catch(
|
||||
@@ -339,7 +339,7 @@ export default function SavesList(props: { instance: InstanceState }) {
|
||||
instance_id: props.instance.id,
|
||||
},
|
||||
showUploadList: false,
|
||||
action: `${window.webRoot}api/upload-save`,
|
||||
action: `${webRoot}api/upload-save`,
|
||||
onChange,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/// <reference path="../global.d.ts" />
|
||||
export { default as bootstrap } from "./bootstrap";
|
||||
export { default as notify, notifyErrorHandler } from "./util/notify";
|
||||
export { default as BaseWebPlugin } from "./BaseWebPlugin";
|
||||
export * from "./util/websocket";
|
||||
|
||||
export * from "./model/instance";
|
||||
export * from "./model/host";
|
||||
|
||||
@@ -7,6 +7,7 @@ type Metadata = {
|
||||
x: number;
|
||||
y: number;
|
||||
size: number;
|
||||
localised_name?: any[];
|
||||
};
|
||||
let itemMetadataCache: Map<string, Metadata> | null = null;
|
||||
export function useItemMetadata() {
|
||||
@@ -22,7 +23,7 @@ export function useItemMetadata() {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let response = await fetch(`${window.staticRoot}static/${exportManifest.assets["item-metadata"]}`);
|
||||
let response = await fetch(`${staticRoot}static/${exportManifest.assets["item-metadata"]}`);
|
||||
if (response.ok) {
|
||||
let data = await response.json();
|
||||
itemMetadataCache = new Map(data);
|
||||
@@ -31,7 +32,7 @@ export function useItemMetadata() {
|
||||
for (let [name, meta] of itemMetadataCache) {
|
||||
style.sheet!.insertRule(`\
|
||||
.item-${CSS.escape(name)} {
|
||||
background-image: url("${window.staticRoot}static/${exportManifest.assets["item-spritesheet"]}");
|
||||
background-image: url("${staticRoot}static/${exportManifest.assets["item-spritesheet"]}");
|
||||
background-repeat: no-repeat;
|
||||
background-position: -${meta.x}px -${meta.y}px;
|
||||
height: ${meta.size}px;
|
||||
|
||||
@@ -12,7 +12,7 @@ export function useLocale(): Map<string, string> {
|
||||
if (!exportManifest?.assets?.["locale"]) {
|
||||
return;
|
||||
}
|
||||
let response = await fetch(`${window.staticRoot}static/${exportManifest.assets["locale"]}`);
|
||||
let response = await fetch(`${staticRoot}static/${exportManifest.assets["locale"]}`);
|
||||
if (response.ok) {
|
||||
let data = await response.json();
|
||||
localeCache = new Map(data);
|
||||
|
||||
File renamed without changes.
@@ -52,6 +52,9 @@ export class Control extends lib.Link {
|
||||
/** Roles of the account this control link is connected as. */
|
||||
accountRoles: lib.AccountRole[] | null = null;
|
||||
|
||||
/** Plugins loaded in the web interface */
|
||||
public plugins = new Map<string, BaseWebPlugin>();
|
||||
|
||||
/** Updates not handled by the subscription service */
|
||||
accountUpdateHandlers: accountHandler[] = [];
|
||||
logHandlers: Map<lib.LogFilter, logHandler[]> = new Map();
|
||||
@@ -85,14 +88,9 @@ export class Control extends lib.Link {
|
||||
|
||||
constructor(
|
||||
connector: ControlConnector,
|
||||
public plugins: Map<string, BaseWebPlugin>,
|
||||
) {
|
||||
super(connector);
|
||||
|
||||
for (let plugin of plugins.values()) {
|
||||
plugin.control = this;
|
||||
}
|
||||
|
||||
this.connector.on("connect", data => {
|
||||
this.accountName = data.account.name;
|
||||
this.accountRoles = data.account.roles;
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"include": [ "src/**/*.tsx", "src/**/*.ts", "package.json", "global.d.ts" ],
|
||||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
"lib": ["dom", "es2023"],
|
||||
"module": "commonjs",
|
||||
"target": "es2022",
|
||||
"rootDir": "./",
|
||||
"outDir": "dist",
|
||||
"composite": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"alwaysStrict": true,
|
||||
"noImplicitAny": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
"noEmitOnError": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"references": [
|
||||
{ "path": "../lib" },
|
||||
],
|
||||
"include": [ "src/**/*.tsx", "src/**/*.ts", "package.json", "global.d.ts" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,10 @@ module.exports = (env = {}) => ({
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: require.resolve("ts-loader"),
|
||||
options: {
|
||||
configFile: "tsconfig.web.json",
|
||||
projectReferences: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
webpack.config.js
|
||||
dist/web/build
|
||||
@@ -2,7 +2,7 @@ import type * as lib from "@clusterio/lib";
|
||||
|
||||
import { ChatEvent } from "./messages";
|
||||
|
||||
const info: lib.PluginDeclaration = {
|
||||
export default {
|
||||
name: "global_chat",
|
||||
title: "Global Chat",
|
||||
description: "Forwards chat between instances.",
|
||||
@@ -12,6 +12,4 @@ const info: lib.PluginDeclaration = {
|
||||
messages: [
|
||||
ChatEvent,
|
||||
],
|
||||
};
|
||||
|
||||
export default info;
|
||||
} satisfies lib.PluginDeclaration;
|
||||
@@ -24,6 +24,7 @@
|
||||
"@clusterio/lib": "^2.0.0-alpha.13",
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.13",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/react": "^18.2.21",
|
||||
"typescript": "^5.1.6",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-cli": "^5.1.4",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.web.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/web/build",
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -7,7 +7,7 @@ const common = require("@clusterio/web_ui/webpack.common");
|
||||
|
||||
module.exports = (env = {}) => merge(common(env), {
|
||||
context: __dirname,
|
||||
entry: "./web/index.jsx",
|
||||
entry: "./web/index.tsx",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist", "web"),
|
||||
},
|
||||
@@ -16,7 +16,7 @@ module.exports = (env = {}) => merge(common(env), {
|
||||
name: "global_chat",
|
||||
library: { type: "var", name: "plugin_global_chat" },
|
||||
exposes: {
|
||||
"./info": "./dist/plugin/info.js",
|
||||
"./info": "./info.ts",
|
||||
"./package.json": "./package.json",
|
||||
},
|
||||
shared: {
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
webpack.config.js
|
||||
dist/web/build
|
||||
@@ -40,7 +40,7 @@ lib.definePermission({
|
||||
grantByDefault: true,
|
||||
});
|
||||
|
||||
const info: lib.PluginDeclaration = {
|
||||
export default {
|
||||
name: "inventory_sync",
|
||||
title: "Inventory sync",
|
||||
description: "Synchronizes players inventories between instances",
|
||||
@@ -60,6 +60,4 @@ const info: lib.PluginDeclaration = {
|
||||
],
|
||||
webEntrypoint: "./web",
|
||||
routes: ["/inventory"],
|
||||
};
|
||||
|
||||
export default info;
|
||||
} satisfies lib.PluginDeclaration;
|
||||
@@ -139,7 +139,7 @@ export class DownloadRequest {
|
||||
}
|
||||
}
|
||||
|
||||
class DatabaseStatsResponse {
|
||||
export class DatabaseStatsResponse {
|
||||
constructor(
|
||||
public databaseSize: number,
|
||||
public databaseEntries: number,
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.13",
|
||||
"@types/fs-extra": "^11.0.1",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/react": "^18.2.21",
|
||||
"antd": "^5.7.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.web.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "instance.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/web/build",
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
|
||||
import { BaseWebPlugin, PageLayout, ControlContext } from "@clusterio/web_ui";
|
||||
import { DatabaseStatsRequest } from "../dist/plugin/messages";
|
||||
import { DatabaseStatsRequest, DatabaseStatsResponse } from "../messages";
|
||||
|
||||
import "./index.css";
|
||||
import "./style.css";
|
||||
|
||||
function InventoryPage() {
|
||||
let control = useContext(ControlContext);
|
||||
|
||||
let [statsData, updateStatsData] = useState();
|
||||
let [statsData, updateStatsData] = useState<DatabaseStatsResponse>();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -38,10 +38,4 @@ export class WebPlugin extends BaseWebPlugin {
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
onControllerConnectionEvent(event) {
|
||||
if (event === "connect") {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -7,7 +7,7 @@ const common = require("@clusterio/web_ui/webpack.common");
|
||||
|
||||
module.exports = (env = {}) => merge(common(env), {
|
||||
context: __dirname,
|
||||
entry: "./web/index.jsx",
|
||||
entry: "./web/index.tsx",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist", "web"),
|
||||
},
|
||||
@@ -16,9 +16,9 @@ module.exports = (env = {}) => merge(common(env), {
|
||||
name: "inventory_sync",
|
||||
library: { type: "var", name: "plugin_inventory_sync" },
|
||||
exposes: {
|
||||
"./info": "./dist/plugin/info.js",
|
||||
"./info": "./info.ts",
|
||||
"./package.json": "./package.json",
|
||||
"./web": "./web/index.jsx",
|
||||
"./web": "./web/index.tsx",
|
||||
},
|
||||
shared: {
|
||||
"@clusterio/lib": { import: false },
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
webpack.config.js
|
||||
dist/web/build
|
||||
@@ -46,10 +46,10 @@ export class ControllerPlugin extends BaseControllerPlugin {
|
||||
}, 60e3).unref();
|
||||
|
||||
this.controller.app.get("/api/player_auth/servers", (req: Request, res: Response) => {
|
||||
let servers = [];
|
||||
let servers: string[] = [];
|
||||
for (let instance of this.controller.instances!.values()) {
|
||||
if (instance.status === "running" && instance.config.get("player_auth.load_plugin")) {
|
||||
servers.push(instance.config.get("factorio.settings")["name"] || "unnamed server");
|
||||
servers.push(instance.config.get("factorio.settings")["name"] as string || "unnamed server");
|
||||
}
|
||||
}
|
||||
res.send(servers);
|
||||
|
||||
@@ -20,7 +20,7 @@ ControllerConfigGroup.define({
|
||||
});
|
||||
ControllerConfigGroup.finalize();
|
||||
|
||||
const info: lib.PluginDeclaration = {
|
||||
export default {
|
||||
name: "player_auth",
|
||||
title: "Player Auth",
|
||||
description: "Provides authentication to the cluster via logging into a Factorio server.",
|
||||
@@ -33,6 +33,4 @@ const info: lib.PluginDeclaration = {
|
||||
messages.FetchPlayerCodeRequest,
|
||||
messages.SetVerifyCodeRequest,
|
||||
],
|
||||
};
|
||||
|
||||
export default info;
|
||||
} satisfies lib.PluginDeclaration;
|
||||
@@ -31,6 +31,7 @@
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jsonwebtoken": "^9.0.2",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/react": "^18.2.21",
|
||||
"antd": "^5.7.3",
|
||||
"phin": "^3.7.0",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.web.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/web/build",
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,20 @@ import React, { useEffect, useState } from "react";
|
||||
import { BaseWebPlugin, notifyErrorHandler } from "@clusterio/web_ui";
|
||||
import { Button, Form, Input, Spin, Typography } from "antd";
|
||||
|
||||
import "./index.css";
|
||||
import "./style.css";
|
||||
|
||||
const { Paragraph, Text } = Typography;
|
||||
|
||||
interface LoginFormProps {
|
||||
setToken(token: string): void,
|
||||
}
|
||||
|
||||
function LoginForm(props) {
|
||||
let [servers, setServers] = useState(null);
|
||||
let [playerCode, setPlayerCode] = useState(null);
|
||||
let [playerCodeError, setPlayerCodeError] = useState(null);
|
||||
let [verifyCode, setVerifyCode] = useState(null);
|
||||
let [verifyToken, setVerifyToken] = useState(null);
|
||||
function LoginForm(props: LoginFormProps) {
|
||||
let [servers, setServers] = useState<string[] | null>(null);
|
||||
let [playerCode, setPlayerCode] = useState<string | null>(null);
|
||||
let [playerCodeError, setPlayerCodeError] = useState<string | null>(null);
|
||||
let [verifyCode, setVerifyCode] = useState<string | undefined>();
|
||||
let [verifyToken, setVerifyToken] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -137,7 +140,7 @@ function LoginForm(props) {
|
||||
<li>
|
||||
<Paragraph>Enter this code into the in-game dialog:</Paragraph>
|
||||
<Paragraph>
|
||||
<Input disabled={verifyCode === null} style={{ width: "10em" }} value={verifyCode} />
|
||||
<Input disabled={verifyCode === undefined} style={{ width: "10em" }} value={verifyCode} />
|
||||
</Paragraph>
|
||||
</li>
|
||||
</ol>
|
||||
File renamed without changes.
@@ -7,7 +7,7 @@ const common = require("@clusterio/web_ui/webpack.common");
|
||||
|
||||
module.exports = (env = {}) => merge(common(env), {
|
||||
context: __dirname,
|
||||
entry: "./web/index.jsx",
|
||||
entry: "./web/index.tsx",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist", "web"),
|
||||
},
|
||||
@@ -16,9 +16,9 @@ module.exports = (env = {}) => merge(common(env), {
|
||||
name: "player_auth",
|
||||
library: { type: "var", name: "plugin_player_auth" },
|
||||
exposes: {
|
||||
"./info": "./dist/plugin/info.js",
|
||||
"./info": "./info.ts",
|
||||
"./package.json": "./package.json",
|
||||
"./web": "./web/index.jsx",
|
||||
"./web": "./web/index.tsx",
|
||||
},
|
||||
shared: {
|
||||
"@clusterio/lib": { import: false },
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
webpack.config.js
|
||||
dist/web/build
|
||||
@@ -2,7 +2,7 @@ import type * as lib from "@clusterio/lib";
|
||||
|
||||
import * as messages from "./messages";
|
||||
|
||||
const info: lib.PluginDeclaration = {
|
||||
export default {
|
||||
name: "research_sync",
|
||||
title: "Research Sync",
|
||||
description: "Synchronises technology research progress between instances.",
|
||||
@@ -15,6 +15,4 @@ const info: lib.PluginDeclaration = {
|
||||
messages.FinishedEvent,
|
||||
messages.SyncTechnologiesRequest,
|
||||
],
|
||||
};
|
||||
|
||||
export default info;
|
||||
} satisfies lib.PluginDeclaration;
|
||||
@@ -27,8 +27,9 @@
|
||||
"devDependencies": {
|
||||
"@clusterio/lib": "^2.0.0-alpha.13",
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.13",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/fs-extra": "^11.0.1",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/react": "^18.2.21",
|
||||
"typescript": "^5.1.6",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-cli": "^5.1.4",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.web.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/web/build",
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -7,7 +7,7 @@ const common = require("@clusterio/web_ui/webpack.common");
|
||||
|
||||
module.exports = (env = {}) => merge(common(env), {
|
||||
context: __dirname,
|
||||
entry: "./web/index.jsx",
|
||||
entry: "./web/index.tsx",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist", "web"),
|
||||
},
|
||||
@@ -16,7 +16,7 @@ module.exports = (env = {}) => merge(common(env), {
|
||||
name: "research_sync",
|
||||
library: { type: "var", name: "plugin_research_sync" },
|
||||
exposes: {
|
||||
"./info": "./dist/plugin/info.js",
|
||||
"./info": "./info.ts",
|
||||
"./package.json": "./package.json",
|
||||
},
|
||||
shared: {
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
webpack.config.js
|
||||
dist/web/build
|
||||
@@ -12,7 +12,7 @@ InstanceConfigGroup.define({
|
||||
});
|
||||
InstanceConfigGroup.finalize();
|
||||
|
||||
const info: lib.PluginDeclaration = {
|
||||
export default {
|
||||
name: "statistics_exporter",
|
||||
title: "Statistics Exporter",
|
||||
description:
|
||||
@@ -20,6 +20,4 @@ const info: lib.PluginDeclaration = {
|
||||
"statistics to the cluster's Prometheus endpoint.",
|
||||
instanceEntrypoint: "dist/plugin/instance",
|
||||
InstanceConfigGroup,
|
||||
};
|
||||
|
||||
export default info;
|
||||
} satisfies lib.PluginDeclaration;
|
||||
@@ -24,6 +24,7 @@
|
||||
"@clusterio/lib": "^2.0.0-alpha.13",
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.13",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/react": "^18.2.21",
|
||||
"typescript": "^5.1.6",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-cli": "^5.1.4",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.web.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/web/build",
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
@@ -7,7 +7,7 @@ const common = require("@clusterio/web_ui/webpack.common");
|
||||
|
||||
module.exports = (env = {}) => merge(common(env), {
|
||||
context: __dirname,
|
||||
entry: "./web/index.jsx",
|
||||
entry: "./web/index.tsx",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist", "web"),
|
||||
},
|
||||
@@ -16,7 +16,7 @@ module.exports = (env = {}) => merge(common(env), {
|
||||
name: "statistics_exporter",
|
||||
library: { type: "var", name: "plugin_statistics_exporter" },
|
||||
exposes: {
|
||||
"./info": "./dist/plugin/info.js",
|
||||
"./info": "./info.ts",
|
||||
"./package.json": "./package.json",
|
||||
},
|
||||
shared: {
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
webpack.config.js
|
||||
dist/web/build
|
||||
@@ -47,7 +47,7 @@ lib.definePermission({
|
||||
grantByDefault: true,
|
||||
});
|
||||
|
||||
module.exports.default = {
|
||||
export default {
|
||||
name: "subspace_storage",
|
||||
title: "Subspace Storage",
|
||||
description: "Provides shared storage across instances for the Subspace Storage mod",
|
||||
@@ -66,4 +66,4 @@ module.exports.default = {
|
||||
],
|
||||
webEntrypoint: "./web",
|
||||
routes: ["/storage"],
|
||||
};
|
||||
} satisfies lib.PluginDeclaration;
|
||||
@@ -30,6 +30,7 @@
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/fs-extra": "^11.0.1",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/react": "^18.2.21",
|
||||
"antd": "^5.7.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.web.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/web/build",
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,18 @@ import { Input, Table, Typography } from "antd";
|
||||
|
||||
import * as lib from "@clusterio/lib";
|
||||
import {
|
||||
BaseWebPlugin, PageLayout, ControlContext,
|
||||
BaseWebPlugin, PageLayout, Control, ControlContext,
|
||||
notifyErrorHandler, useItemMetadata, useLocale,
|
||||
} from "@clusterio/web_ui";
|
||||
import { GetStorageRequest, SetStorageSubscriptionRequest } from "../dist/plugin/messages";
|
||||
import { GetStorageRequest, Item, SetStorageSubscriptionRequest, UpdateStorageEvent } from "../messages";
|
||||
|
||||
import "./index.css";
|
||||
import "./style.css";
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
|
||||
function useStorage(control) {
|
||||
let plugin = control.plugins.get("subspace_storage");
|
||||
function useStorage(control: Control) {
|
||||
let plugin = control.plugins.get("subspace_storage") as WebPlugin;
|
||||
let [storage, setStorage] = useState([...plugin.storage]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -35,14 +35,15 @@ function StoragePage() {
|
||||
let locale = useLocale();
|
||||
let itemMetadata = useItemMetadata();
|
||||
let storage = useStorage(control);
|
||||
let [filter, setFilter] = useState(null);
|
||||
type ItemFilter = ([name, count]: [string, number]) => boolean;
|
||||
let [filter, setFilter] = useState<null | ItemFilter>(null);
|
||||
|
||||
function getLocaleName(itemName) {
|
||||
function getLocaleName(itemName: string) {
|
||||
let localeName = itemName;
|
||||
let meta = itemMetadata.get(itemName);
|
||||
if (meta && meta.localized_name) {
|
||||
if (meta && meta.localised_name) {
|
||||
// TODO: implement the locale to name conversion.
|
||||
localeName = locale.get(meta.localized_name[0]);
|
||||
localeName = locale.get(meta.localised_name[0])!;
|
||||
} else {
|
||||
for (let section of ["item-name", "entity-name", "fluid-name", "equipment-name"]) {
|
||||
let name = locale.get(`${section}.${itemName}`);
|
||||
@@ -72,7 +73,7 @@ function StoragePage() {
|
||||
search = search.replace(/(^| )(\w)/g, "$1\\b$2");
|
||||
search = search.replace(/ +/g, ".*");
|
||||
let filterExpr = new RegExp(search, "i");
|
||||
setFilter(() => (item => {
|
||||
setFilter(() => ((item: [string, number]) => {
|
||||
let name = getLocaleName(item[0]);
|
||||
return filterExpr.test(name) || filterExpr.test(item[0]);
|
||||
}));
|
||||
@@ -118,6 +119,9 @@ function StoragePage() {
|
||||
}
|
||||
|
||||
export class WebPlugin extends BaseWebPlugin {
|
||||
storage = new Map<string, number>();
|
||||
callbacks: (() => void)[] = [];
|
||||
|
||||
async init() {
|
||||
this.pages = [
|
||||
{
|
||||
@@ -127,29 +131,27 @@ export class WebPlugin extends BaseWebPlugin {
|
||||
content: <StoragePage/>,
|
||||
},
|
||||
];
|
||||
|
||||
this.storage = new Map();
|
||||
this.callbacks = [];
|
||||
this.control.handle(UpdateStorageEvent, this.handleUpdateStorageEvent.bind(this));
|
||||
}
|
||||
|
||||
onControllerConnectionEvent(event) {
|
||||
onControllerConnectionEvent(event: "connect" | "drop" | "resume" | "close") {
|
||||
if (event === "connect") {
|
||||
this.updateSubscription();
|
||||
}
|
||||
}
|
||||
|
||||
async updateStorageEventHandler(event) {
|
||||
async handleUpdateStorageEvent(event: UpdateStorageEvent) {
|
||||
this.updateStorage(event.items);
|
||||
}
|
||||
|
||||
onUpdate(callback) {
|
||||
onUpdate(callback: () => void) {
|
||||
this.callbacks.push(callback);
|
||||
if (this.callbacks.length) {
|
||||
this.updateSubscription();
|
||||
}
|
||||
}
|
||||
|
||||
offUpdate(callback) {
|
||||
offUpdate(callback: () => void) {
|
||||
let index = this.callbacks.lastIndexOf(callback);
|
||||
if (index === -1) {
|
||||
throw new Error("callback is not registered");
|
||||
@@ -161,7 +163,7 @@ export class WebPlugin extends BaseWebPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
updateStorage(items) {
|
||||
updateStorage(items: Item[]) {
|
||||
for (let item of items) {
|
||||
this.storage.set(item.name, item.count);
|
||||
}
|
||||
@@ -180,7 +182,7 @@ export class WebPlugin extends BaseWebPlugin {
|
||||
).catch(notifyErrorHandler("Error subscribing to storage"));
|
||||
|
||||
if (this.callbacks.length) {
|
||||
this.control.send(new GetStorageRequest()).then(
|
||||
this.control!.send(new GetStorageRequest()).then(
|
||||
items => {
|
||||
this.updateStorage(items);
|
||||
}
|
||||
File renamed without changes.
@@ -7,7 +7,7 @@ const common = require("@clusterio/web_ui/webpack.common");
|
||||
|
||||
module.exports = (env = {}) => merge(common(env), {
|
||||
context: __dirname,
|
||||
entry: "./web/index.jsx",
|
||||
entry: "./web/index.tsx",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist", "web"),
|
||||
},
|
||||
@@ -16,9 +16,9 @@ module.exports = (env = {}) => merge(common(env), {
|
||||
name: "subspace_storage",
|
||||
library: { type: "var", name: "plugin_subspace_storage" },
|
||||
exposes: {
|
||||
"./info": "./dist/plugin/info.js",
|
||||
"./info": "./info.ts",
|
||||
"./package.json": "./package.json",
|
||||
"./web": "./web/index.jsx",
|
||||
"./web": "./web/index.tsx",
|
||||
},
|
||||
shared: {
|
||||
"@clusterio/lib": { import: false },
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"incremental": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
},
|
||||
|
||||
+8
-1
@@ -3,14 +3,21 @@
|
||||
"references": [
|
||||
{ "path": "./packages/lib" },
|
||||
{ "path": "./packages/controller" },
|
||||
{ "path": "./packages/controller/tsconfig.web.json" },
|
||||
{ "path": "./packages/host" },
|
||||
{ "path": "./packages/ctl" },
|
||||
{ "path": "./packages/web_ui" },
|
||||
{ "path": "./packages/web_ui/tsconfig.web.json" },
|
||||
{ "path": "./plugins/global_chat" },
|
||||
{ "path": "./plugins/global_chat/tsconfig.web.json" },
|
||||
{ "path": "./plugins/inventory_sync" },
|
||||
{ "path": "./plugins/inventory_sync/tsconfig.web.json" },
|
||||
{ "path": "./plugins/player_auth" },
|
||||
{ "path": "./plugins/player_auth/tsconfig.web.json" },
|
||||
{ "path": "./plugins/research_sync" },
|
||||
{ "path": "./plugins/research_sync/tsconfig.web.json" },
|
||||
{ "path": "./plugins/statistics_exporter" },
|
||||
{ "path": "./plugins/statistics_exporter/tsconfig.web.json" },
|
||||
{ "path": "./plugins/subspace_storage" },
|
||||
{ "path": "./plugins/subspace_storage/tsconfig.web.json" },
|
||||
],
|
||||
}
|
||||
@@ -5,8 +5,6 @@
|
||||
"module": "node16",
|
||||
"moduleResolution": "node16",
|
||||
"target": "es2022",
|
||||
"esModuleInterop": true,
|
||||
"alwaysStrict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "./tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["dom", "es2023"],
|
||||
"module": "es2022",
|
||||
"moduleResolution": "bundler",
|
||||
"target": "es2022",
|
||||
"jsx": "react",
|
||||
"noEmitOnError": true,
|
||||
},
|
||||
}
|
||||
Reference in new issue
Block a user