diff --git a/.vscode/settings.json b/.vscode/settings.json index 3734ef64..a0484054 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true -} \ No newline at end of file + "editor.formatOnSave": true, + "compile-hero.disable-compile-files-on-did-save-code": true +} diff --git a/src/components/ui/Checkbox.tsx b/src/components/ui/Checkbox.tsx index 8f52ac1b..1117a613 100644 --- a/src/components/ui/Checkbox.tsx +++ b/src/components/ui/Checkbox.tsx @@ -51,7 +51,7 @@ const CheckboxDescription = styled.span` color: var(--secondary-foreground); `; -const Checkmark = styled.div<{ checked: boolean }>` +const Checkmark = styled.div<{ checked: boolean; contrast?: boolean }>` margin: 4px; width: 24px; height: 24px; @@ -66,6 +66,16 @@ const Checkmark = styled.div<{ checked: boolean }>` color: var(--secondary-background); } + ${(props) => + props.contrast && + css` + background: var(--primary-background); + + svg { + color: var(--primary-background); + } + `} + ${(props) => props.checked && css` @@ -76,6 +86,7 @@ const Checkmark = styled.div<{ checked: boolean }>` export interface CheckboxProps { checked: boolean; disabled?: boolean; + contrast?: boolean; className?: string; children: Children; description?: Children; @@ -100,7 +111,7 @@ export default function Checkbox(props: CheckboxProps) { !props.disabled && props.onChange(!props.checked) } /> - + diff --git a/src/pages/settings/panes/MyBots.tsx b/src/pages/settings/panes/MyBots.tsx index 27e21083..90b7838b 100644 --- a/src/pages/settings/panes/MyBots.tsx +++ b/src/pages/settings/panes/MyBots.tsx @@ -21,6 +21,7 @@ import InputBox from "../../../components/ui/InputBox"; import Overline from "../../../components/ui/Overline"; import Tip from "../../../components/ui/Tip"; import CategoryButton from "../../../components/ui/fluent/CategoryButton"; +import { User } from "revolt.js/dist/maps/Users"; interface Data { _id: string; @@ -29,6 +30,13 @@ interface Data { interactions_url?: string; } +interface Changes { + name?: string; + public?: boolean; + interactions_url?: string; + remove?: "InteractionsURL"; +} + const BotBadge = styled.div` display: inline-block; @@ -44,50 +52,212 @@ const BotBadge = styled.div` border-radius: calc(var(--border-radius) / 2); `; -function BotEditor({ bot }: { bot: Data }) { - const client = useClient(); - const [data, setData] = useState(bot); +interface Props { + bot: Bot; + user: User; + onDelete(): Promise; + onUpdate(changes: Changes): Promise; +} - function save() { - const changes: Record = {}; - if (data.username !== bot.username) changes.name = data.username; +function BotCard({ bot, user, onDelete, onUpdate }: Props) { + const [data, setData] = useState({ + _id: bot._id, + username: user!.username, + public: bot.public, + interactions_url: bot.interactions_url, + }); + const [saving, setSaving] = useState(false); + const [editMode, setEditMode] = useState(false); + const [usernameRef, setUsernameRef] = useState( + null, + ); + const [interactionsRef, setInteractionsRef] = + useState(null); + const { writeClipboard, openScreen } = useIntermediate(); + + 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 !== bot.interactions_url) + if (data.interactions_url === '') + changes.remove = 'InteractionsURL'; + else if (data.interactions_url !== bot.interactions_url) changes.interactions_url = data.interactions_url; - - client.bots.edit(bot._id, changes); + setSaving(true); + try { + await onUpdate(changes); + setEditMode(false); + } catch (e) { + // TODO error handling + } + setSaving(false); } return ( -
-

- - setData({ ...data, username: e.currentTarget.value }) +

+
+
+ + openScreen({ + id: "profile", + user_id: user!._id, + }) + } + /> + {!editMode ? ( +
+
+ {user!.username}{" "} + + + +
+ + +
+ ) : ( + + setData({ + ...data, + username: e.currentTarget.value, + }) + } + /> + )} +
+ + {!editMode && ( + + {bot.public ? ( + + ) : ( + + )} + + )} + +
+ {!editMode && ( + } + onClick={() => writeClipboard(bot.token)} + description={ + <> + {"••••••••••••••••••••••••••••••••••••"}{" "} + + stopPropagation( + ev, + openScreen({ + id: "token_reveal", + token: bot.token, + username: user!.username, + }), + ) + }> + + + } - /> -

-

- setData({ ...data, public: v })}> - is public - -

-

interactions url: (reserved for the future)

-

- - setData({ - ...data, - interactions_url: e.currentTarget.value, - }) - } - /> -

- + action={}> + Token +
+ )} + {editMode && ( + <> + setData({ ...data, public: v })}> + Public Bot + +

Interactions URL

+
This field is reserved for the future.
+ + setData({ + ...data, + interactions_url: + e.currentTarget.value, + }) + } + /> + + )} + +
+ {editMode && ( + <> + + + + )} + {!editMode && ( + + )} +
); } @@ -102,8 +272,6 @@ export const MyBots = observer(() => { }, []); const [name, setName] = useState(""); - const [editMode, setEditMode] = useState(false); - const { writeClipboard, openScreen } = useIntermediate(); return (
@@ -132,116 +300,31 @@ export const MyBots = observer(() => {

my bots {bots?.map((bot) => { - const user = client.users.get(bot._id); + const user = client.users.get(bot._id)!; return ( -
-
-
- - openScreen({ - id: "profile", - user_id: user!._id, - }) - } - /> -
-
- {user!.username}{" "} - - - -
- - -
-
- - - {bot.public ? : } - - {/* */} -
- } - onClick={() => writeClipboard(bot.token)} - description={ - <> - {"••••••••••••••••••••••••••••••••••••"}{" "} - - stopPropagation( - ev, - openScreen({ - id: "token_reveal", - token: bot.token, - username: user!.username, - }), - ) - }> - - - - } - action={}> - Token - - - - -
+ .then(() => setBots(bots.filter((x) => x._id !== bot._id))) + + } + onUpdate={(changes: Changes) => + client.bots.edit(bot._id, changes).then(() => 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 === 'InteractionsURL') x.interactions_url = undefined; + } + return x; + }), + )) + } + /> ); })}
diff --git a/src/pages/settings/panes/Panes.module.scss b/src/pages/settings/panes/Panes.module.scss index 6550e5ca..116b7e8f 100644 --- a/src/pages/settings/panes/Panes.module.scss +++ b/src/pages/settings/panes/Panes.module.scss @@ -584,6 +584,13 @@ } } } + + .buttonRow { + margin-top: 2em; + display: flex; + flex-direction: row; + gap: 10px; + } } section {