mirror of
https://github.com/revoltchat/revite.git
synced 2024-12-26 07:22:10 -05:00
Merge branch 'master' of https://github.com/revoltchat/revite into pr-5
This commit is contained in:
commit
3d707a64da
12 changed files with 437 additions and 91 deletions
2
external/lang
vendored
2
external/lang
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 7be90cf44ba08d235ae52d7dc6073d8f9347232b
|
Subproject commit b27044c332ca419d22edbe9e7bf465a665398999
|
|
@ -115,8 +115,8 @@
|
||||||
"react-virtualized-auto-sizer": "^1.0.5",
|
"react-virtualized-auto-sizer": "^1.0.5",
|
||||||
"react-virtuoso": "^1.10.4",
|
"react-virtuoso": "^1.10.4",
|
||||||
"redux": "^4.1.0",
|
"redux": "^4.1.0",
|
||||||
"revolt-api": "^0.5.2-alpha.0",
|
"revolt-api": "0.5.2-alpha.1",
|
||||||
"revolt.js": "5.0.0-alpha.21",
|
"revolt.js": "5.0.1-alpha.3",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"sass": "^1.35.1",
|
"sass": "^1.35.1",
|
||||||
"shade-blend-color": "^1.0.0",
|
"shade-blend-color": "^1.0.0",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
import { User } from "revolt.js/dist/maps/Users";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
|
||||||
|
@ -9,6 +10,21 @@ import { useClient } from "../../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
import UserIcon from "./UserIcon";
|
import UserIcon from "./UserIcon";
|
||||||
|
|
||||||
|
const BotBadge = styled.div`
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
height: 1.4em;
|
||||||
|
padding: 0 4px;
|
||||||
|
font-size: 0.6em;
|
||||||
|
user-select: none;
|
||||||
|
margin-inline-start: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
color: var(--foreground);
|
||||||
|
background: var(--accent);
|
||||||
|
border-radius: calc(var(--border-radius) / 2);
|
||||||
|
`;
|
||||||
|
|
||||||
export const Username = observer(
|
export const Username = observer(
|
||||||
({
|
({
|
||||||
user,
|
user,
|
||||||
|
@ -51,6 +67,21 @@ export const Username = observer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user?.bot) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span {...otherProps} style={{ color }}>
|
||||||
|
{username ?? (
|
||||||
|
<Text id="app.main.channel.unknown_user" />
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<BotBadge>
|
||||||
|
<Text id="app.main.channel.bot" />
|
||||||
|
</BotBadge>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span {...otherProps} style={{ color }}>
|
<span {...otherProps} style={{ color }}>
|
||||||
{username ?? <Text id="app.main.channel.unknown_user" />}
|
{username ?? <Text id="app.main.channel.unknown_user" />}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import InputBox from "../../ui/InputBox";
|
||||||
import Overline from "../../ui/Overline";
|
import Overline from "../../ui/Overline";
|
||||||
import Preloader from "../../ui/Preloader";
|
import Preloader from "../../ui/Preloader";
|
||||||
|
|
||||||
import { GenericSidebarBase } from "../SidebarBase";
|
import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase";
|
||||||
|
|
||||||
type SearchState =
|
type SearchState =
|
||||||
| {
|
| {
|
||||||
|
@ -100,57 +100,59 @@ export function SearchSidebar({ close }: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GenericSidebarBase>
|
<GenericSidebarBase>
|
||||||
<SearchBase>
|
<GenericSidebarList>
|
||||||
<Overline type="error" onClick={close} block>
|
<SearchBase>
|
||||||
« back to members
|
<Overline type="error" onClick={close} block>
|
||||||
</Overline>
|
« back to members
|
||||||
<Overline type="subtle" block>
|
</Overline>
|
||||||
<Text id="app.main.channel.search.title" />
|
<Overline type="subtle" block>
|
||||||
</Overline>
|
<Text id="app.main.channel.search.title" />
|
||||||
<InputBox
|
</Overline>
|
||||||
value={query}
|
<InputBox
|
||||||
onKeyDown={(e) => e.key === "Enter" && search()}
|
value={query}
|
||||||
onChange={(e) => setQuery(e.currentTarget.value)}
|
onKeyDown={(e) => e.key === "Enter" && search()}
|
||||||
/>
|
onChange={(e) => setQuery(e.currentTarget.value)}
|
||||||
<div class="sort">
|
/>
|
||||||
{["Latest", "Oldest", "Relevance"].map((key) => (
|
<div class="sort">
|
||||||
<Button
|
{["Latest", "Oldest", "Relevance"].map((key) => (
|
||||||
key={key}
|
<Button
|
||||||
compact
|
key={key}
|
||||||
error={sort === key}
|
compact
|
||||||
onClick={() => setSort(key as Sort)}>
|
error={sort === key}
|
||||||
<Text
|
onClick={() => setSort(key as Sort)}>
|
||||||
id={`app.main.channel.search.sort.${key.toLowerCase()}`}
|
<Text
|
||||||
/>
|
id={`app.main.channel.search.sort.${key.toLowerCase()}`}
|
||||||
</Button>
|
/>
|
||||||
))}
|
</Button>
|
||||||
</div>
|
))}
|
||||||
{state.type === "loading" && <Preloader type="ring" />}
|
|
||||||
{state.type === "results" && (
|
|
||||||
<div class="list">
|
|
||||||
{state.results.map((message) => {
|
|
||||||
let href = "";
|
|
||||||
if (channel?.channel_type === "TextChannel") {
|
|
||||||
href += `/server/${channel.server_id}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
href += `/channel/${message.channel_id}/${message._id}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link to={href} key={message._id}>
|
|
||||||
<div class="message">
|
|
||||||
<Message
|
|
||||||
message={message}
|
|
||||||
head
|
|
||||||
hideReply
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
{state.type === "loading" && <Preloader type="ring" />}
|
||||||
</SearchBase>
|
{state.type === "results" && (
|
||||||
|
<div class="list">
|
||||||
|
{state.results.map((message) => {
|
||||||
|
let href = "";
|
||||||
|
if (channel?.channel_type === "TextChannel") {
|
||||||
|
href += `/server/${channel.server_id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
href += `/channel/${message.channel_id}/${message._id}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link to={href} key={message._id}>
|
||||||
|
<div class="message">
|
||||||
|
<Message
|
||||||
|
message={message}
|
||||||
|
head
|
||||||
|
hideReply
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</SearchBase>
|
||||||
|
</GenericSidebarList>
|
||||||
</GenericSidebarBase>
|
</GenericSidebarBase>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import Button from "../../../components/ui/Button";
|
||||||
import InputBox from "../../../components/ui/InputBox";
|
import InputBox from "../../../components/ui/InputBox";
|
||||||
import Modal from "../../../components/ui/Modal";
|
import Modal from "../../../components/ui/Modal";
|
||||||
import Overline from "../../../components/ui/Overline";
|
import Overline from "../../../components/ui/Overline";
|
||||||
|
import Tip from "../../../components/ui/Tip";
|
||||||
|
|
||||||
import { FileUploader } from "../../revoltjs/FileUploads";
|
import { FileUploader } from "../../revoltjs/FileUploads";
|
||||||
import { useClient } from "../../revoltjs/RevoltClient";
|
import { useClient } from "../../revoltjs/RevoltClient";
|
||||||
|
@ -30,6 +31,9 @@ export const ServerIdentityModal = observer(({ server, onClose }: Props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal visible={true} onClose={onClose}>
|
<Modal visible={true} onClose={onClose}>
|
||||||
|
<Tip warning hideSeparator>
|
||||||
|
This section is under construction.
|
||||||
|
</Tip>
|
||||||
<Overline type="subtle">Nickname</Overline>
|
<Overline type="subtle">Nickname</Overline>
|
||||||
<p>
|
<p>
|
||||||
<InputBox
|
<InputBox
|
||||||
|
|
|
@ -136,34 +136,34 @@
|
||||||
a {
|
a {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.entry {
|
.entry {
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 12px;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
align-items: center;
|
||||||
|
transition: background-color 0.1s;
|
||||||
|
color: var(--secondary-foreground);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
background-color: var(--secondary-background);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--primary-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding: 12px;
|
overflow: hidden;
|
||||||
display: flex;
|
white-space: nowrap;
|
||||||
cursor: pointer;
|
text-overflow: ellipsis;
|
||||||
align-items: center;
|
|
||||||
transition: background-color 0.1s;
|
|
||||||
color: var(--secondary-foreground);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
background-color: var(--secondary-background);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: var(--primary-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
min-width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,11 @@ import ChannelIcon from "../../../components/common/ChannelIcon";
|
||||||
import ServerIcon from "../../../components/common/ServerIcon";
|
import ServerIcon from "../../../components/common/ServerIcon";
|
||||||
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 { Username } from "../../../components/common/user/UserShort";
|
||||||
import UserStatus from "../../../components/common/user/UserStatus";
|
import UserStatus from "../../../components/common/user/UserStatus";
|
||||||
import IconButton from "../../../components/ui/IconButton";
|
import IconButton from "../../../components/ui/IconButton";
|
||||||
import Modal from "../../../components/ui/Modal";
|
import Modal from "../../../components/ui/Modal";
|
||||||
|
import Overline from "../../../components/ui/Overline";
|
||||||
import Preloader from "../../../components/ui/Preloader";
|
import Preloader from "../../../components/ui/Preloader";
|
||||||
|
|
||||||
import Markdown from "../../../components/markdown/Markdown";
|
import Markdown from "../../../components/markdown/Markdown";
|
||||||
|
@ -118,7 +120,9 @@ export const UserProfile = observer(
|
||||||
const backgroundURL =
|
const backgroundURL =
|
||||||
profile &&
|
profile &&
|
||||||
client.generateFileURL(profile.background, { width: 1000 }, true);
|
client.generateFileURL(profile.background, { width: 1000 }, true);
|
||||||
|
|
||||||
const badges = user.badges ?? 0;
|
const badges = user.badges ?? 0;
|
||||||
|
const flags = user.flags ?? 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
@ -229,11 +233,63 @@ export const UserProfile = observer(
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
{tab === "profile" && (
|
{tab === "profile" && (
|
||||||
<div>
|
<div>
|
||||||
{!(profile?.content || badges > 0) && (
|
{!(
|
||||||
|
profile?.content ||
|
||||||
|
badges > 0 ||
|
||||||
|
flags > 0 ||
|
||||||
|
user.bot
|
||||||
|
) && (
|
||||||
<div className={styles.empty}>
|
<div className={styles.empty}>
|
||||||
<Text id="app.special.popovers.user_profile.empty" />
|
<Text id="app.special.popovers.user_profile.empty" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{flags & 1 ? (
|
||||||
|
/** ! FIXME: i18n this area */
|
||||||
|
<Overline type="error" block>
|
||||||
|
User is suspended
|
||||||
|
</Overline>
|
||||||
|
) : undefined}
|
||||||
|
{flags & 2 ? (
|
||||||
|
<Overline type="error" block>
|
||||||
|
User deleted their account
|
||||||
|
</Overline>
|
||||||
|
) : undefined}
|
||||||
|
{flags & 4 ? (
|
||||||
|
<Overline type="error" block>
|
||||||
|
User is banned
|
||||||
|
</Overline>
|
||||||
|
) : undefined}
|
||||||
|
{user.bot ? (
|
||||||
|
<>
|
||||||
|
<div className={styles.category}>
|
||||||
|
bot owner
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
onClick={() =>
|
||||||
|
user.bot &&
|
||||||
|
openScreen({
|
||||||
|
id: "profile",
|
||||||
|
user_id: user.bot.owner,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className={styles.entry}
|
||||||
|
key={user.bot.owner}>
|
||||||
|
<UserIcon
|
||||||
|
size={32}
|
||||||
|
target={client.users.get(
|
||||||
|
user.bot.owner,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
<Username
|
||||||
|
user={client.users.get(
|
||||||
|
user.bot.owner,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : undefined}
|
||||||
{badges > 0 && (
|
{badges > 0 && (
|
||||||
<div className={styles.category}>
|
<div className={styles.category}>
|
||||||
<Text id="app.special.popovers.user_profile.sub.badges" />
|
<Text id="app.special.popovers.user_profile.sub.badges" />
|
||||||
|
|
|
@ -20,6 +20,7 @@ import Developer from "./developer/Developer";
|
||||||
import Friends from "./friends/Friends";
|
import Friends from "./friends/Friends";
|
||||||
import Home from "./home/Home";
|
import Home from "./home/Home";
|
||||||
import Invite from "./invite/Invite";
|
import Invite from "./invite/Invite";
|
||||||
|
import InviteBot from "./invite/InviteBot";
|
||||||
import ChannelSettings from "./settings/ChannelSettings";
|
import ChannelSettings from "./settings/ChannelSettings";
|
||||||
import ServerSettings from "./settings/ServerSettings";
|
import ServerSettings from "./settings/ServerSettings";
|
||||||
import Settings from "./settings/Settings";
|
import Settings from "./settings/Settings";
|
||||||
|
@ -119,6 +120,7 @@ export default function App() {
|
||||||
<Route path="/dev" component={Developer} />
|
<Route path="/dev" component={Developer} />
|
||||||
<Route path="/friends" component={Friends} />
|
<Route path="/friends" component={Friends} />
|
||||||
<Route path="/open/:id" component={Open} />
|
<Route path="/open/:id" component={Open} />
|
||||||
|
<Route path="/bot/:id" component={InviteBot} />
|
||||||
<Route path="/invite/:code" component={Invite} />
|
<Route path="/invite/:code" component={Invite} />
|
||||||
<Route path="/" component={Home} />
|
<Route path="/" component={Home} />
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
80
src/pages/invite/InviteBot.tsx
Normal file
80
src/pages/invite/InviteBot.tsx
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { Route } from "revolt.js/dist/api/routes";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
|
import { useClient } from "../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
|
import UserIcon from "../../components/common/user/UserIcon";
|
||||||
|
import Button from "../../components/ui/Button";
|
||||||
|
import ComboBox from "../../components/ui/ComboBox";
|
||||||
|
import Overline from "../../components/ui/Overline";
|
||||||
|
import Preloader from "../../components/ui/Preloader";
|
||||||
|
|
||||||
|
export default function InviteBot() {
|
||||||
|
const { id } = useParams<{ id: string }>();
|
||||||
|
const client = useClient();
|
||||||
|
const [data, setData] =
|
||||||
|
useState<Route<"GET", "/bots/id/invite">["response"]>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
client.bots.fetchPublic(id).then(setData);
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const [server, setServer] = useState("none");
|
||||||
|
const [group, setGroup] = useState("none");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: "6em" }}>
|
||||||
|
{typeof data === "undefined" && <Preloader type="spinner" />}
|
||||||
|
{data && (
|
||||||
|
<>
|
||||||
|
<UserIcon size={64} attachment={data.avatar} />
|
||||||
|
<h1>{data.username}</h1>
|
||||||
|
{data.description && <p>{data.description}</p>}
|
||||||
|
<Overline type="subtle">Add to server</Overline>
|
||||||
|
<ComboBox
|
||||||
|
value={server}
|
||||||
|
onChange={(e) => setServer(e.currentTarget.value)}>
|
||||||
|
<option value="none">un selected</option>
|
||||||
|
{[...client.servers.values()].map((server) => (
|
||||||
|
<option value={server._id} key={server._id}>
|
||||||
|
{server.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</ComboBox>
|
||||||
|
<Button
|
||||||
|
contrast
|
||||||
|
onClick={() =>
|
||||||
|
group !== "none" &&
|
||||||
|
client.bots.invite(data._id, { server })
|
||||||
|
}>
|
||||||
|
add
|
||||||
|
</Button>
|
||||||
|
<Overline type="subtle">Add to group</Overline>
|
||||||
|
<ComboBox
|
||||||
|
value={group}
|
||||||
|
onChange={(e) => setGroup(e.currentTarget.value)}>
|
||||||
|
<option value="none">un selected</option>
|
||||||
|
{[...client.channels.values()]
|
||||||
|
.filter((x) => x.channel_type === "Group")
|
||||||
|
.map((channel) => (
|
||||||
|
<option value={channel._id} key={channel._id}>
|
||||||
|
{channel.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</ComboBox>
|
||||||
|
<Button
|
||||||
|
contrast
|
||||||
|
onClick={() =>
|
||||||
|
group !== "none" &&
|
||||||
|
client.bots.invite(data._id, { group })
|
||||||
|
}>
|
||||||
|
add
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import {
|
||||||
Globe,
|
Globe,
|
||||||
LogOut,
|
LogOut,
|
||||||
Desktop,
|
Desktop,
|
||||||
|
Bot,
|
||||||
} from "@styled-icons/boxicons-regular";
|
} from "@styled-icons/boxicons-regular";
|
||||||
import {
|
import {
|
||||||
Bell,
|
Bell,
|
||||||
|
@ -39,6 +40,7 @@ import { Appearance } from "./panes/Appearance";
|
||||||
import { ExperimentsPage } from "./panes/Experiments";
|
import { ExperimentsPage } from "./panes/Experiments";
|
||||||
import { Feedback } from "./panes/Feedback";
|
import { Feedback } from "./panes/Feedback";
|
||||||
import { Languages } from "./panes/Languages";
|
import { Languages } from "./panes/Languages";
|
||||||
|
import { MyBots } from "./panes/MyBots";
|
||||||
import { Native } from "./panes/Native";
|
import { Native } from "./panes/Native";
|
||||||
import { Notifications } from "./panes/Notifications";
|
import { Notifications } from "./panes/Notifications";
|
||||||
import { Profile } from "./panes/Profile";
|
import { Profile } from "./panes/Profile";
|
||||||
|
@ -109,11 +111,17 @@ export default function Settings() {
|
||||||
title: <Text id="app.settings.pages.native.title" />,
|
title: <Text id="app.settings.pages.native.title" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
divider: true,
|
|
||||||
id: "experiments",
|
id: "experiments",
|
||||||
icon: <Flask size={20} />,
|
icon: <Flask size={20} />,
|
||||||
title: <Text id="app.settings.pages.experiments.title" />,
|
title: <Text id="app.settings.pages.experiments.title" />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
divider: true,
|
||||||
|
category: "revolt",
|
||||||
|
id: "bots",
|
||||||
|
icon: <Bot size={20} />,
|
||||||
|
title: <Text id="app.settings.pages.bots.title" />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "feedback",
|
id: "feedback",
|
||||||
icon: <Megaphone size={20} />,
|
icon: <Megaphone size={20} />,
|
||||||
|
@ -148,6 +156,9 @@ export default function Settings() {
|
||||||
<Route path="/settings/experiments">
|
<Route path="/settings/experiments">
|
||||||
<ExperimentsPage />
|
<ExperimentsPage />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="/settings/bots">
|
||||||
|
<MyBots />
|
||||||
|
</Route>
|
||||||
<Route path="/settings/feedback">
|
<Route path="/settings/feedback">
|
||||||
<Feedback />
|
<Feedback />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
160
src/pages/settings/panes/MyBots.tsx
Normal file
160
src/pages/settings/panes/MyBots.tsx
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { Bot } from "revolt-api/types/Bots";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
|
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||||
|
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
|
import UserShort from "../../../components/common/user/UserShort";
|
||||||
|
import Button from "../../../components/ui/Button";
|
||||||
|
import Checkbox from "../../../components/ui/Checkbox";
|
||||||
|
import InputBox from "../../../components/ui/InputBox";
|
||||||
|
import Overline from "../../../components/ui/Overline";
|
||||||
|
import Tip from "../../../components/ui/Tip";
|
||||||
|
|
||||||
|
interface Data {
|
||||||
|
_id: string;
|
||||||
|
username: string;
|
||||||
|
public: boolean;
|
||||||
|
interactions_url?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function BotEditor({ bot }: { bot: Data }) {
|
||||||
|
const client = useClient();
|
||||||
|
const [data, setData] = useState<Data>(bot);
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
const changes: Record<string, string | boolean | undefined> = {};
|
||||||
|
if (data.username !== bot.username) changes.name = data.username;
|
||||||
|
if (data.public !== bot.public) changes.public = data.public;
|
||||||
|
if (data.interactions_url !== bot.interactions_url)
|
||||||
|
changes.interactions_url = data.interactions_url;
|
||||||
|
|
||||||
|
client.bots.edit(bot._id, changes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<InputBox
|
||||||
|
value={data.username}
|
||||||
|
onChange={(e) =>
|
||||||
|
setData({ ...data, username: e.currentTarget.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<Checkbox
|
||||||
|
checked={data.public}
|
||||||
|
onChange={(v) => setData({ ...data, public: v })}>
|
||||||
|
is public
|
||||||
|
</Checkbox>
|
||||||
|
</p>
|
||||||
|
<p>interactions url: (reserved for the future)</p>
|
||||||
|
<p>
|
||||||
|
<InputBox
|
||||||
|
value={data.interactions_url}
|
||||||
|
onChange={(e) =>
|
||||||
|
setData({
|
||||||
|
...data,
|
||||||
|
interactions_url: e.currentTarget.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<Button onClick={save}>save</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MyBots = observer(() => {
|
||||||
|
const client = useClient();
|
||||||
|
const [bots, setBots] = useState<Bot[] | undefined>(undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
client.bots.fetchOwned().then(({ bots }) => setBots(bots));
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
const { writeClipboard } = useIntermediate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Tip warning hideSeparator>
|
||||||
|
This section is under construction.
|
||||||
|
</Tip>
|
||||||
|
<Overline>create a new bot</Overline>
|
||||||
|
<p>
|
||||||
|
<InputBox
|
||||||
|
value={name}
|
||||||
|
contrast
|
||||||
|
onChange={(e) => setName(e.currentTarget.value)}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<Button
|
||||||
|
contrast
|
||||||
|
onClick={() =>
|
||||||
|
name.length > 0 &&
|
||||||
|
client.bots
|
||||||
|
.create({ name })
|
||||||
|
.then(({ bot }) => setBots([...(bots ?? []), bot]))
|
||||||
|
}>
|
||||||
|
create
|
||||||
|
</Button>
|
||||||
|
</p>
|
||||||
|
<Overline>my bots</Overline>
|
||||||
|
{bots?.map((bot) => {
|
||||||
|
const user = client.users.get(bot._id);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={bot._id}
|
||||||
|
style={{
|
||||||
|
background: "var(--secondary-background)",
|
||||||
|
margin: "8px",
|
||||||
|
padding: "12px",
|
||||||
|
}}>
|
||||||
|
<UserShort user={user} />
|
||||||
|
<p>
|
||||||
|
token:{" "}
|
||||||
|
<code style={{ userSelect: "all" }}>
|
||||||
|
{bot.token}
|
||||||
|
</code>
|
||||||
|
</p>
|
||||||
|
<BotEditor
|
||||||
|
bot={{
|
||||||
|
...bot,
|
||||||
|
username: user!.username,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
error
|
||||||
|
onClick={() =>
|
||||||
|
client.bots
|
||||||
|
.delete(bot._id)
|
||||||
|
.then(() =>
|
||||||
|
setBots(
|
||||||
|
bots.filter(
|
||||||
|
(x) => x._id !== bot._id,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}>
|
||||||
|
delete
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
writeClipboard(
|
||||||
|
`${window.origin}/bot/${bot._id}`,
|
||||||
|
)
|
||||||
|
}>
|
||||||
|
copy invite link
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
16
yarn.lock
16
yarn.lock
|
@ -3600,15 +3600,15 @@ reusify@^1.0.4:
|
||||||
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
||||||
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
||||||
|
|
||||||
revolt-api@^0.5.2-alpha.0:
|
revolt-api@0.5.2-alpha.1:
|
||||||
version "0.5.2-alpha.0"
|
version "0.5.2-alpha.1"
|
||||||
resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.2-alpha.0.tgz#a41f44ee38622636c9b5b5843f9e2798a79f00d3"
|
resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.2-alpha.1.tgz#2164d04cd5581267ce59142557666bd386bc85c4"
|
||||||
integrity sha512-VI/o4nQTPXrDCVdFpZFfZfj7Q4nunj62gftdmYJtuSmXx+6eN2Nve7QQZjNt6UIH6Dc/IDgiFDcBdafBF9YXug==
|
integrity sha512-3OrjYCDNPkJ+yO9d87NJvuUDAbungEbUfrfHlvFwV8hJze/RMkuYUTFWe1HyBMwBC7F/yWQK+2V7IoifC5STmw==
|
||||||
|
|
||||||
revolt.js@5.0.0-alpha.21:
|
revolt.js@5.0.1-alpha.3:
|
||||||
version "5.0.0-alpha.21"
|
version "5.0.1-alpha.3"
|
||||||
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-5.0.0-alpha.21.tgz#24e01dbcb2887dadcb480732a1b9b8f167c557b5"
|
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-5.0.1-alpha.3.tgz#986d2ec21d751067d95c4f444f81b922df566cde"
|
||||||
integrity sha512-UNRJRCyKoOFKULRYIWFZ3QN4th6s/sgMpQXtqaitVMtVBo6BJJvUT9wUM3WV08pN1acr3EPwnVre6sOtKM7khg==
|
integrity sha512-h1xlaBvKyTS+wF9Oe4rtjuTe5plrOpYMp9qskqxMeNIoVu9VuJjHU+n9YUWANbgn7Ji9sxPHZrco5+0+bLOCcg==
|
||||||
dependencies:
|
dependencies:
|
||||||
axios "^0.19.2"
|
axios "^0.19.2"
|
||||||
eventemitter3 "^4.0.7"
|
eventemitter3 "^4.0.7"
|
||||||
|
|
Loading…
Reference in a new issue