diff --git a/src/pages/settings/panes/MyBots.tsx b/src/pages/settings/panes/MyBots.tsx index a524e9c1..77358625 100644 --- a/src/pages/settings/panes/MyBots.tsx +++ b/src/pages/settings/panes/MyBots.tsx @@ -3,20 +3,27 @@ import { LockAlt } from "@styled-icons/boxicons-solid"; import type { AxiosError } from "axios"; import { observer } from "mobx-react-lite"; 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 styled from "styled-components"; import styles from "./Panes.module.scss"; 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 { useTranslation } from "../../../lib/i18n"; import { stopPropagation } from "../../../lib/stopPropagation"; import { useIntermediate } from "../../../context/intermediate/Intermediate"; import { FileUploader } from "../../../context/revoltjs/FileUploads"; 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 UserIcon from "../../../components/common/user/UserIcon"; import Button from "../../../components/ui/Button"; @@ -62,6 +69,7 @@ interface Props { function BotCard({ bot, onDelete, onUpdate }: Props) { const client = useClient(); + const translate = useTranslation(); const [user, setUser] = useState(client.users.get(bot._id)!); const [data, setData] = useState({ _id: bot._id, @@ -79,6 +87,37 @@ function BotCard({ bot, onDelete, onUpdate }: Props) { useState(null); const { writeClipboard, openScreen } = useIntermediate(); + const [profile, setProfile] = useState(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() { const changes: Changes = {}; if (data.username !== user!.username) changes.name = data.username; @@ -90,7 +129,9 @@ function BotCard({ bot, onDelete, onUpdate }: Props) { setError(""); try { await client.bots.edit(bot._id, changes); + if (changed) await editBotContent(profile?.content); onUpdate(changes); + setChanged(false); setEditMode(false); } catch (e) { const err = e as AxiosError; @@ -128,6 +169,63 @@ function BotCard({ bot, onDelete, onUpdate }: Props) { 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 (
@@ -270,6 +368,60 @@ function BotCard({ bot, onDelete, onUpdate }: Props) { )} {editMode && (
+ +

+ +

+ + editBotBackground(background) + } + remove={() => editBotBackground()} + previewURL={ + profile?.background + ? client.generateFileURL( + profile.background, + { width: 1000 }, + true, + ) + : undefined + } + /> +

+ +

+ + { + 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} + /> +
h5 { + margin-bottom: 1em; + } + > h3 { + margin-bottom: 0; + } + + details, + label { + margin-top: 0; + } } .infoheader {