Skip to content
Snippets Groups Projects
Commit f81f7768 authored by insert's avatar insert
Browse files

New design for server roles editor.

parent c4bbd1e4
Branches
No related merge requests found
Subproject commit ec907eb606a3e1d5046bef503caa0585f6bcbc22
Subproject commit 9bb62d1185f7e6f7a3821751797e30cb41e74bf8
......@@ -78,7 +78,7 @@
"react-router-dom": "^5.2.0",
"react-scroll": "^1.8.2",
"redux": "^4.1.0",
"revolt.js": "4.3.3-alpha.6",
"revolt.js": "4.3.3-alpha.7",
"rimraf": "^3.0.2",
"sass": "^1.35.1",
"shade-blend-color": "^1.0.0",
......
......@@ -61,6 +61,7 @@ export default styled.button<Props>`
&:hover {
filter: brightness(1.2);
background: var(--error);
}
&:disabled {
......
......@@ -31,6 +31,15 @@ const CheckboxBase = styled.label`
background: var(--background);
}
}
&[disabled] {
opacity: 0.5;
cursor: unset;
&:hover {
background: unset;
}
}
`;
const CheckboxContent = styled.span`
......@@ -52,6 +61,7 @@ const Checkmark = styled.div<{ checked: boolean }>`
width: 24px;
height: 24px;
display: grid;
flex-shrink: 0;
border-radius: 4px;
place-items: center;
transition: 0.2s ease all;
......
......@@ -31,7 +31,8 @@ export type Screen =
{ type: "create_channel", target: Servers.Server }
)) |
({ id: "special_input" } & (
{ type: "create_group" | "create_server" | "set_custom_status" | "add_friend" }
{ type: "create_group" | "create_server" | "set_custom_status" | "add_friend" } |
{ type: "create_role", server: string, callback: (id: string) => void }
))
| {
id: "_input";
......
......@@ -68,7 +68,8 @@ export function InputModal({
}
type SpecialProps = { onClose: () => void } & (
{ type: "create_group" | "create_server" | "set_custom_status" | "add_friend" }
{ type: "create_group" | "create_server" | "set_custom_status" | "add_friend" } |
{ type: "create_role", server: string, callback: (id: string) => void }
)
export function SpecialInputModal(props: SpecialProps) {
......@@ -112,6 +113,17 @@ export function SpecialInputModal(props: SpecialProps) {
}}
/>;
}
case "create_role": {
return <InputModal
onClose={onClose}
question={<Text id="app.settings.permissions.create_role" />}
field={<Text id="app.settings.permissions.role_name" />}
callback={async name => {
const role = await client.servers.createRole(props.server, name);
props.callback(role.id);
}}
/>;
}
case "set_custom_status": {
return <InputModal
onClose={onClose}
......
......@@ -57,7 +57,6 @@
}
.members {
.subtitle {
display: flex;
justify-content: space-between;
......@@ -84,15 +83,43 @@
.list {
width: 160px;
flex-shrink: 0;
overflow-y: scroll;
}
.permissions {
flex-grow: 1;
padding: 0 8px;
overflow-y: scroll;
section {
margin-bottom: 1em;
}
}
.title {
gap: 8px;
display: flex;
margin-bottom: 1em;
align-items: center;
h1, h2 {
margin: 0;
min-width: 0;
flex-grow: 1;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
svg {
cursor: pointer;
}
}
h2 {
margin: 8px 0;
.actions {
gap: 8px;
display: flex;
padding: 8px 0;
}
}
\ No newline at end of file
import { Text } from "preact-i18n";
import styles from './Panes.module.scss';
import Button from "../../../components/ui/Button";
import Overline from "../../../components/ui/Overline";
import { Servers } from "revolt.js/dist/api/objects";
import InputBox from "../../../components/ui/InputBox";
import Checkbox from "../../../components/ui/Checkbox";
import { useContext, useEffect, useState } from "preact/hooks";
import { AppContext } from "../../../context/revoltjs/RevoltClient";
import { ChannelPermission, ServerPermission } from "revolt.js/dist/api/permissions";
import Tip from "../../../components/ui/Tip";
import IconButton from "../../../components/ui/IconButton";
import ButtonItem from "../../../components/navigation/items/ButtonItem";
import isEqual from 'lodash.isequal';
import InputBox from "../../../components/ui/InputBox";
import { Plus } from "@styled-icons/boxicons-regular";
import { useIntermediate } from "../../../context/intermediate/Intermediate";
interface Props {
server: Servers.Server;
}
const I32ToU32 = (arr: number[]) => arr.map(x => x >>> 0);
// ! FIXME: bad code :)
export function Roles({ server }: Props) {
const [ selected, setSelected ] = useState('default');
const [ role, setRole ] = useState('default');
const { openScreen } = useIntermediate();
const client = useContext(AppContext);
const roles = server.roles ?? {};
const keys = [ 'default', ...Object.keys(roles) ];
const defaultRole = { name: 'Default', permissions: server.default_permissions };
const selectedRole: Servers.Role = selected === 'default' ? defaultRole : roles[selected];
if (!selectedRole) {
useEffect(() => setSelected('default'), [ ]);
return null;
if (role !== 'default' && typeof roles[role] === 'undefined') {
useEffect(() => setRole('default'));
return;
}
const [ p, setPerm ] = useState([
selectedRole.permissions[0] >>> 0,
selectedRole.permissions[1] >>> 0,
]);
useEffect(() => {
setPerm([
selectedRole.permissions[0] >>> 0,
selectedRole.permissions[1] >>> 0,
]);
}, [ selected, selectedRole.permissions ]);
const v = (id: string) => I32ToU32(id === 'default' ? server.default_permissions : roles[id].permissions)
const [ perm, setPerm ] = useState(v(role));
useEffect(() => setPerm(v(role)), [ role, roles[role]?.permissions ]);
const [ name, setName ] = useState('');
const modified = !isEqual(perm, v(role));
const save = () => client.servers.setPermissions(server._id, role, { server: perm[0], channel: perm[1] });
const deleteRole = () => {
setRole('default');
client.servers.deleteRole(server._id, role);
};
return (
<div className={styles.roles}>
<Tip warning>This section is under construction.</Tip>
<div className={styles.list}>
<h1><Text id="app.settings.server_pages.roles.title" /></h1>
{ keys
<div className={styles.title}>
<h1><Text id="app.settings.server_pages.roles.title" /></h1>
<Plus size={16} onClick={() =>
openScreen({ id: 'special_input', type: 'create_role', server: server._id, callback: id => setRole(id) })} />
</div>
{ [ 'default', ...Object.keys(roles) ]
.map(id => {
let role: Servers.Role = id === 'default' ? defaultRole : roles[id];
return (
<Checkbox checked={selected === id} onChange={selected => selected && setSelected(id)}>
{ role.name }
</Checkbox>
)
if (id === 'default') {
return (
<ButtonItem active={role === 'default'} onClick={() => setRole('default')}>
<Text id="app.settings.permissions.default_role" />
</ButtonItem>
)
} else {
return (
<ButtonItem active={role === id} onClick={() => setRole(id)}>
{ roles[id].name }
</ButtonItem>
)
}
})
}
<Button disabled={selected === 'default'} error onClick={() => {
setSelected('default');
client.servers.deleteRole(server._id, selected);
}}>delete role</Button><br/>
<InputBox placeholder="role name" value={name} onChange={e => setName(e.currentTarget.value)} />
<Button contrast onClick={() => {
client.servers.createRole(server._id, name);
}}>create</Button>
</div>
<div className={styles.permissions}>
<h2>{ selectedRole.name }</h2>
{ Object.keys(ServerPermission)
.map(perm => {
let value = ServerPermission[perm as keyof typeof ServerPermission];
<div className={styles.title}>
<h2>{ role === 'default' ? <Text id="app.settings.permissions.default_role" /> : roles[role].name }</h2>
<Button contrast disabled={!modified} onClick={save}>Save</Button>
</div>
<section>
<Overline type="subtle"><Text id="app.settings.permissions.server" /></Overline>
{ Object.keys(ServerPermission)
.map(key => {
if (key === 'View') return;
let value = ServerPermission[key as keyof typeof ServerPermission];
return (
<Checkbox checked={(p[0] & value) > 0} onChange={c => setPerm([ c ? (p[0] | value) : (p[0] ^ value), p[1] ])}>
{ perm }
</Checkbox>
)
})
}
<h2>channel permmissions</h2>
{ Object.keys(ChannelPermission)
.map(perm => {
let value = ChannelPermission[perm as keyof typeof ChannelPermission];
return (
<Checkbox checked={(perm[0] & value) > 0}
onChange={() => setPerm([ perm[0] ^ value, perm[1] ])}
description={<Text id={`permissions.server.${key}.d`} />}>
<Text id={`permissions.server.${key}.t`} />
</Checkbox>
)
})
}
</section>
<section>
<Overline type="subtle"><Text id="app.settings.permissions.channel" /></Overline>
{ Object.keys(ChannelPermission)
.map(key => {
if (key === 'ManageChannel') return;
let value = ChannelPermission[key as keyof typeof ChannelPermission];
return (
<Checkbox checked={((p[1] >>> 0) & value) > 0} onChange={c => setPerm([ p[0], c ? (p[1] | value) : (p[1] ^ value) ])}>
{ perm }
</Checkbox>
)
})
}
<Button contrast onClick={() => {
client.servers.setPermissions(server._id, selected, { server: p[0], channel: p[1] });
}}>click here to save permissions for role</Button>
return (
<Checkbox checked={((perm[1] >>> 0) & value) > 0}
onChange={() => setPerm([ perm[0], perm[1] ^ value ])}
disabled={key === 'View'}
description={<Text id={`permissions.channel.${key}.d`} />}>
<Text id={`permissions.channel.${key}.t`} />
</Checkbox>
)
})
}
</section>
<div className={styles.actions}>
<Button contrast disabled={!modified} onClick={save}>Save</Button>
{ role !== 'default' && <Button contrast error onClick={deleteRole}>Delete</Button> }
</div>
</div>
</div>
);
......
......@@ -3420,10 +3420,10 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
revolt.js@4.3.3-alpha.6:
version "4.3.3-alpha.6"
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.3-alpha.6.tgz#054e685a5c0dac2c7ae3e2aa454d1965218cb2b0"
integrity sha512-u1/xf+YSQr8DbKsO0raym+F05R75bqYadrPWaIie3m2s2p7ZWeamHlfWIKJlmDO5AL+Lg3xoZWoLwuRHrD1K/Q==
revolt.js@4.3.3-alpha.7:
version "4.3.3-alpha.7"
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.3-alpha.7.tgz#de6ecef444e8368aac3753761e2e10f516f50712"
integrity sha512-oi76A+EIxrD+tVRTU8s2LISFBpvMf0kpinw5rdukoc1VWpl0bCC6Kko26yC7lhVkWGLTZxHMOKaUkgbOgy0flA==
dependencies:
"@insertish/mutable" "1.1.0"
axios "^0.19.2"
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment