mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-26 09:00:57 -05:00
Merge pull request #204 from Snazzah/bots-banner-and-info
Add banner and info box to bots page
This commit is contained in:
commit
932756d7c7
2 changed files with 165 additions and 7 deletions
|
@ -3,20 +3,27 @@ import { LockAlt } from "@styled-icons/boxicons-solid";
|
||||||
import type { AxiosError } from "axios";
|
import type { AxiosError } from "axios";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Bot } from "revolt-api/types/Bots";
|
import { Bot } from "revolt-api/types/Bots";
|
||||||
|
import { Profile as ProfileI } from "revolt-api/types/Users";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
import { User } from "revolt.js/dist/maps/Users";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import styles from "./Panes.module.scss";
|
import styles from "./Panes.module.scss";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useCallback, useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
|
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
|
||||||
import { internalEmit } from "../../../lib/eventEmitter";
|
import { internalEmit } from "../../../lib/eventEmitter";
|
||||||
|
import { useTranslation } from "../../../lib/i18n";
|
||||||
import { stopPropagation } from "../../../lib/stopPropagation";
|
import { stopPropagation } from "../../../lib/stopPropagation";
|
||||||
|
|
||||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||||
import { FileUploader } from "../../../context/revoltjs/FileUploads";
|
import { FileUploader } from "../../../context/revoltjs/FileUploads";
|
||||||
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
|
import AutoComplete, {
|
||||||
|
useAutoComplete,
|
||||||
|
} from "../../../components/common/AutoComplete";
|
||||||
|
import CollapsibleSection from "../../../components/common/CollapsibleSection";
|
||||||
import Tooltip from "../../../components/common/Tooltip";
|
import Tooltip from "../../../components/common/Tooltip";
|
||||||
import UserIcon from "../../../components/common/user/UserIcon";
|
import UserIcon from "../../../components/common/user/UserIcon";
|
||||||
import Button from "../../../components/ui/Button";
|
import Button from "../../../components/ui/Button";
|
||||||
|
@ -62,6 +69,7 @@ interface Props {
|
||||||
|
|
||||||
function BotCard({ bot, onDelete, onUpdate }: Props) {
|
function BotCard({ bot, onDelete, onUpdate }: Props) {
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
|
const translate = useTranslation();
|
||||||
const [user, setUser] = useState<User>(client.users.get(bot._id)!);
|
const [user, setUser] = useState<User>(client.users.get(bot._id)!);
|
||||||
const [data, setData] = useState<Data>({
|
const [data, setData] = useState<Data>({
|
||||||
_id: bot._id,
|
_id: bot._id,
|
||||||
|
@ -79,6 +87,37 @@ function BotCard({ bot, onDelete, onUpdate }: Props) {
|
||||||
useState<HTMLInputElement | null>(null);
|
useState<HTMLInputElement | null>(null);
|
||||||
const { writeClipboard, openScreen } = useIntermediate();
|
const { writeClipboard, openScreen } = useIntermediate();
|
||||||
|
|
||||||
|
const [profile, setProfile] = useState<undefined | ProfileI>(undefined);
|
||||||
|
|
||||||
|
const refreshProfile = useCallback(() => {
|
||||||
|
client
|
||||||
|
.request(
|
||||||
|
"GET",
|
||||||
|
`/users/${bot._id}/profile` as "/users/id/profile",
|
||||||
|
{
|
||||||
|
headers: { "x-bot-token": bot.token },
|
||||||
|
transformRequest: (data, headers) => {
|
||||||
|
// Remove user headers for this request
|
||||||
|
delete headers["x-user-id"];
|
||||||
|
delete headers["x-session-token"];
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.then((profile) => setProfile(profile ?? {}));
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [user, setProfile]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (profile === undefined && editMode) refreshProfile();
|
||||||
|
}, [profile, editMode, refreshProfile]);
|
||||||
|
|
||||||
|
const [changed, setChanged] = useState(false);
|
||||||
|
function setContent(content?: string) {
|
||||||
|
setProfile({ ...profile, content });
|
||||||
|
if (!changed) setChanged(true);
|
||||||
|
}
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
const changes: Changes = {};
|
const changes: Changes = {};
|
||||||
if (data.username !== user!.username) changes.name = data.username;
|
if (data.username !== user!.username) changes.name = data.username;
|
||||||
|
@ -90,7 +129,9 @@ function BotCard({ bot, onDelete, onUpdate }: Props) {
|
||||||
setError("");
|
setError("");
|
||||||
try {
|
try {
|
||||||
await client.bots.edit(bot._id, changes);
|
await client.bots.edit(bot._id, changes);
|
||||||
|
if (changed) await editBotContent(profile?.content);
|
||||||
onUpdate(changes);
|
onUpdate(changes);
|
||||||
|
setChanged(false);
|
||||||
setEditMode(false);
|
setEditMode(false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const err = e as AxiosError;
|
const err = e as AxiosError;
|
||||||
|
@ -128,6 +169,63 @@ function BotCard({ bot, onDelete, onUpdate }: Props) {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function editBotBackground(background?: string) {
|
||||||
|
setSaving(true);
|
||||||
|
setError("");
|
||||||
|
await client.request("PATCH", "/users/id", {
|
||||||
|
headers: { "x-bot-token": bot.token },
|
||||||
|
transformRequest: (data, headers) => {
|
||||||
|
// Remove user headers for this request
|
||||||
|
delete headers["x-user-id"];
|
||||||
|
delete headers["x-session-token"];
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
data: JSON.stringify(
|
||||||
|
background
|
||||||
|
? { profile: { background } }
|
||||||
|
: { remove: "ProfileBackground" },
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!background) setProfile({ ...profile, background: undefined });
|
||||||
|
else refreshProfile();
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function editBotContent(content?: string) {
|
||||||
|
setSaving(true);
|
||||||
|
setError("");
|
||||||
|
await client.request("PATCH", "/users/id", {
|
||||||
|
headers: { "x-bot-token": bot.token },
|
||||||
|
transformRequest: (data, headers) => {
|
||||||
|
// Remove user headers for this request
|
||||||
|
delete headers["x-user-id"];
|
||||||
|
delete headers["x-session-token"];
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
data: JSON.stringify(
|
||||||
|
content
|
||||||
|
? { profile: { content } }
|
||||||
|
: { remove: "ProfileContent" },
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!content) setProfile({ ...profile, content: undefined });
|
||||||
|
else refreshProfile();
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
onChange,
|
||||||
|
onKeyUp,
|
||||||
|
onKeyDown,
|
||||||
|
onFocus,
|
||||||
|
onBlur,
|
||||||
|
...autoCompleteProps
|
||||||
|
} = useAutoComplete(setContent, {
|
||||||
|
users: { type: "all" },
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={bot._id} className={styles.botCard}>
|
<div key={bot._id} className={styles.botCard}>
|
||||||
<div className={styles.infoheader}>
|
<div className={styles.infoheader}>
|
||||||
|
@ -270,6 +368,60 @@ function BotCard({ bot, onDelete, onUpdate }: Props) {
|
||||||
)}
|
)}
|
||||||
{editMode && (
|
{editMode && (
|
||||||
<div className={styles.botSection}>
|
<div className={styles.botSection}>
|
||||||
|
<CollapsibleSection
|
||||||
|
defaultValue={false}
|
||||||
|
id={`bot_profile_${bot._id}`}
|
||||||
|
summary="Profile">
|
||||||
|
<h3>
|
||||||
|
<Text id="app.settings.pages.profile.custom_background" />
|
||||||
|
</h3>
|
||||||
|
<FileUploader
|
||||||
|
height={92}
|
||||||
|
style="banner"
|
||||||
|
behaviour="upload"
|
||||||
|
fileType="backgrounds"
|
||||||
|
maxFileSize={6_000_000}
|
||||||
|
onUpload={(background) =>
|
||||||
|
editBotBackground(background)
|
||||||
|
}
|
||||||
|
remove={() => editBotBackground()}
|
||||||
|
previewURL={
|
||||||
|
profile?.background
|
||||||
|
? client.generateFileURL(
|
||||||
|
profile.background,
|
||||||
|
{ width: 1000 },
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<h3>
|
||||||
|
<Text id="app.settings.pages.profile.info" />
|
||||||
|
</h3>
|
||||||
|
<AutoComplete detached {...autoCompleteProps} />
|
||||||
|
<TextAreaAutoSize
|
||||||
|
maxRows={10}
|
||||||
|
minHeight={200}
|
||||||
|
maxLength={2000}
|
||||||
|
value={profile?.content ?? ""}
|
||||||
|
disabled={typeof profile === "undefined"}
|
||||||
|
onChange={(ev) => {
|
||||||
|
onChange(ev);
|
||||||
|
setContent(ev.currentTarget.value);
|
||||||
|
}}
|
||||||
|
placeholder={translate(
|
||||||
|
`app.settings.pages.profile.${
|
||||||
|
typeof profile === "undefined"
|
||||||
|
? "fetching"
|
||||||
|
: "placeholder"
|
||||||
|
}`,
|
||||||
|
)}
|
||||||
|
onKeyUp={onKeyUp}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
onFocus={onFocus}
|
||||||
|
onBlur={onBlur}
|
||||||
|
/>
|
||||||
|
</CollapsibleSection>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={data.public}
|
checked={data.public}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
|
|
|
@ -525,15 +525,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.myBots {
|
.myBots {
|
||||||
|
|
||||||
.botCard {
|
.botCard {
|
||||||
background: var(--secondary-background);
|
background: var(--secondary-background);
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
|
|
||||||
h5 { margin-bottom: 1em }
|
|
||||||
h3 { margin-bottom: 0 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.botSection {
|
.botSection {
|
||||||
|
@ -542,7 +538,17 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
|
||||||
label { margin-top: 0 }
|
> h5 {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
> h3 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
details,
|
||||||
|
label {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.infoheader {
|
.infoheader {
|
||||||
|
|
Loading…
Reference in a new issue