diff --git a/external/lang b/external/lang index 7be90cf4..b27044c3 160000 --- a/external/lang +++ b/external/lang @@ -1 +1 @@ -Subproject commit 7be90cf44ba08d235ae52d7dc6073d8f9347232b +Subproject commit b27044c332ca419d22edbe9e7bf465a665398999 diff --git a/package.json b/package.json index bf6f3e31..27dc0a6b 100644 --- a/package.json +++ b/package.json @@ -115,8 +115,8 @@ "react-virtualized-auto-sizer": "^1.0.5", "react-virtuoso": "^1.10.4", "redux": "^4.1.0", - "revolt-api": "^0.5.2-alpha.0", - "revolt.js": "5.0.0-alpha.21", + "revolt-api": "0.5.2-alpha.1", + "revolt.js": "5.0.1-alpha.3", "rimraf": "^3.0.2", "sass": "^1.35.1", "shade-blend-color": "^1.0.0", diff --git a/src/components/common/user/UserShort.tsx b/src/components/common/user/UserShort.tsx index c2d760dc..4f9c15f8 100644 --- a/src/components/common/user/UserShort.tsx +++ b/src/components/common/user/UserShort.tsx @@ -1,6 +1,7 @@ import { observer } from "mobx-react-lite"; import { useParams } from "react-router-dom"; import { User } from "revolt.js/dist/maps/Users"; +import styled from "styled-components"; import { Text } from "preact-i18n"; @@ -9,6 +10,21 @@ import { useClient } from "../../../context/revoltjs/RevoltClient"; 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( ({ user, @@ -51,6 +67,21 @@ export const Username = observer( } } + if (user?.bot) { + return ( + <> + + {username ?? ( + + )} + + + + + + ); + } + return ( {username ?? } diff --git a/src/components/navigation/right/Search.tsx b/src/components/navigation/right/Search.tsx index 1926a54f..d6e2b2fc 100644 --- a/src/components/navigation/right/Search.tsx +++ b/src/components/navigation/right/Search.tsx @@ -13,7 +13,7 @@ import InputBox from "../../ui/InputBox"; import Overline from "../../ui/Overline"; import Preloader from "../../ui/Preloader"; -import { GenericSidebarBase } from "../SidebarBase"; +import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase"; type SearchState = | { @@ -100,57 +100,59 @@ export function SearchSidebar({ close }: Props) { return ( - - - « back to members - - - - - e.key === "Enter" && search()} - onChange={(e) => setQuery(e.currentTarget.value)} - /> -
- {["Latest", "Oldest", "Relevance"].map((key) => ( - - ))} -
- {state.type === "loading" && } - {state.type === "results" && ( -
- {state.results.map((message) => { - let href = ""; - if (channel?.channel_type === "TextChannel") { - href += `/server/${channel.server_id}`; - } - - href += `/channel/${message.channel_id}/${message._id}`; - - return ( - -
- -
- - ); - })} + + + + « back to members + + + + + e.key === "Enter" && search()} + onChange={(e) => setQuery(e.currentTarget.value)} + /> +
+ {["Latest", "Oldest", "Relevance"].map((key) => ( + + ))}
- )} -
+ {state.type === "loading" && } + {state.type === "results" && ( +
+ {state.results.map((message) => { + let href = ""; + if (channel?.channel_type === "TextChannel") { + href += `/server/${channel.server_id}`; + } + + href += `/channel/${message.channel_id}/${message._id}`; + + return ( + +
+ +
+ + ); + })} +
+ )} + +
); } diff --git a/src/context/intermediate/popovers/ServerIdentityModal.tsx b/src/context/intermediate/popovers/ServerIdentityModal.tsx index eb6e449e..3ef55266 100644 --- a/src/context/intermediate/popovers/ServerIdentityModal.tsx +++ b/src/context/intermediate/popovers/ServerIdentityModal.tsx @@ -7,6 +7,7 @@ import Button from "../../../components/ui/Button"; import InputBox from "../../../components/ui/InputBox"; import Modal from "../../../components/ui/Modal"; import Overline from "../../../components/ui/Overline"; +import Tip from "../../../components/ui/Tip"; import { FileUploader } from "../../revoltjs/FileUploads"; import { useClient } from "../../revoltjs/RevoltClient"; @@ -30,6 +31,9 @@ export const ServerIdentityModal = observer(({ server, onClose }: Props) => { return ( + + This section is under construction. + Nickname

{tab === "profile" && (

- {!(profile?.content || badges > 0) && ( + {!( + profile?.content || + badges > 0 || + flags > 0 || + user.bot + ) && (
)} + {flags & 1 ? ( + /** ! FIXME: i18n this area */ + + User is suspended + + ) : undefined} + {flags & 2 ? ( + + User deleted their account + + ) : undefined} + {flags & 4 ? ( + + User is banned + + ) : undefined} + {user.bot ? ( + <> +
+ bot owner +
+
+ user.bot && + openScreen({ + id: "profile", + user_id: user.bot.owner, + }) + } + className={styles.entry} + key={user.bot.owner}> + + + + +
+ + ) : undefined} {badges > 0 && (
diff --git a/src/pages/RevoltApp.tsx b/src/pages/RevoltApp.tsx index bc309c30..7d861746 100644 --- a/src/pages/RevoltApp.tsx +++ b/src/pages/RevoltApp.tsx @@ -20,6 +20,7 @@ import Developer from "./developer/Developer"; import Friends from "./friends/Friends"; import Home from "./home/Home"; import Invite from "./invite/Invite"; +import InviteBot from "./invite/InviteBot"; import ChannelSettings from "./settings/ChannelSettings"; import ServerSettings from "./settings/ServerSettings"; import Settings from "./settings/Settings"; @@ -119,6 +120,7 @@ export default function App() { + diff --git a/src/pages/invite/InviteBot.tsx b/src/pages/invite/InviteBot.tsx new file mode 100644 index 00000000..dd7f72a0 --- /dev/null +++ b/src/pages/invite/InviteBot.tsx @@ -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["response"]>(); + + useEffect(() => { + client.bots.fetchPublic(id).then(setData); + // eslint-disable-next-line + }, []); + + const [server, setServer] = useState("none"); + const [group, setGroup] = useState("none"); + + return ( +
+ {typeof data === "undefined" && } + {data && ( + <> + +

{data.username}

+ {data.description &&

{data.description}

} + Add to server + setServer(e.currentTarget.value)}> + + {[...client.servers.values()].map((server) => ( + + ))} + + + Add to group + setGroup(e.currentTarget.value)}> + + {[...client.channels.values()] + .filter((x) => x.channel_type === "Group") + .map((channel) => ( + + ))} + + + + )} +
+ ); +} diff --git a/src/pages/settings/Settings.tsx b/src/pages/settings/Settings.tsx index 7d51cc44..315a782d 100644 --- a/src/pages/settings/Settings.tsx +++ b/src/pages/settings/Settings.tsx @@ -4,6 +4,7 @@ import { Globe, LogOut, Desktop, + Bot, } from "@styled-icons/boxicons-regular"; import { Bell, @@ -39,6 +40,7 @@ import { Appearance } from "./panes/Appearance"; import { ExperimentsPage } from "./panes/Experiments"; import { Feedback } from "./panes/Feedback"; import { Languages } from "./panes/Languages"; +import { MyBots } from "./panes/MyBots"; import { Native } from "./panes/Native"; import { Notifications } from "./panes/Notifications"; import { Profile } from "./panes/Profile"; @@ -109,11 +111,17 @@ export default function Settings() { title: , }, { - divider: true, id: "experiments", icon: , title: , }, + { + divider: true, + category: "revolt", + id: "bots", + icon: , + title: , + }, { id: "feedback", icon: , @@ -148,6 +156,9 @@ export default function Settings() { + + + diff --git a/src/pages/settings/panes/MyBots.tsx b/src/pages/settings/panes/MyBots.tsx new file mode 100644 index 00000000..5e558954 --- /dev/null +++ b/src/pages/settings/panes/MyBots.tsx @@ -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(bot); + + function save() { + const changes: Record = {}; + 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 ( +
+

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

+

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

+

interactions url: (reserved for the future)

+

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

+ +
+ ); +} + +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 [name, setName] = useState(""); + const { writeClipboard } = useIntermediate(); + + return ( +
+ + This section is under construction. + + create a new bot +

+ setName(e.currentTarget.value)} + /> +

+

+ +

+ my bots + {bots?.map((bot) => { + const user = client.users.get(bot._id); + return ( +
+ +

+ token:{" "} + + {bot.token} + +

+ + + +
+ ); + })} +
+ ); +}); diff --git a/yarn.lock b/yarn.lock index 7d7fe555..53ec96e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3600,15 +3600,15 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -revolt-api@^0.5.2-alpha.0: - version "0.5.2-alpha.0" - resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.2-alpha.0.tgz#a41f44ee38622636c9b5b5843f9e2798a79f00d3" - integrity sha512-VI/o4nQTPXrDCVdFpZFfZfj7Q4nunj62gftdmYJtuSmXx+6eN2Nve7QQZjNt6UIH6Dc/IDgiFDcBdafBF9YXug== +revolt-api@0.5.2-alpha.1: + version "0.5.2-alpha.1" + resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.2-alpha.1.tgz#2164d04cd5581267ce59142557666bd386bc85c4" + integrity sha512-3OrjYCDNPkJ+yO9d87NJvuUDAbungEbUfrfHlvFwV8hJze/RMkuYUTFWe1HyBMwBC7F/yWQK+2V7IoifC5STmw== -revolt.js@5.0.0-alpha.21: - version "5.0.0-alpha.21" - resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-5.0.0-alpha.21.tgz#24e01dbcb2887dadcb480732a1b9b8f167c557b5" - integrity sha512-UNRJRCyKoOFKULRYIWFZ3QN4th6s/sgMpQXtqaitVMtVBo6BJJvUT9wUM3WV08pN1acr3EPwnVre6sOtKM7khg== +revolt.js@5.0.1-alpha.3: + version "5.0.1-alpha.3" + resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-5.0.1-alpha.3.tgz#986d2ec21d751067d95c4f444f81b922df566cde" + integrity sha512-h1xlaBvKyTS+wF9Oe4rtjuTe5plrOpYMp9qskqxMeNIoVu9VuJjHU+n9YUWANbgn7Ji9sxPHZrco5+0+bLOCcg== dependencies: axios "^0.19.2" eventemitter3 "^4.0.7"