diff --git a/npm/README.md b/npm/README.md
index 7803634be..d7925e154 100644
--- a/npm/README.md
+++ b/npm/README.md
@@ -19,7 +19,7 @@ Create a Web client middleware for use with express-style server libraries.
**Parameters**
* `options`
- * `options.apiBase` - Base URL to the mount point of the
+ * `options.apiUrl` - Base URL to the mount point of the
[üWave Web API][u-wave-core] to talk to.
Defaults to `/api`, but it's recommended to set this explicitly.
* `options.emoji` - An object describing the custom emoji that will be
@@ -49,7 +49,7 @@ app.listen(6041);
app.use('/', createWebClient({
// Use nginx to send this traffic to the API server.
- apiBase: 'https://example.com/api',
+ apiUrl: 'https://example.com/api',
recaptcha: { key: 'my ReCaptcha site key' },
}));
```
diff --git a/src/admin/components/SchemaForm/AssetField.js b/src/admin/components/SchemaForm/AssetField.js
new file mode 100644
index 000000000..01ed7771c
--- /dev/null
+++ b/src/admin/components/SchemaForm/AssetField.js
@@ -0,0 +1,60 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { useAsyncCallback } from 'react-async-hook';
+import Typography from '@mui/material/Typography';
+import FormHelperText from '@mui/material/FormHelperText';
+import Button from '@mui/material/Button';
+import FileIcon from '@mui/icons-material/FileUpload';
+import TextField from '../../../components/Form/TextField';
+
+const {
+ useRef,
+} = React;
+
+function AssetField({
+ className,
+ schema,
+ value,
+ onChange,
+}) {
+ const fileInput = useRef(null);
+ const handleSelect = useAsyncCallback(async (event) => {
+ const file = event.target.files[0];
+ const body = new FormData();
+ body.append('file', file);
+
+ const res = await fetch('/api/server/config/asset/u-wave:base.logo', {
+ method: 'put',
+ body,
+ });
+ const json = await res.json();
+ const { data } = json;
+ onChange(data.path);
+ }, [onChange]);
+
+ return (
+
+ {schema.title && {schema.title}}
+
+
+ } />
+
+
+ {schema.description && {schema.description}}
+
+ );
+}
+
+AssetField.propTypes = {
+ className: PropTypes.string,
+ schema: PropTypes.object.isRequired,
+ value: PropTypes.string,
+ onChange: PropTypes.func.isRequired,
+};
+
+export default AssetField;
diff --git a/src/admin/components/SchemaForm/Field.js b/src/admin/components/SchemaForm/Field.js
index 322469995..dde970610 100644
--- a/src/admin/components/SchemaForm/Field.js
+++ b/src/admin/components/SchemaForm/Field.js
@@ -17,6 +17,10 @@ function getControlName(schema) {
return 'enum';
}
+ if (schema.type === 'string' && schema.format === 'asset') {
+ return 'asset';
+ }
+
return schema.type;
}
diff --git a/src/admin/components/SchemaForm/index.js b/src/admin/components/SchemaForm/index.js
index 3146b503d..08c750b4a 100644
--- a/src/admin/components/SchemaForm/index.js
+++ b/src/admin/components/SchemaForm/index.js
@@ -8,6 +8,7 @@ import NumberField from './NumberField';
import ObjectField, { ObjectProperties } from './ObjectField';
import StringField from './StringField';
import MarkdownField from './MarkdownField';
+import AssetField from './AssetField';
import EnumField from './EnumField';
const controls = new Map([
@@ -18,6 +19,7 @@ const controls = new Map([
['string', StringField],
['enum', EnumField],
['markdown', MarkdownField],
+ ['asset', AssetField],
]);
function SchemaForm({
diff --git a/src/components/HeaderBar/AppTitle.js b/src/components/HeaderBar/AppTitle.js
index ee42ae372..d56467cd4 100644
--- a/src/components/HeaderBar/AppTitle.js
+++ b/src/components/HeaderBar/AppTitle.js
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import IconButton from '@mui/material/IconButton';
import AboutIcon from '@mui/icons-material/ArrowDropDown';
-const logo = new URL('../../../assets/img/logo-white.png', import.meta.url);
+const defaultLogo = new URL('../../../assets/img/logo-white.png', import.meta.url);
// We use `logo.pathname` to ensure that the prerendered loading screen
// does not include a `file://` prefix. This should still work fine in browsers
@@ -17,28 +17,32 @@ function pathname(url) {
return url.pathname;
}
-const AppTitle = ({
+function AppTitle({
className,
children,
+ logo = defaultLogo,
onClick,
-}) => (
-