parent
13c726c8a1
commit
f29251529a
|
|
@ -7,6 +7,9 @@ dist-ssr
|
|||
*.log
|
||||
/.idea
|
||||
|
||||
.yarn/cache
|
||||
.yarn/install-state.gz
|
||||
|
||||
public/assets
|
||||
public/assets_*
|
||||
!public/assets_default
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,9 @@
|
|||
nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
|
||||
spec: "@yarnpkg/plugin-typescript"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.2.0.cjs
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit c7fd514d4d83561e1802ae722501192d316cfe50
|
||||
Subproject commit 1c8fd46b5005691f2a144151b4406d3f839a7822
|
||||
17
package.json
17
package.json
|
|
@ -61,7 +61,6 @@
|
|||
"dependencies": {
|
||||
"@fontsource/bitter": "^4.5.0",
|
||||
"@insertish/vite-plugin-babel-macros": "^1.0.5",
|
||||
"color-rgba": "^2.3.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"klaw": "^3.0.0",
|
||||
"react-beautiful-dnd": "^13.1.0",
|
||||
|
|
@ -90,16 +89,16 @@
|
|||
"@fontsource/ubuntu-mono": "^4.4.5",
|
||||
"@hcaptcha/react-hcaptcha": "^0.3.6",
|
||||
"@preact/preset-vite": "^2.0.0",
|
||||
"@revoltchat/ui": "1.0.27",
|
||||
"@rollup/plugin-replace": "^2.4.2",
|
||||
"@styled-icons/boxicons-logos": "^10.34.0",
|
||||
"@styled-icons/boxicons-regular": "^10.34.0",
|
||||
"@styled-icons/boxicons-solid": "^10.37.0",
|
||||
"@styled-icons/boxicons-logos": "^10.38.0",
|
||||
"@styled-icons/boxicons-regular": "^10.38.0",
|
||||
"@styled-icons/boxicons-solid": "^10.38.0",
|
||||
"@styled-icons/simple-icons": "^10.33.0",
|
||||
"@tippyjs/react": "^4.2.5",
|
||||
"@traptitech/markdown-it-katex": "^3.4.3",
|
||||
"@traptitech/markdown-it-spoiler": "^1.1.6",
|
||||
"@trivago/prettier-plugin-sort-imports": "^2.0.2",
|
||||
"@types/color-rgba": "^2.1.0",
|
||||
"@types/lodash.defaultsdeep": "^4.6.6",
|
||||
"@types/lodash.isequal": "^4.5.5",
|
||||
"@types/markdown-it": "^12.0.2",
|
||||
|
|
@ -116,6 +115,7 @@
|
|||
"@typescript-eslint/eslint-plugin": "^4.27.0",
|
||||
"@typescript-eslint/parser": "^4.27.0",
|
||||
"classnames": "^2.3.1",
|
||||
"color-rgba": "^2.4.0",
|
||||
"dayjs": "^1.10.6",
|
||||
"detect-browser": "^5.2.0",
|
||||
"eslint": "^7.28.0",
|
||||
|
|
@ -133,7 +133,7 @@
|
|||
"markdown-it-sup": "^1.0.0",
|
||||
"mediasoup-client": "npm:@insertish/mediasoup-client@3.6.36-esnext",
|
||||
"mobx": "^6.3.2",
|
||||
"mobx-react-lite": "^3.2.0",
|
||||
"mobx-react-lite": "^3.3.0",
|
||||
"preact": "^10.5.14",
|
||||
"preact-context-menu": "^0.2.1",
|
||||
"preact-i18n": "^2.4.0-preactx",
|
||||
|
|
@ -147,7 +147,7 @@
|
|||
"react-scroll": "^1.8.2",
|
||||
"react-virtualized-auto-sizer": "^1.0.5",
|
||||
"react-virtuoso": "^1.10.4",
|
||||
"revolt.js": "6.0.0-rc.13",
|
||||
"revolt.js": "6.0.0-rc.15",
|
||||
"rimraf": "^3.0.2",
|
||||
"sass": "^1.35.1",
|
||||
"shade-blend-color": "^1.0.0",
|
||||
|
|
@ -163,5 +163,6 @@
|
|||
"main": "index.js",
|
||||
"repository": "https://github.com/revoltchat/revite.git",
|
||||
"author": "Paul <paulmakles@gmail.com>",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"packageManager": "yarn@3.2.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { API } from "revolt.js";
|
||||
import { Nullable } from "revolt.js/dist/util/null";
|
||||
import { Nullable } from "revolt.js/esm/util/null";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
|
||||
export interface IconBaseProps<T> {
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
import { Check, Square, X } from "@styled-icons/boxicons-regular";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
type State = "Allow" | "Neutral" | "Deny";
|
||||
|
||||
const SwitchContainer = styled.div.attrs({
|
||||
role: "radiogroup",
|
||||
"aria-orientiation": "horizontal",
|
||||
})`
|
||||
flex-shrink: 0;
|
||||
|
||||
display: flex;
|
||||
margin: 4px 16px;
|
||||
overflow: hidden;
|
||||
border-radius: var(--border-radius);
|
||||
background: var(--secondary-background);
|
||||
border: 2px solid var(--tertiary-background);
|
||||
`;
|
||||
|
||||
const Switch = styled.div.attrs({
|
||||
role: "radio",
|
||||
})<{ state: State; selected: boolean }>`
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease all;
|
||||
|
||||
color: ${(props) =>
|
||||
props.state === "Allow"
|
||||
? "var(--success)"
|
||||
: props.state === "Deny"
|
||||
? "var(--error)"
|
||||
: "var(--tertiary-foreground)"};
|
||||
|
||||
${(props) =>
|
||||
props.selected &&
|
||||
css`
|
||||
color: white;
|
||||
|
||||
background: ${props.state === "Allow"
|
||||
? "var(--success)"
|
||||
: props.state === "Deny"
|
||||
? "var(--error)"
|
||||
: "var(--primary-background)"};
|
||||
`}
|
||||
|
||||
&:hover {
|
||||
filter: brightness(0.8);
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
state: State;
|
||||
onChange: (state: State) => void;
|
||||
}
|
||||
|
||||
export function OverrideSwitch({ state, onChange }: Props) {
|
||||
return (
|
||||
<SwitchContainer>
|
||||
<Switch
|
||||
onClick={() => onChange("Deny")}
|
||||
state="Deny"
|
||||
selected={state === "Deny"}>
|
||||
<X size={24} />
|
||||
</Switch>
|
||||
<Switch
|
||||
onClick={() => onChange("Neutral")}
|
||||
state="Neutral"
|
||||
selected={state === "Neutral"}>
|
||||
<Square size={24} />
|
||||
</Switch>
|
||||
<Switch
|
||||
onClick={() => onChange("Allow")}
|
||||
state="Allow"
|
||||
selected={state === "Allow"}>
|
||||
<Check size={24} />
|
||||
</Switch>
|
||||
</SwitchContainer>
|
||||
);
|
||||
}
|
||||
|
|
@ -16,7 +16,16 @@ export function PermissionList({ value, onChange, filter }: Props) {
|
|||
{(Object.keys(Permission) as (keyof typeof Permission)[])
|
||||
.filter(
|
||||
(key) =>
|
||||
key !== "GrantAllSafe" &&
|
||||
![
|
||||
"GrantAllSafe",
|
||||
"TimeoutMembers",
|
||||
"ReadMessageHistory",
|
||||
"Speak",
|
||||
"Video",
|
||||
"MuteMembers",
|
||||
"DeafenMembers",
|
||||
"MoveMembers",
|
||||
].includes(key) &&
|
||||
(!filter || filter.includes(key)),
|
||||
)
|
||||
.map((x) => (
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ import { Text } from "preact-i18n";
|
|||
import { useMemo } from "preact/hooks";
|
||||
|
||||
import Checkbox from "../../ui/Checkbox";
|
||||
|
||||
import { OverrideSwitch } from "./OverrideSwitch";
|
||||
import { OverrideSwitch } from "@revoltchat/ui";
|
||||
|
||||
interface PermissionSelectProps {
|
||||
id: keyof typeof Permission;
|
||||
|
|
@ -20,6 +19,7 @@ interface PermissionSelectProps {
|
|||
type State = "Allow" | "Neutral" | "Deny";
|
||||
|
||||
const PermissionEntry = styled.label`
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
margin: 8px 0;
|
||||
display: flex;
|
||||
|
|
@ -100,9 +100,9 @@ export function PermissionSelect({
|
|||
return (
|
||||
<PermissionEntry>
|
||||
<span class="title">
|
||||
<Text id={`permissions.server.${id}.t`}>{id}</Text>
|
||||
<Text id={`permissions.${id}.t`}>{id}</Text>
|
||||
<span class="description">
|
||||
<Text id={`permissions.server.${id}.d`} />
|
||||
<Text id={`permissions.${id}.d`} />
|
||||
</span>
|
||||
</span>
|
||||
{typeof value === "object" ? (
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
import { API } from "revolt.js";
|
||||
|
||||
export type RoleOrDefault = (
|
||||
| API.Role
|
||||
| {
|
||||
name: string;
|
||||
permissions: number;
|
||||
colour?: string;
|
||||
hoist?: boolean;
|
||||
rank?: number;
|
||||
}
|
||||
) & { id: string };
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
import { API } from "revolt.js";
|
||||
|
||||
import Checkbox from "../../ui/Checkbox";
|
||||
|
||||
export type RoleOrDefault = (
|
||||
| API.Role
|
||||
| {
|
||||
name: string;
|
||||
permissions: number;
|
||||
colour?: string;
|
||||
hoist?: boolean;
|
||||
rank?: number;
|
||||
}
|
||||
) & { id: string };
|
||||
|
||||
interface Props {
|
||||
selected: string;
|
||||
onSelect: (id: string) => void;
|
||||
|
||||
roles: RoleOrDefault[];
|
||||
}
|
||||
|
||||
export function RoleSelection({ selected, onSelect, roles }: Props) {
|
||||
return (
|
||||
<>
|
||||
{roles.map((x) => (
|
||||
<Checkbox
|
||||
checked={x.id === selected}
|
||||
onChange={() => onSelect(x.id)}>
|
||||
{x.name}
|
||||
</Checkbox>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import Tip from "../../../components/ui/Tip";
|
||||
import Button from "../../ui/Button";
|
||||
|
||||
interface Props {
|
||||
save: () => void;
|
||||
}
|
||||
|
||||
export function UnsavedChanges({ save }: Props) {
|
||||
return (
|
||||
<Tip hideSeparator>
|
||||
<span
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
}}>
|
||||
You have unsaved changes!
|
||||
<Button onClick={save}>Save</Button>
|
||||
</span>
|
||||
</Tip>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* This file monitors changes to settings and syncs them to the server.
|
||||
*/
|
||||
import { ClientboundNotification } from "revolt.js/dist/websocket/notifications";
|
||||
import { ClientboundNotification } from "revolt.js/esm/websocket/notifications";
|
||||
|
||||
import { useEffect } from "preact/hooks";
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
import { Cog, UserVoice } from "@styled-icons/boxicons-solid";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { Channel, Message, Server, User, API } from "revolt.js";
|
||||
import { Permission, UserPermission } from "revolt.js/dist/api/permissions";
|
||||
import { Permission, UserPermission } from "revolt.js/esm/api/permissions";
|
||||
|
||||
import {
|
||||
ContextMenuWithData,
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import {
|
|||
LeftArrowAlt,
|
||||
} from "@styled-icons/boxicons-regular";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Server } from "revolt.js/dist/maps/Servers";
|
||||
import { Channel } from "revolt.js/esm/maps/Channels";
|
||||
import { Server } from "revolt.js/esm/maps/Servers";
|
||||
|
||||
import { ContextMenuWithData, MenuItem } from "preact-context-menu";
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { action, makeAutoObservable } from "mobx";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Message } from "revolt.js/dist/maps/Messages";
|
||||
import { Nullable } from "revolt.js/dist/util/null";
|
||||
import { Channel } from "revolt.js/esm/maps/Channels";
|
||||
import { Message } from "revolt.js/esm/maps/Messages";
|
||||
import { Nullable } from "revolt.js/esm/util/null";
|
||||
|
||||
import { SimpleRenderer } from "./simple/SimpleRenderer";
|
||||
import { RendererRoutines, ScrollState } from "./types";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Message } from "revolt.js/dist/maps/Messages";
|
||||
import { Message } from "revolt.js/esm/maps/Messages";
|
||||
|
||||
import { ChannelRenderer } from "./Singleton";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { action, makeAutoObservable, runInAction } from "mobx";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Nullable, toNullable } from "revolt.js/dist/util/null";
|
||||
import { Channel } from "revolt.js/esm/maps/Channels";
|
||||
import { Nullable, toNullable } from "revolt.js/esm/util/null";
|
||||
|
||||
import type { ProduceType, VoiceUser } from "./Types";
|
||||
import type VoiceClient from "./VoiceClient";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { action, computed, makeAutoObservable, ObservableMap } from "mobx";
|
||||
import { API } from "revolt.js";
|
||||
import { Nullable } from "revolt.js/dist/util/null";
|
||||
import { Nullable } from "revolt.js/esm/util/null";
|
||||
|
||||
import { mapToRecord } from "../../lib/conversion";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { action, computed, makeAutoObservable, ObservableMap } from "mobx";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Message } from "revolt.js/dist/maps/Messages";
|
||||
import { Server } from "revolt.js/dist/maps/Servers";
|
||||
import { Channel } from "revolt.js/esm/maps/Channels";
|
||||
import { Message } from "revolt.js/esm/maps/Messages";
|
||||
import { Server } from "revolt.js/esm/maps/Servers";
|
||||
|
||||
import { mapToRecord } from "../../lib/conversion";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { action, computed, makeAutoObservable } from "mobx";
|
||||
import { API } from "revolt.js";
|
||||
import { Client } from "revolt.js";
|
||||
import { Nullable } from "revolt.js/dist/util/null";
|
||||
import { Nullable } from "revolt.js/esm/util/null";
|
||||
|
||||
import { isDebug } from "../../revision";
|
||||
import Persistent from "../interfaces/Persistent";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Ghost } from "@styled-icons/boxicons-solid";
|
|||
import { reaction } from "mobx";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Redirect, useParams } from "react-router-dom";
|
||||
import { Channel as ChannelI } from "revolt.js/dist/maps/Channels";
|
||||
import { Channel as ChannelI } from "revolt.js/esm/maps/Channels";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import {
|
|||
} from "@styled-icons/boxicons-regular";
|
||||
import { Notepad, Group } from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { Channel } from "revolt.js/esm/maps/Channels";
|
||||
import { User } from "revolt.js/esm/maps/Users";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
|
||||
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Channel } from "revolt.js/esm/maps/Channels";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { runInAction } from "mobx";
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { useHistory, useParams } from "react-router-dom";
|
||||
import { animateScroll } from "react-scroll";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Channel } from "revolt.js/esm/maps/Channels";
|
||||
import styled from "styled-components/macro";
|
||||
import useResizeObserver from "use-resize-observer";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Message } from "revolt.js/dist/maps/Messages";
|
||||
import { Message } from "revolt.js/esm/maps/Messages";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { useContext, useEffect, useState } from "preact/hooks";
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import { X } from "@styled-icons/boxicons-regular";
|
|||
import isEqual from "lodash.isequal";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { API } from "revolt.js";
|
||||
import { Message as MessageI } from "revolt.js/dist/maps/Messages";
|
||||
import { Nullable } from "revolt.js/dist/util/null";
|
||||
import { Message as MessageI } from "revolt.js/esm/maps/Messages";
|
||||
import { Nullable } from "revolt.js/esm/util/null";
|
||||
import styled from "styled-components/macro";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { X, Plus } from "@styled-icons/boxicons-regular";
|
|||
import { PhoneCall, Envelope, UserX } from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { User } from "revolt.js/esm/maps/Users";
|
||||
|
||||
import styles from "./Friend.module.scss";
|
||||
import classNames from "classnames";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { ChevronRight } from "@styled-icons/boxicons-regular";
|
||||
import { UserDetail, MessageAdd, UserPlus } from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { User } from "revolt.js/esm/maps/Users";
|
||||
|
||||
import styles from "./Friend.module.scss";
|
||||
import classNames from "classnames";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Channel } from "revolt.js/esm/maps/Channels";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
import isEqual from "lodash.isequal";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Channel, API } from "revolt.js";
|
||||
import { DEFAULT_PERMISSION_DIRECT_MESSAGE } from "revolt.js/esm/api/permissions";
|
||||
|
||||
import { useLayoutEffect, useState } from "preact/hooks";
|
||||
import { useState } from "preact/hooks";
|
||||
|
||||
import { PermissionsLayout, Button, SpaceBetween, H1 } from "@revoltchat/ui";
|
||||
|
||||
import { PermissionList } from "../../../components/settings/roles/PermissionList";
|
||||
import {
|
||||
RoleOrDefault,
|
||||
RoleSelection,
|
||||
} from "../../../components/settings/roles/RoleSelection";
|
||||
import { UnsavedChanges } from "../../../components/settings/roles/UnsavedChanges";
|
||||
import { RoleOrDefault } from "../../../components/settings/roles/RoleSelection";
|
||||
import { useRoles } from "../server/Roles";
|
||||
|
||||
interface Props {
|
||||
|
|
@ -24,10 +23,12 @@ export default observer(({ channel }: Props) => {
|
|||
{
|
||||
id: "default",
|
||||
name: "Default",
|
||||
permissions: channel.permissions,
|
||||
permissions:
|
||||
channel.permissions ??
|
||||
DEFAULT_PERMISSION_DIRECT_MESSAGE,
|
||||
},
|
||||
] as RoleOrDefault[])
|
||||
: (useRoles(channel.server!).map((role) => {
|
||||
: (useRoles(channel.server! as any).map((role) => {
|
||||
return {
|
||||
...role,
|
||||
permissions: (role.id === "default"
|
||||
|
|
@ -39,154 +40,72 @@ export default observer(({ channel }: Props) => {
|
|||
};
|
||||
}) as RoleOrDefault[]);
|
||||
|
||||
// Keep track of whatever role we're editing right now.
|
||||
const [selected, setSelected] = useState("default");
|
||||
const [value, setValue] = useState<API.OverrideField | number | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const currentPermission = currentRoles.find(
|
||||
(x) => x.id === selected,
|
||||
)!.permissions;
|
||||
const currentValue = value ?? currentPermission;
|
||||
|
||||
// If a role gets deleted, unselect it immediately.
|
||||
useLayoutEffect(() => {
|
||||
if (!channel?.server?.roles) return;
|
||||
if (!channel.server.roles[selected]) {
|
||||
setSelected("default");
|
||||
}
|
||||
}, [channel.server?.roles]);
|
||||
|
||||
// Upload new role information to server.
|
||||
function save() {
|
||||
channel.setPermissions(
|
||||
selected,
|
||||
typeof currentValue === "number"
|
||||
? currentValue
|
||||
: ({
|
||||
allow: currentValue.a,
|
||||
deny: currentValue.d,
|
||||
} as any),
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ height: "100%", overflowY: "scroll" }}>
|
||||
<h1>Select Role</h1>
|
||||
<RoleSelection
|
||||
selected={selected}
|
||||
onSelect={(id) => {
|
||||
setValue(undefined);
|
||||
setSelected(id);
|
||||
}}
|
||||
roles={currentRoles}
|
||||
/>
|
||||
{!isEqual(currentPermission, currentValue) && (
|
||||
<>
|
||||
<hr />
|
||||
<UnsavedChanges save={save} />
|
||||
</>
|
||||
)}
|
||||
<hr />
|
||||
<h1>Edit Permissions</h1>
|
||||
<PermissionList
|
||||
value={currentValue}
|
||||
onChange={setValue}
|
||||
filter={[
|
||||
"ViewChannel",
|
||||
"ReadMessageHistory",
|
||||
"SendMessage",
|
||||
"ManageMessages",
|
||||
"ManageWebhooks",
|
||||
"InviteOthers",
|
||||
"SendEmbeds",
|
||||
"UploadFiles",
|
||||
"Masquerade",
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
<PermissionsLayout
|
||||
channel={channel}
|
||||
editor={({ selected }) => {
|
||||
const currentRole = currentRoles.find(
|
||||
(x) => x.id === selected,
|
||||
)!;
|
||||
|
||||
/*const [selected, setSelected] = useState("default");
|
||||
if (!currentRole) return null;
|
||||
|
||||
type R = { name: string; permissions: number };
|
||||
const roles: { [key: string]: R } = {};
|
||||
if (channel.channel_type !== "Group") {
|
||||
const server = channel.server;
|
||||
const a = server?.roles ?? {};
|
||||
for (const b of Object.keys(a)) {
|
||||
roles[b] = {
|
||||
name: a[b].name,
|
||||
permissions:
|
||||
channel.role_permissions?.[b] ?? a[b].permissions[1],
|
||||
};
|
||||
}
|
||||
}
|
||||
// Keep track of whatever role we're editing right now.
|
||||
const [value, setValue] = useState<
|
||||
API.OverrideField | number | undefined
|
||||
>(undefined);
|
||||
const currentPermission = currentRoles.find(
|
||||
(x) => x.id === selected,
|
||||
)!.permissions;
|
||||
const currentValue = value ?? currentPermission;
|
||||
|
||||
const keys = ["default", ...Object.keys(roles)];
|
||||
|
||||
const defaultRole = {
|
||||
name: "Default",
|
||||
permissions:
|
||||
(channel.channel_type === "Group"
|
||||
? channel.permissions
|
||||
: channel.default_permissions) ?? DEFAULT_PERMISSION_DM,
|
||||
};
|
||||
const selectedRole = selected === "default" ? defaultRole : roles[selected];
|
||||
|
||||
if (!selectedRole) {
|
||||
useEffect(() => setSelected("default"), []);
|
||||
return null;
|
||||
}
|
||||
|
||||
const [p, setPerm] = useState(selectedRole.permissions >>> 0);
|
||||
|
||||
useEffect(() => {
|
||||
setPerm(selectedRole.permissions >>> 0);
|
||||
}, [selected, selectedRole.permissions]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tip warning>This section is under construction.</Tip>
|
||||
<h2>select role</h2>
|
||||
{selected}
|
||||
{keys.map((id) => {
|
||||
const role: R = id === "default" ? defaultRole : roles[id];
|
||||
|
||||
return (
|
||||
<Checkbox
|
||||
key={id}
|
||||
checked={selected === id}
|
||||
onChange={(selected) => selected && setSelected(id)}>
|
||||
{role.name}
|
||||
</Checkbox>
|
||||
);
|
||||
})}
|
||||
<h2>channel permissions</h2>
|
||||
{Object.keys(ChannelPermission).map((perm) => {
|
||||
if (perm === "View") return null;
|
||||
|
||||
const value =
|
||||
ChannelPermission[perm as keyof typeof ChannelPermission];
|
||||
if (value & DEFAULT_PERMISSION_DM) {
|
||||
return (
|
||||
<Checkbox
|
||||
checked={(p & value) > 0}
|
||||
onChange={(c) =>
|
||||
setPerm(c ? p | value : p ^ value)
|
||||
}>
|
||||
{perm}
|
||||
</Checkbox>
|
||||
// Upload new role information to server.
|
||||
function save() {
|
||||
channel.setPermissions(
|
||||
selected,
|
||||
typeof currentValue === "number"
|
||||
? currentValue
|
||||
: ({
|
||||
allow: currentValue.a,
|
||||
deny: currentValue.d,
|
||||
} as any),
|
||||
);
|
||||
}
|
||||
})}
|
||||
<Button
|
||||
contrast
|
||||
onClick={() => {
|
||||
channel.setPermissions(selected, p);
|
||||
}}>
|
||||
click here to save permissions for role
|
||||
</Button>
|
||||
</div>
|
||||
);*/
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SpaceBetween>
|
||||
<H1>Permissions for {currentRole.name}</H1>
|
||||
<Button
|
||||
palette="secondary"
|
||||
disabled={isEqual(
|
||||
currentPermission,
|
||||
currentValue,
|
||||
)}
|
||||
onClick={save}>
|
||||
Save
|
||||
</Button>
|
||||
</SpaceBetween>
|
||||
<PermissionList
|
||||
value={currentValue}
|
||||
onChange={setValue}
|
||||
filter={[
|
||||
...(channel.channel_type === "Group"
|
||||
? []
|
||||
: ["ViewChannel" as "ViewChannel"]),
|
||||
"ReadMessageHistory",
|
||||
"SendMessage",
|
||||
"ManageMessages",
|
||||
"ManageWebhooks",
|
||||
"InviteOthers",
|
||||
"SendEmbeds",
|
||||
"UploadFiles",
|
||||
"Masquerade",
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { LockAlt, HelpCircle } from "@styled-icons/boxicons-solid";
|
|||
import type { AxiosError } from "axios";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { API } from "revolt.js";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { User } from "revolt.js/esm/maps/Users";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import styles from "./Panes.module.scss";
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { XCircle } from "@styled-icons/boxicons-regular";
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { Virtuoso } from "react-virtuoso";
|
||||
import { API } from "revolt.js";
|
||||
import { Server } from "revolt.js/dist/maps/Servers";
|
||||
import { Server } from "revolt.js/esm/maps/Servers";
|
||||
|
||||
import styles from "./Panes.module.scss";
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { ChevronDown } from "@styled-icons/boxicons-regular";
|
|||
import { isEqual } from "lodash";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Virtuoso } from "react-virtuoso";
|
||||
import { Member } from "revolt.js/dist/maps/Members";
|
||||
import { Server } from "revolt.js/dist/maps/Servers";
|
||||
import { Member } from "revolt.js/esm/maps/Members";
|
||||
import { Server } from "revolt.js/esm/maps/Servers";
|
||||
|
||||
import styles from "./Panes.module.scss";
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Markdown } from "@styled-icons/boxicons-logos";
|
||||
import isEqual from "lodash.isequal";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Server } from "revolt.js/dist/maps/Servers";
|
||||
import { Server } from "revolt.js/esm/maps/Servers";
|
||||
|
||||
import styles from "./Panes.module.scss";
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
|
|||
|
|
@ -116,48 +116,3 @@
|
|||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.roles {
|
||||
gap: 12px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
||||
.list {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.permissions {
|
||||
flex-grow: 1;
|
||||
padding: 0 8px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,29 @@
|
|||
import isEqual from "lodash.isequal";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Server } from "revolt.js/dist/maps/Servers";
|
||||
import { Server } from "revolt.js/esm/maps/Servers";
|
||||
|
||||
import { useLayoutEffect, useMemo, useState } from "preact/hooks";
|
||||
import { useMemo, useState } from "preact/hooks";
|
||||
|
||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||
|
||||
import Button from "../../../components/ui/Button";
|
||||
import Checkbox from "../../../components/ui/Checkbox";
|
||||
import ColourSwatches from "../../../components/ui/ColourSwatches";
|
||||
import InputBox from "../../../components/ui/InputBox";
|
||||
import Overline from "../../../components/ui/Overline";
|
||||
import { Button, PermissionsLayout, SpaceBetween, H1 } from "@revoltchat/ui";
|
||||
|
||||
import { PermissionList } from "../../../components/settings/roles/PermissionList";
|
||||
import {
|
||||
RoleOrDefault,
|
||||
RoleSelection,
|
||||
} from "../../../components/settings/roles/RoleSelection";
|
||||
import { UnsavedChanges } from "../../../components/settings/roles/UnsavedChanges";
|
||||
import { RoleOrDefault } from "../../../components/settings/roles/RoleSelection";
|
||||
|
||||
interface Props {
|
||||
server: Server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to memo-ize role information.
|
||||
* @param server Target server
|
||||
* @returns Role array
|
||||
*/
|
||||
export function useRoles(server: Server) {
|
||||
return useMemo(
|
||||
() =>
|
||||
|
|
@ -38,151 +41,183 @@ export function useRoles(server: Server) {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Roles settings menu
|
||||
*/
|
||||
export const Roles = observer(({ server }: Props) => {
|
||||
// Consolidate all permissions that we can change right now.
|
||||
const currentRoles = useRoles(server);
|
||||
|
||||
// Keep track of whatever role we're editing right now.
|
||||
const [selected, setSelected] = useState("default");
|
||||
const [value, setValue] = useState<Partial<RoleOrDefault>>({});
|
||||
const currentRole = currentRoles.find((x) => x.id === selected)!;
|
||||
const currentRoleValue = { ...currentRole, ...value };
|
||||
|
||||
// If a role gets deleted, unselect it immediately.
|
||||
useLayoutEffect(() => {
|
||||
if (!server.roles) return;
|
||||
if (!server.roles[selected]) {
|
||||
setSelected("default");
|
||||
}
|
||||
}, [server.roles]);
|
||||
|
||||
// Calculate permissions we have access to on this server.
|
||||
const current = server.permission;
|
||||
|
||||
// Upload new role information to server.
|
||||
function save() {
|
||||
const { permissions: permsCurrent, ...current } = currentRole;
|
||||
const { permissions: permsValue, ...value } = currentRoleValue;
|
||||
|
||||
if (!isEqual(permsCurrent, permsValue)) {
|
||||
server.setPermissions(
|
||||
selected,
|
||||
typeof permsValue === "number"
|
||||
? permsValue
|
||||
: {
|
||||
allow: permsValue.a,
|
||||
deny: permsValue.d,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (!isEqual(current, value)) {
|
||||
server.editRole(selected, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the role from this server.
|
||||
function deleteRole() {
|
||||
setSelected("default");
|
||||
server.deleteRole(selected);
|
||||
}
|
||||
// Pull in modal context.
|
||||
const { openScreen } = useIntermediate();
|
||||
|
||||
return (
|
||||
<div style={{ height: "100%", overflowY: "scroll" }}>
|
||||
<h1>Select Role</h1>
|
||||
<RoleSelection
|
||||
selected={selected}
|
||||
onSelect={(id) => {
|
||||
setValue({});
|
||||
setSelected(id);
|
||||
}}
|
||||
roles={currentRoles}
|
||||
/>
|
||||
{selected !== "default" && (
|
||||
<>
|
||||
<hr />
|
||||
<h1>Edit Role</h1>
|
||||
<section>
|
||||
<Overline type="subtle">Role Name</Overline>
|
||||
<p>
|
||||
<InputBox
|
||||
value={currentRoleValue.name}
|
||||
onChange={(e) =>
|
||||
setValue({
|
||||
...value,
|
||||
name: e.currentTarget.value,
|
||||
})
|
||||
}
|
||||
contrast
|
||||
/>
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<Overline type="subtle">Role Colour</Overline>
|
||||
<p>
|
||||
<ColourSwatches
|
||||
value={currentRoleValue.colour ?? "gray"}
|
||||
onChange={(colour) =>
|
||||
setValue({ ...value, colour })
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<Overline type="subtle">Role Options</Overline>
|
||||
<p>
|
||||
<Checkbox
|
||||
checked={currentRoleValue.hoist ?? false}
|
||||
onChange={(hoist) =>
|
||||
setValue({ ...value, hoist })
|
||||
}
|
||||
description="Display this role above others.">
|
||||
Hoist Role
|
||||
</Checkbox>
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<Overline type="subtle">
|
||||
Experimental Role Ranking
|
||||
</Overline>
|
||||
<p>
|
||||
<InputBox
|
||||
value={currentRoleValue.rank ?? 0}
|
||||
onChange={(e) =>
|
||||
setValue({
|
||||
...value,
|
||||
rank: parseInt(e.currentTarget.value),
|
||||
})
|
||||
}
|
||||
contrast
|
||||
/>
|
||||
</p>
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
{!isEqual(currentRole, currentRoleValue) && (
|
||||
<>
|
||||
<hr />
|
||||
<UnsavedChanges save={save} />
|
||||
</>
|
||||
)}
|
||||
<hr />
|
||||
<h1>Edit Permissions</h1>
|
||||
<PermissionList
|
||||
value={currentRoleValue.permissions}
|
||||
onChange={(permissions) =>
|
||||
setValue({ ...value, permissions } as RoleOrDefault)
|
||||
<PermissionsLayout
|
||||
server={server}
|
||||
onCreateRole={(callback) =>
|
||||
openScreen({
|
||||
id: "special_input",
|
||||
type: "create_role",
|
||||
server: server as any,
|
||||
callback,
|
||||
})
|
||||
}
|
||||
editor={({ selected }) => {
|
||||
const currentRole = currentRoles.find(
|
||||
(x) => x.id === selected,
|
||||
)!;
|
||||
|
||||
if (!currentRole) return null;
|
||||
|
||||
// Keep track of whatever role we're editing right now.
|
||||
const [value, setValue] = useState<Partial<RoleOrDefault>>({});
|
||||
|
||||
const currentRoleValue = { ...currentRole, ...value };
|
||||
|
||||
// Calculate permissions we have access to on this server.
|
||||
const current = server.permission;
|
||||
|
||||
// Upload new role information to server.
|
||||
function save() {
|
||||
const { permissions: permsCurrent, ...current } =
|
||||
currentRole;
|
||||
const { permissions: permsValue, ...value } =
|
||||
currentRoleValue;
|
||||
|
||||
if (!isEqual(permsCurrent, permsValue)) {
|
||||
server.setPermissions(
|
||||
selected,
|
||||
typeof permsValue === "number"
|
||||
? permsValue
|
||||
: {
|
||||
allow: permsValue.a,
|
||||
deny: permsValue.d,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (!isEqual(current, value)) {
|
||||
server.editRole(selected, value);
|
||||
}
|
||||
}
|
||||
/>
|
||||
{selected !== "default" && (
|
||||
<>
|
||||
<hr />
|
||||
<h1>Danger Zone</h1>
|
||||
<Button contrast error onClick={deleteRole}>
|
||||
Delete Role
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
// Delete the role from this server.
|
||||
function deleteRole() {
|
||||
server.deleteRole(selected);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SpaceBetween>
|
||||
<H1>Edit {currentRole.name}</H1>
|
||||
<Button
|
||||
palette="secondary"
|
||||
disabled={isEqual(
|
||||
currentRole,
|
||||
currentRoleValue,
|
||||
)}
|
||||
onClick={save}>
|
||||
Save
|
||||
</Button>
|
||||
</SpaceBetween>
|
||||
<hr />
|
||||
{selected !== "default" && (
|
||||
<>
|
||||
<section>
|
||||
<Overline type="subtle">Role Name</Overline>
|
||||
<p>
|
||||
<InputBox
|
||||
value={currentRoleValue.name}
|
||||
onChange={(e) =>
|
||||
setValue({
|
||||
...value,
|
||||
name: e.currentTarget.value,
|
||||
})
|
||||
}
|
||||
contrast
|
||||
/>
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<Overline type="subtle">
|
||||
Role Colour
|
||||
</Overline>
|
||||
<p>
|
||||
<ColourSwatches
|
||||
value={
|
||||
currentRoleValue.colour ??
|
||||
"gray"
|
||||
}
|
||||
onChange={(colour) =>
|
||||
setValue({ ...value, colour })
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<Overline type="subtle">
|
||||
Role Options
|
||||
</Overline>
|
||||
<p>
|
||||
<Checkbox
|
||||
checked={
|
||||
currentRoleValue.hoist ?? false
|
||||
}
|
||||
onChange={(hoist) =>
|
||||
setValue({ ...value, hoist })
|
||||
}
|
||||
description="Display this role above others.">
|
||||
Hoist Role
|
||||
</Checkbox>
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<Overline type="subtle">
|
||||
Role Ranking
|
||||
</Overline>
|
||||
<p>
|
||||
<InputBox
|
||||
type="number"
|
||||
value={currentRoleValue.rank ?? 0}
|
||||
onChange={(e) =>
|
||||
setValue({
|
||||
...value,
|
||||
rank: parseInt(
|
||||
e.currentTarget.value,
|
||||
),
|
||||
})
|
||||
}
|
||||
contrast
|
||||
/>
|
||||
</p>
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
<h1>Edit Permissions</h1>
|
||||
<PermissionList
|
||||
value={currentRoleValue.permissions}
|
||||
onChange={(permissions) =>
|
||||
setValue({
|
||||
...value,
|
||||
permissions,
|
||||
} as RoleOrDefault)
|
||||
}
|
||||
/>
|
||||
{selected !== "default" && (
|
||||
<>
|
||||
<hr />
|
||||
<h1>Danger Zone</h1>
|
||||
<Button
|
||||
palette="error"
|
||||
compact
|
||||
onClick={deleteRole}>
|
||||
Delete Role
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -120,6 +120,9 @@ export default defineConfig({
|
|||
},
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: ["revolt.js"],
|
||||
exclude: ["revolt.js", "@revoltchat/ui"],
|
||||
},
|
||||
resolve: {
|
||||
preserveSymlinks: true,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue