// ! FIXME: this code is garbage, need to replace import { Key, Clipboard, Globe, Plus } from "@styled-icons/boxicons-regular"; import { LockAlt, HelpCircle } from "@styled-icons/boxicons-solid"; import type { AxiosError } from "axios"; import { observer } from "mobx-react-lite"; import { API } from "revolt.js"; import { User } from "revolt.js"; import styled from "styled-components/macro"; import styles from "./Panes.module.scss"; import { Text } from "preact-i18n"; import { useCallback, useEffect, useState } from "preact/hooks"; import { Button, CategoryButton, Checkbox, InputBox, Tip, } from "@revoltchat/ui"; 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 { modalController } from "../../../context/modals"; 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"; interface Data { _id: string; username: string; public: boolean; interactions_url?: string; } interface Changes { name?: string; public?: boolean; interactions_url?: string; remove?: "InteractionsURL"[]; } const BotBadge = styled.div` display: inline-block; flex-shrink: 0; height: 1.3em; padding: 0px 4px; font-size: 0.7em; user-select: none; margin-inline-start: 2px; text-transform: uppercase; color: var(--accent-contrast); background: var(--accent); border-radius: calc(var(--border-radius) / 2); `; interface Props { bot: API.Bot; onDelete(): void; onUpdate(changes: Changes): void; } 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, username: user.username, public: bot.public, interactions_url: bot.interactions_url as any, }); const [error, setError] = useState(""); const [saving, setSaving] = useState(false); const [editMode, setEditMode] = useState(false); const [usernameRef, setUsernameRef] = useState( null, ); const [interactionsRef, setInteractionsRef] = useState(null); const { writeClipboard, openScreen } = useIntermediate(); const [profile, setProfile] = useState( undefined, ); const refreshProfile = useCallback(() => { client.api .get(`/users/${bot._id as ""}/profile`, undefined, { headers: { "x-bot-token": bot.token }, }) .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; if (data.public !== bot.public) changes.public = data.public; if (data.interactions_url === "") changes.remove = ["InteractionsURL"]; else if (data.interactions_url !== bot.interactions_url) changes.interactions_url = data.interactions_url; setSaving(true); setError(""); try { if (Object.keys(changes).length > 0) await client.bots.edit(bot._id, changes); if (changed) await editBotContent(profile?.content ?? undefined); onUpdate(changes); setChanged(false); setEditMode(false); } catch (e) { const err = e as AxiosError; if (err.isAxiosError && err.response?.data?.type) { switch (err.response.data.type) { case "UsernameTaken": setError("That username is taken!"); break; default: setError(`Error: ${err.response.data.type}`); break; } } else setError(err.toString()); } setSaving(false); } async function editBotAvatar(avatar?: string) { setSaving(true); setError(""); await client.api.patch( "/users/@me", avatar ? { avatar } : { remove: ["Avatar"] }, { headers: { "x-bot-token": bot.token }, }, ); const res = await client.bots.fetch(bot._id); if (!avatar) res.user.update({}, ["Avatar"]); setUser(res.user); setSaving(false); } async function editBotBackground(background?: string) { setSaving(true); setError(""); await client.api.patch( "/users/@me", background ? { profile: { background } } : { remove: ["ProfileBackground"] }, { headers: { "x-bot-token": bot.token }, }, ); if (!background) setProfile({ ...profile, background: undefined }); else refreshProfile(); setSaving(false); } async function editBotContent(content?: string) { setSaving(true); setError(""); await client.api.patch( "/users/@me", content ? { profile: { content } } : { remove: ["ProfileContent"] }, { headers: { "x-bot-token": bot.token }, }, ); if (!content) setProfile({ ...profile, content: undefined }); else refreshProfile(); setSaving(false); } const { onChange, onKeyUp, onKeyDown, onFocus, onBlur, ...autoCompleteProps } = useAutoComplete(setContent, { users: { type: "all" }, }); return (
{!editMode ? ( openScreen({ id: "profile", user_id: user._id, }) } /> ) : ( editBotAvatar(avatar)} remove={() => editBotAvatar()} defaultPreview={user.generateAvatarURL( { max_side: 256 }, true, )} previewURL={user.generateAvatarURL( { max_side: 256 }, true, )} /> )} {!editMode ? (
{user!.username}{" "}
) : ( setData({ ...data, username: e.currentTarget.value, }) } /> )}
{!editMode && ( }> {bot.public ? ( ) : ( )} )}
{!editMode && ( } onClick={() => writeClipboard(bot.token)} description={ <> {"••••• "} stopPropagation( ev, modalController.push({ type: "show_token", token: bot.token, name: user!.username, }), ) }> } action={}> )} {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} />
} description={ } onChange={(v) => setData({ ...data, public: v })} />

setData({ ...data, interactions_url: e.currentTarget.value, }) } />
)} {error && (
{error}
)}
{editMode && ( <> )} {!editMode && ( <> )}
); } export const MyBots = observer(() => { const client = useClient(); const [bots, setBots] = useState(undefined); useEffect(() => { client.bots.fetchOwned().then(({ bots }) => setBots(bots)); // eslint-disable-next-line }, []); const { openScreen } = useIntermediate(); return (
} onClick={() => openScreen({ id: "create_bot", onCreate: (bot) => setBots([...(bots ?? []), bot]), }) } action="chevron">
By creating a bot, you are agreeing to the {` `} Acceptable Usage Policy .

{bots?.map((bot) => { return ( setBots(bots.filter((x) => x._id !== bot._id)) } onUpdate={(changes: Changes) => setBots( bots.map((x) => { if (x._id === bot._id) { if ( "public" in changes && typeof changes.public === "boolean" ) x.public = changes.public; if ("interactions_url" in changes) x.interactions_url = changes.interactions_url; if ( changes.remove?.includes( "InteractionsURL", ) ) x.interactions_url = undefined; } return x; }), ) } /> ); })}
); });