Port settings.

This commit is contained in:
Paul
2021-06-19 22:37:12 +01:00
parent b4bc2262ae
commit 31d8950ea1
48 changed files with 3056 additions and 106 deletions

View File

@@ -0,0 +1,23 @@
import { Servers } from "revolt.js/dist/api/objects";
import { useContext, useEffect, useState } from "preact/hooks";
import { AppContext } from "../../../context/revoltjs/RevoltClient";
interface Props {
server: Servers.Server;
}
export function Bans({ server }: Props) {
const client = useContext(AppContext);
const [bans, setBans] = useState<Servers.Ban[] | undefined>(undefined);
useEffect(() => {
client.servers.fetchBans(server._id)
.then(bans => setBans(bans))
}, [ ]);
return (
<div>
{ bans?.map(x => <div>{x._id.user}: {x.reason ?? 'no reason'} <button onClick={() => client.servers.unbanUser(server._id, x._id.user)}>unban</button></div>) }
</div>
);
}

View File

@@ -0,0 +1,70 @@
import styles from './Panes.module.scss';
import { XCircle } from "@styled-icons/feather";
import { useEffect, useState } from "preact/hooks";
import Preloader from "../../../components/ui/Preloader";
import UserIcon from "../../../components/common/UserIcon";
import IconButton from "../../../components/ui/IconButton";
import { getChannelName } from "../../../context/revoltjs/util";
import { Invites as InvitesNS, Servers } from "revolt.js/dist/api/objects";
import { useChannels, useForceUpdate, useUsers } from "../../../context/revoltjs/hooks";
interface Props {
server: Servers.Server;
}
export function Invites({ server }: Props) {
const [invites, setInvites] = useState<InvitesNS.ServerInvite[] | undefined>(undefined);
const ctx = useForceUpdate();
const [deleting, setDelete] = useState<string[]>([]);
const users = useUsers(invites?.map(x => x.creator) ?? [], ctx);
const channels = useChannels(invites?.map(x => x.channel) ?? [], ctx);
useEffect(() => {
ctx.client.servers.fetchInvites(server._id)
.then(invites => setInvites(invites))
}, [ ]);
return (
<div className={styles.invites}>
{ typeof invites === 'undefined' && <Preloader /> }
{
invites?.map(
invite => {
let creator = users.find(x => x?._id === invite.creator);
let channel = channels.find(x => x?._id === invite.channel);
return (
<div className={styles.invite}
data-deleting={deleting.indexOf(invite._id) > -1}>
<code>{ invite._id }</code>
<span>
<UserIcon target={creator} size={24} /> {creator?.username ?? 'unknown'}
</span>
<span>{ (channel && creator) ? getChannelName(ctx.client, channel, [ creator ], true) : '#unknown' }</span>
<IconButton
onClick={async () => {
setDelete([
...deleting,
invite._id
]);
await ctx.client.deleteInvite(invite._id);
setInvites(
invites?.filter(
x => x._id !== invite._id
)
);
}}
disabled={deleting.indexOf(invite._id) > -1}>
<XCircle size={24} />
</IconButton>
</div>
)
}
)
}
</div>
);
}

View File

@@ -0,0 +1,24 @@
import { useEffect, useState } from "preact/hooks";
import { Servers } from "revolt.js/dist/api/objects";
import { useForceUpdate, useUsers } from "../../../context/revoltjs/hooks";
interface Props {
server: Servers.Server;
}
export function Members({ server }: Props) {
const [members, setMembers] = useState<Servers.Member[] | undefined>(undefined);
const ctx = useForceUpdate();
const users = useUsers(members?.map(x => x._id.user) ?? [], ctx);
useEffect(() => {
ctx.client.servers.members.fetchMembers(server._id)
.then(members => setMembers(members))
}, [ ]);
return (
<div>
{ members && members.length > 0 && users?.map(x => x && <div>@{x.username}</div>) }
</div>
);
}

View File

@@ -0,0 +1,98 @@
import { Text } from "preact-i18n";
import styles from './Panes.module.scss';
import Button from "../../../components/ui/Button";
import { Servers } from "revolt.js/dist/api/objects";
import { SettingsTextArea } from "../SettingsTextArea";
import InputBox from "../../../components/ui/InputBox";
import { useContext, useEffect, useState } from "preact/hooks";
import { AppContext } from "../../../context/revoltjs/RevoltClient";
import { FileUploader } from "../../../context/revoltjs/FileUploads";
interface Props {
server: Servers.Server;
}
export function Overview({ server }: Props) {
const client = useContext(AppContext);
const [name, setName] = useState(server.name);
const [description, setDescription] = useState(server.description ?? '');
useEffect(() => setName(server.name), [ server.name ]);
useEffect(() => setDescription(server.description ?? ''), [ server.description ]);
const [ changed, setChanged ] = useState(false);
function save() {
let changes: any = {};
if (name !== server.name) changes.name = name;
if (description !== server.description)
changes.description = description;
client.servers.edit(server._id, changes);
setChanged(false);
}
return (
<div className={styles.overview}>
<div className={styles.row}>
<FileUploader
width={80}
height={80}
style="icon"
fileType="icons"
behaviour="upload"
maxFileSize={2_500_000}
onUpload={icon => client.servers.edit(server._id, { icon })}
previewURL={client.servers.getIconURL(server._id, { max_side: 256 }, true)}
remove={() => client.servers.edit(server._id, { remove: 'Icon' })}
/>
<div className={styles.name}>
<h3>
<Text id="app.main.servers.name" />
</h3>
<InputBox
contrast
value={name}
maxLength={32}
onChange={e => {
setName(e.currentTarget.value)
if (!changed) setChanged(true)
}}
/>
</div>
</div>
<h3>
<Text id="app.main.servers.description" />
</h3>
<SettingsTextArea
maxRows={10}
minHeight={60}
maxLength={1024}
value={description}
placeholder={"Add a topic..."}
onChange={content => {
setDescription(content);
if (!changed) setChanged(true)
}}
/>
<Button onClick={save} style="contrast" disabled={!changed}>
<Text id="app.special.modals.actions.save" />
</Button>
<h3>
<Text id="app.main.servers.custom_banner" />
</h3>
<FileUploader
height={160}
style="banner"
fileType="banners"
behaviour="upload"
maxFileSize={6_000_000}
onUpload={banner => client.servers.edit(server._id, { banner })}
previewURL={client.servers.getBannerURL(server._id, { width: 1000 }, true)}
remove={() => client.servers.edit(server._id, { remove: 'Banner' })}
/>
</div>
);
}

View File

@@ -0,0 +1,48 @@
.overview {
.row {
gap: 20px;
display: flex;
.name {
flex-grow: 1;
input {
width: 100%;
}
}
}
}
.invites {
gap: 8px;
display: flex;
flex-direction: column;
.invite {
gap: 8px;
padding: 8px;
display: flex;
align-items: center;
flex-direction: row;
background: var(--secondary-background);
code, span {
flex: 1;
}
code {
font-size: 1.4em;
user-select: all;
}
span {
gap: 8px;
display: flex;
color: var(--secondary-foreground);
}
&[data-deleting="true"] {
opacity: 0.5;
}
}
}