Files
factorio-clusterio/packages/web_ui/src/components/UserViewPage.jsx
T
Hornwitser 8a0c1e8a4b Use single entrypoint for lib in web interface
In order to import a path inside packages/lib/ in the webpack shared
module environment it has to be declared as shared in both the web_ui
webpack config and the plugin's webpack config.  This is rather error
prone and easy to miss.  Move the burden of specifying which entrypoints
are valid in lib to lib itself by changing all imports in the web
interface code to import from @clusterio/lib instead sub paths.

For web code this means imports is done like this from now on:

import { libConfig, libLink } from "@clusterio/lib";

For info.js which has to be loadable by both Node.js and web code the
imports are done as follows:

const { libConfig, libLink } = require("@clusterio/lib");

While the Node.js only code can either continue to import sub paths
directly or use the info.js style.
2021-08-17 01:42:45 +02:00

247 lines
6.5 KiB
React

import React, { useEffect, useContext, useState } from "react";
import { useHistory, useParams } from "react-router-dom";
import { Button, Checkbox, Col, Form, Input, Popconfirm, Popover, Row, Select, Space, Spin, Switch } from "antd";
import DeleteOutlined from "@ant-design/icons/DeleteOutlined";
import { libLink } from "@clusterio/lib";
import ControlContext from "./ControlContext";
import PageLayout from "./PageLayout";
import { notifyErrorHandler } from "../util/notify";
function useUser(name) {
let control = useContext(ControlContext);
let [user, setUser] = useState({ loading: true });
function updateUser() {
// XXX optimize by requesting only the user in question
libLink.messages.listUsers.send(control).then(result => {
let match = result.list.find(u => u.name === name);
if (!match) {
setUser({ missing: true });
} else {
setUser({ ...match, present: true });
}
});
}
useEffect(() => {
updateUser();
}, [name]);
return [user, updateUser];
}
export default function UserViewPage() {
let params = useParams();
let userName = params.name;
let history = useHistory();
let control = useContext(ControlContext);
let [user, updateUser] = useUser(userName);
let [roles, setRoles] = useState(null);
let [form] = Form.useForm();
let [rolesDirty, setRolesDirty] = useState(false);
let [applyingRoles, setApplyingRoles] = useState(false);
let [rolesError, setRolesError] = useState();
let [banUserPopover, setBanUserPopover] = useState(false);
useEffect(() => {
libLink.messages.listRoles.send(control).then(result => {
setRoles(new Map(result["list"].map(role => [role["id"], role])));
}).catch(() => {
setRoles(new Map());
});
}, []);
let nav = [{ name: "Users", path: "/users" }, { name: userName }];
if (user.loading) {
return <PageLayout nav={nav}>
<h2>{userName}</h2>
<Spin size="large" />
</PageLayout>;
}
if (user.missing) {
return <PageLayout nav={nav}>
<h2>User not found</h2>
<p>User with name {userName} was not found on the master server.</p>
</PageLayout>;
}
return <PageLayout nav={nav}>
<Row>
<Col flex="auto">
<h2>{userName}</h2>
</Col>
<Col flex="0 0 auto">
<Popconfirm
title={<>
Delete user account and all data associated with it?
{(user["is_banned"] && <><br/>This will remove the user ban!</>)}
</>}
placement="bottomRight"
okText="Delete"
okButtonProps={{ danger: true }}
onConfirm={() => {
libLink.messages.deleteUser.send(
control, { name: userName }
).then(() => {
history.push("/users");
}).catch(notifyErrorHandler("Error deleting user"));
}}
>
<Button danger >
<DeleteOutlined />
</Button>
</Popconfirm>
</Col>
</Row>
<Form form={form}>
<Form.Item
label="Roles"
>
<Row gutter={8}>
<Col flex="auto">
<Form.Item
noStyle
name="roles"
initialValue={user["roles"]}
rules={[
{ validator: () => (rolesError ? Promise.reject(rolesError) : Promise.resolve()) },
]}
>
<Select
onChange={() => setRolesDirty(true)}
loading={!roles}
mode="multiple"
showArrow={true}
filterOption={(inputValue, option) => {
let role = roles.get(option.value);
return role && role["name"].toLowerCase().includes(inputValue.toLowerCase());
}}
>
{roles && [...roles.values()].map(r => <Select.Option
key={r["id"]}
value={r["id"]}
>{r["name"]}</Select.Option>)}
</Select>
</Form.Item>
</Col>
<Col flex="0 0 auto">
<Button
type={"primary"}
disabled={!rolesDirty}
loading={applyingRoles}
onClick={() => {
let newRoles = form.getFieldValue("roles");
setApplyingRoles(true);
libLink.messages.updateUserRoles.send(control, {
name: userName,
roles: newRoles,
}).then(() => {
setRolesDirty(false);
setRolesError();
}).catch(err => {
setRolesError(err.message);
return form.validateFields();
}).finally(() => {
setApplyingRoles(false);
});
}}
>Apply</Button>
</Col>
</Row>
</Form.Item>
<Form.Item label="In-game Admin">
<Switch
checked={user["is_admin"]}
onClick={() => {
libLink.messages.setUserAdmin.send(control, {
name: userName,
admin: !user["is_admin"],
create: false,
}).then(() => {
updateUser();
}).catch(notifyErrorHandler("Error toggling user admin status"));
}}
/>
</Form.Item>
<Form.Item label="Whitelisted">
<Switch
checked={user["is_whitelisted"]}
onClick={() => {
libLink.messages.setUserWhitelisted.send(control, {
name: userName,
whitelisted: !user["is_whitelisted"],
create: false,
}).then(() => {
updateUser();
}).catch(notifyErrorHandler("Error toggling user whitelisted status"));
}}
/>
</Form.Item>
<Form.Item label="Banned">
<Space>
{user["is_banned"] ? "Yes" : "No"}
<Popover
title="Ban user"
visible={banUserPopover}
trigger="click"
onVisibleChange={() => {
if (!user["is_banned"]) {
setBanUserPopover(!banUserPopover);
}
}}
content=<Form.Item label="Reason">
<Row gutter={8}>
<Col flex="auto">
<Form.Item noStyle name="reason">
<Input/>
</Form.Item>
</Col>
<Col flex="0 0 auto">
<Button
type="primary"
onClick={() => {
let reason = form.getFieldValue("reason");
libLink.messages.setUserBanned.send(control, {
name: userName,
create: false,
banned: true,
reason,
}).then(() => {
setBanUserPopover(false);
updateUser();
}).catch(notifyErrorHandler("Error banning user"));
}}
>Ban</Button>
</Col>
</Row>
</Form.Item>
>
<Button
type="primary"
size="small"
onClick={() => {
if (user["is_banned"]) {
libLink.messages.setUserBanned.send(control, {
name: userName,
create: false,
banned: false,
reason: "",
}).then(() => {
updateUser();
}).catch(notifyErrorHandler("Error pardoning user"));
}
}}
>{user["is_banned"] ? "Pardon User" : "Ban User"}</Button>
</Popover>
</Space>
</Form.Item>
</Form>
</PageLayout>;
}