forked from abner/for-legacy-web
Merge branch 'master' into production
commit
4541a34cef
|
|
@ -5,3 +5,6 @@ dist_injected
|
|||
node_modules
|
||||
.env
|
||||
.env.local
|
||||
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
ko_fi: insertish
|
||||
custom: https://insrt.uk/donate
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
name: Bug report
|
||||
description: File a bug report
|
||||
title: "[Bug Report]"
|
||||
title: "bug: "
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
|
|
@ -52,7 +52,7 @@ body:
|
|||
id: desktop
|
||||
attributes:
|
||||
label: Desktop
|
||||
description: Is this bug specific to [the desktop client](https://gihtub.com/revoltchat/desktop)? (If not, leave this unchecked.)
|
||||
description: Is this bug specific to [the desktop client](https://github.com/revoltchat/desktop)? (If not, leave this unchecked.)
|
||||
options:
|
||||
- label: Yes, this bug is specific to Revolt Desktop and is *not* an issue with Revolt Desktop itself.
|
||||
required: false
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
name: Feature request
|
||||
description: Make a feature request
|
||||
title: "[Feature Request]"
|
||||
title: "feature request: "
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
# Security
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you would like to report a security vulnerability,
|
||||
please email **[security@revolt.chat](mailto:security@revolt.chat)**,
|
||||
this will open a new ticket in ticket system, you should receive a response
|
||||
within the next couple of days, potentially within a few minutes if someone
|
||||
is currently active.
|
||||
|
||||
To help us best triage the issue, please provide:
|
||||
|
||||
- The type of issue at hand
|
||||
- The name of the relevant project affected
|
||||
- Reproduction steps
|
||||
- Reference to any relevant source file(s) that you may suspect are causing the issue
|
||||
- Any extra information about your configuration.
|
||||
- Description of potential ways this can be exploited, if you can list any
|
||||
|
||||
For revoltchat/revite in particular:
|
||||
|
||||
- Please include the commit hash of the client, it is visible in settings under the log out button.
|
||||
|
||||
Thank you for helping Revolt.
|
||||
|
|
@ -7,6 +7,9 @@ dist-ssr
|
|||
*.log
|
||||
/.idea
|
||||
|
||||
.yarn/cache
|
||||
.yarn/install-state.gz
|
||||
|
||||
public/assets
|
||||
public/assets_*
|
||||
!public/assets_default
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"compile-hero.disable-compile-files-on-did-save-code": true
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
|
|
|
|||
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
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,11 @@
|
|||
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"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||
spec: "@yarnpkg/plugin-workspace-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.2.0.cjs
|
||||
15
Dockerfile
15
Dockerfile
|
|
@ -1,18 +1,15 @@
|
|||
FROM node:16-buster AS builder
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
COPY package*.json ./
|
||||
|
||||
RUN yarn --no-cache
|
||||
|
||||
COPY . .
|
||||
COPY .env.build .env
|
||||
RUN yarn add --dev @babel/plugin-proposal-decorators
|
||||
RUN yarn typecheck
|
||||
RUN yarn build
|
||||
RUN npm prune --production
|
||||
|
||||
FROM node:16-buster
|
||||
RUN yarn install --frozen-lockfile
|
||||
RUN yarn typecheck
|
||||
RUN yarn build:highmem
|
||||
RUN yarn workspaces focus --production --all
|
||||
|
||||
FROM node:16-alpine
|
||||
WORKDIR /usr/src/app
|
||||
COPY --from=builder /usr/src/app .
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 6a72c5c952eedfbeb8a193a8a4b97927cc44cd6f
|
||||
Subproject commit bac88cffd196a2afacf7d726e4f7ef19bd6bd94c
|
||||
31
package.json
31
package.json
|
|
@ -4,6 +4,7 @@
|
|||
"dev": "node scripts/setup_assets.js --check && vite",
|
||||
"pull": "node scripts/setup_assets.js",
|
||||
"build": "rimraf build && node scripts/setup_assets.js --check && vite build",
|
||||
"build:highmem": "NODE_OPTIONS='--max-old-space-size=4096' yarn build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint src/**/*.{js,jsx,ts,tsx}",
|
||||
"fmt": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'",
|
||||
|
|
@ -61,7 +62,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",
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
"vite": "^2.6.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-decorators": "^7.16.7",
|
||||
"@babel/plugin-proposal-decorators": "^7.17.9",
|
||||
"@fontsource/atkinson-hyperlegible": "^4.4.5",
|
||||
"@fontsource/comic-neue": "^4.4.5",
|
||||
"@fontsource/fira-code": "^4.4.5",
|
||||
|
|
@ -81,7 +81,7 @@
|
|||
"@fontsource/noto-sans": "^4.4.5",
|
||||
"@fontsource/open-sans": "^4.5.2",
|
||||
"@fontsource/opendyslexic": "^4.5.2",
|
||||
"@fontsource/poppins": "^4.4.5",
|
||||
"@fontsource/poppins": "^4.4.5",
|
||||
"@fontsource/raleway": "^4.4.5",
|
||||
"@fontsource/roboto": "^4.4.5",
|
||||
"@fontsource/roboto-mono": "^4.4.5",
|
||||
|
|
@ -91,16 +91,16 @@
|
|||
"@fontsource/ubuntu-mono": "^4.4.5",
|
||||
"@hcaptcha/react-hcaptcha": "^0.3.6",
|
||||
"@preact/preset-vite": "^2.0.0",
|
||||
"@revoltchat/ui": "1.0.31",
|
||||
"@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",
|
||||
|
|
@ -111,13 +111,13 @@
|
|||
"@types/react-helmet": "^6.1.1",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
"@types/react-scroll": "^1.8.2",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||
"@types/styled-components": "^5.1.10",
|
||||
"@types/twemoji": "^12.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^4.27.0",
|
||||
"@typescript-eslint/parser": "^4.27.0",
|
||||
"@vitejs/plugin-legacy": "^1.7.1",
|
||||
"classnames": "^2.3.1",
|
||||
"color-rgba": "^2.4.0",
|
||||
"dayjs": "^1.10.6",
|
||||
"detect-browser": "^5.2.0",
|
||||
"eslint": "^7.28.0",
|
||||
|
|
@ -127,13 +127,12 @@
|
|||
"localforage": "^1.9.0",
|
||||
"lodash.defaultsdeep": "^4.6.1",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"long": "^5.2.0",
|
||||
"markdown-it": "^12.0.6",
|
||||
"markdown-it-emoji": "^2.0.0",
|
||||
"markdown-it-sub": "^1.0.0",
|
||||
"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.4.0-patch.0",
|
||||
"preact-i18n": "^2.4.0-preactx",
|
||||
|
|
@ -145,10 +144,8 @@
|
|||
"react-overlapping-panels": "1.2.2",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scroll": "^1.8.2",
|
||||
"react-virtualized-auto-sizer": "^1.0.5",
|
||||
"react-virtuoso": "^1.10.4",
|
||||
"revolt-api": "^0.5.3-alpha.12",
|
||||
"revolt.js": "^5.2.8",
|
||||
"revolt.js": "6.0.0-2",
|
||||
"rimraf": "^3.0.2",
|
||||
"sass": "^1.35.1",
|
||||
"shade-blend-color": "^1.0.0",
|
||||
|
|
@ -160,10 +157,10 @@
|
|||
"vite-plugin-pwa": "^0.11.13",
|
||||
"workbox-precaching": "^6.1.5"
|
||||
},
|
||||
"packageManager": "yarn@1.22.17",
|
||||
"name": "client",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/revoltchat/revite.git",
|
||||
"author": "Paul <paulmakles@gmail.com>",
|
||||
"license": "MIT"
|
||||
}
|
||||
"license": "MIT",
|
||||
"packageManager": "yarn@3.2.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="64.00001046823172" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-0.000008942940667111543 -0.0000033745862566547657 64.00001046823172 64.00000545874563" height="64.00000545874563" style="-webkit-print-color-adjust: exact;"><g id="shape-d9b11490-3403-11ec-bc16-7b519797d558"><rect rx="0" ry="0" x="0" y="0" transform="matrix(1.0000000000000007,-8.726646259971662e-8,-1.5707963280665485e-7,1.0000000000000124,0.000005026548230091521,0.0000027925264056705146)" width="64" height="64" style="fill: rgb(255, 255, 255); fill-opacity: 1;"/></g></svg>
|
||||
|
After Width: | Height: | Size: 626 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="64.00001046823172" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-0.000008942940667111543 -0.0000033745862566547657 64.00001046823172 64.00000545874563" height="64.00000545874563" style="-webkit-print-color-adjust: exact;"><g id="shape-d9b11490-3403-11ec-bc16-7b519797d558"><rect rx="0" ry="0" x="0" y="0" transform="matrix(1.0000000000000007,-8.726646259971662e-8,-1.5707963280665485e-7,1.0000000000000124,0.000005026548230091521,0.0000027925264056705146)" width="64" height="64" style="fill: rgb(255, 255, 255); fill-opacity: 1;"/></g></svg>
|
||||
|
After Width: | Height: | Size: 626 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="64.00001046823172" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-0.000008942940667111543 -0.0000033745862566547657 64.00001046823172 64.00000545874563" height="64.00000545874563" style="-webkit-print-color-adjust: exact;"><g id="shape-d9b11490-3403-11ec-bc16-7b519797d558"><rect rx="0" ry="0" x="0" y="0" transform="matrix(1.0000000000000007,-8.726646259971662e-8,-1.5707963280665485e-7,1.0000000000000124,0.000005026548230091521,0.0000027925264056705146)" width="64" height="64" style="fill: rgb(255, 255, 255); fill-opacity: 1;"/></g></svg>
|
||||
|
After Width: | Height: | Size: 626 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="64.00001046823172" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-0.000008942940667111543 -0.0000033745862566547657 64.00001046823172 64.00000545874563" height="64.00000545874563" style="-webkit-print-color-adjust: exact;"><g id="shape-d9b11490-3403-11ec-bc16-7b519797d558"><rect rx="0" ry="0" x="0" y="0" transform="matrix(1.0000000000000007,-8.726646259971662e-8,-1.5707963280665485e-7,1.0000000000000124,0.000005026548230091521,0.0000027925264056705146)" width="64" height="64" style="fill: rgb(255, 255, 255); fill-opacity: 1;"/></g></svg>
|
||||
|
After Width: | Height: | Size: 626 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="64.00001046823172" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-0.000008942940667111543 -0.0000033745862566547657 64.00001046823172 64.00000545874563" height="64.00000545874563" style="-webkit-print-color-adjust: exact;"><g id="shape-d9b11490-3403-11ec-bc16-7b519797d558"><rect rx="0" ry="0" x="0" y="0" transform="matrix(1.0000000000000007,-8.726646259971662e-8,-1.5707963280665485e-7,1.0000000000000124,0.000005026548230091521,0.0000027925264056705146)" width="64" height="64" style="fill: rgb(255, 255, 255); fill-opacity: 1;"/></g></svg>
|
||||
|
After Width: | Height: | Size: 626 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="64.00001046823172" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-0.000008942940667111543 -0.0000033745862566547657 64.00001046823172 64.00000545874563" height="64.00000545874563" style="-webkit-print-color-adjust: exact;"><g id="shape-d9b11490-3403-11ec-bc16-7b519797d558"><rect rx="0" ry="0" x="0" y="0" transform="matrix(1.0000000000000007,-8.726646259971662e-8,-1.5707963280665485e-7,1.0000000000000124,0.000005026548230091521,0.0000027925264056705146)" width="64" height="64" style="fill: rgb(255, 255, 255); fill-opacity: 1;"/></g></svg>
|
||||
|
After Width: | Height: | Size: 626 B |
|
|
@ -1,6 +1,6 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Channel } from "revolt.js";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { Channel, User } from "revolt.js";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
|
||||
import { StateUpdater, useState } from "preact/hooks";
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import { Hash, VolumeFull } from "@styled-icons/boxicons-regular";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Channel } from "revolt.js";
|
||||
|
||||
import { useContext } from "preact/hooks";
|
||||
|
||||
import { AppContext } from "../../context/revoltjs/RevoltClient";
|
||||
|
||||
import { ImageIconBase, IconBaseProps } from "./IconBase";
|
||||
import fallback from "./assets/group.png";
|
||||
|
||||
import { ImageIconBase, IconBaseProps } from "./IconBase";
|
||||
|
||||
interface Props extends IconBaseProps<Channel> {
|
||||
isServerChannel?: boolean;
|
||||
}
|
||||
|
|
@ -32,7 +33,7 @@ export default observer(
|
|||
...imgProps
|
||||
} = props;
|
||||
const iconURL = client.generateFileURL(
|
||||
target?.icon ?? attachment,
|
||||
target?.icon ?? attachment ?? undefined,
|
||||
{ max_side: 256 },
|
||||
animate,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Attachment } from "revolt-api/types/Autumn";
|
||||
import { API } from "revolt.js";
|
||||
import { Nullable } from "revolt.js";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
|
||||
import { Ref } from "preact";
|
||||
|
|
@ -6,7 +7,7 @@ import { Ref } from "preact";
|
|||
export interface IconBaseProps<T> {
|
||||
target?: T;
|
||||
url?: string;
|
||||
attachment?: Attachment;
|
||||
attachment?: Nullable<API.File>;
|
||||
|
||||
size: number;
|
||||
hover?: boolean;
|
||||
|
|
|
|||
|
|
@ -2,14 +2,11 @@ import { Check } from "@styled-icons/boxicons-regular";
|
|||
import { Cog } from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Link } from "react-router-dom";
|
||||
import { ServerPermission } from "revolt.js/dist/api/permissions";
|
||||
import { Server } from "revolt.js/dist/maps/Servers";
|
||||
import { Server } from "revolt.js";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
||||
|
||||
import IconButton from "../ui/IconButton";
|
||||
|
||||
import Tooltip from "./Tooltip";
|
||||
|
|
@ -125,7 +122,7 @@ export default observer(({ server }: Props) => {
|
|||
</Tooltip>
|
||||
) : undefined}
|
||||
<div className="title">{server.name}</div>
|
||||
{(server.permission & ServerPermission.ManageServer) > 0 && (
|
||||
{server.havePermission("ManageServer") && (
|
||||
<Link to={`/server/${server._id}/settings`}>
|
||||
<IconButton>
|
||||
<Cog size={20} />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { Server } from "revolt.js/dist/maps/Servers";
|
||||
import { Server } from "revolt.js";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { useContext } from "preact/hooks";
|
||||
|
|
@ -39,7 +39,7 @@ export default observer(
|
|||
const { target, attachment, size, animate, server_name, ...imgProps } =
|
||||
props;
|
||||
const iconURL = client.generateFileURL(
|
||||
target?.icon ?? attachment,
|
||||
target?.icon ?? attachment ?? undefined,
|
||||
{ max_side: 256 },
|
||||
animate,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { Message as MessageObject } from "revolt.js/dist/maps/Messages";
|
||||
import { Message as MessageObject } from "revolt.js";
|
||||
|
||||
import { useTriggerEvents } from "preact-context-menu";
|
||||
import { memo } from "preact/compat";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { Message } from "revolt.js/dist/maps/Messages";
|
||||
import { Message } from "revolt.js";
|
||||
import styled, { css, keyframes } from "styled-components/macro";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,16 @@
|
|||
import { Send, ShieldX, HappyBeaming, Box } from "@styled-icons/boxicons-solid";
|
||||
import { Send, ShieldX } from "@styled-icons/boxicons-solid";
|
||||
import Axios, { CancelTokenSource } from "axios";
|
||||
import Long from "long";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { ChannelPermission } from "revolt.js/dist/api/permissions";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import {
|
||||
Channel,
|
||||
DEFAULT_PERMISSION_DIRECT_MESSAGE,
|
||||
DEFAULT_PERMISSION_VIEW_ONLY,
|
||||
Permission,
|
||||
Server,
|
||||
U32_MAX,
|
||||
UserPermission,
|
||||
} from "revolt.js";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
import { ulid } from "ulid";
|
||||
|
||||
|
|
@ -125,6 +133,11 @@ const FileAction = styled.div`
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
`;
|
||||
|
||||
const ThisCodeWillBeReplacedAnywaysSoIMightAsWellJustDoItThisWay__Padding = styled.div`
|
||||
width: 16px;
|
||||
`;
|
||||
|
||||
// For sed replacement
|
||||
|
|
@ -150,7 +163,7 @@ export default observer(({ channel }: Props) => {
|
|||
|
||||
const renderer = getRenderer(channel);
|
||||
|
||||
if (!(channel.permission & ChannelPermission.SendMessage)) {
|
||||
if (!channel.havePermission("SendMessage")) {
|
||||
return (
|
||||
<Base>
|
||||
<Blocked>
|
||||
|
|
@ -231,7 +244,7 @@ export default observer(({ channel }: Props) => {
|
|||
);
|
||||
renderer.messages.reverse();
|
||||
|
||||
if (msg) {
|
||||
if (msg?.content) {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let [_, toReplace, newText, flags] = content.split(/\//);
|
||||
|
||||
|
|
@ -493,7 +506,7 @@ export default observer(({ channel }: Props) => {
|
|||
setReplies={setReplies}
|
||||
/>
|
||||
<Base>
|
||||
{channel.permission & ChannelPermission.UploadFiles ? (
|
||||
{channel.havePermission("UploadFiles") ? (
|
||||
<FileAction>
|
||||
<FileUploader
|
||||
size={24}
|
||||
|
|
@ -530,7 +543,9 @@ export default observer(({ channel }: Props) => {
|
|||
}}
|
||||
/>
|
||||
</FileAction>
|
||||
) : undefined}
|
||||
) : (
|
||||
<ThisCodeWillBeReplacedAnywaysSoIMightAsWellJustDoItThisWay__Padding />
|
||||
)}
|
||||
<TextAreaAutoSize
|
||||
autoFocus
|
||||
hideBorder
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ import {
|
|||
MessageSquareEdit,
|
||||
} from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { SystemMessage as SystemMessageI } from "revolt-api/types/Channels";
|
||||
import { Message } from "revolt.js/dist/maps/Messages";
|
||||
import { Message, API } from "revolt.js";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { useTriggerEvents } from "preact-context-menu";
|
||||
|
|
@ -75,13 +74,11 @@ export const SystemMessage = observer(
|
|||
({ attachContext, message, highlight, hideInfo }: Props) => {
|
||||
const data = message.asSystemMessage;
|
||||
const SystemMessageIcon =
|
||||
iconDictionary[data.type as SystemMessageI["type"]] ?? InfoCircle;
|
||||
iconDictionary[data.type as API.SystemMessage["type"]] ??
|
||||
InfoCircle;
|
||||
|
||||
let children;
|
||||
let children = null;
|
||||
switch (data.type) {
|
||||
case "text":
|
||||
children = <span>{data.content}</span>;
|
||||
break;
|
||||
case "user_added":
|
||||
case "user_remove":
|
||||
children = (
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Attachment as AttachmentI } from "revolt-api/types/Autumn";
|
||||
import { API } from "revolt.js";
|
||||
|
||||
import styles from "./Attachment.module.scss";
|
||||
import classNames from "classnames";
|
||||
|
|
@ -14,7 +14,7 @@ import Spoiler from "./Spoiler";
|
|||
import TextFile from "./TextFile";
|
||||
|
||||
interface Props {
|
||||
attachment: AttachmentI;
|
||||
attachment: API.File;
|
||||
hasContent?: boolean;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import {
|
|||
Download,
|
||||
} from "@styled-icons/boxicons-regular";
|
||||
import { File, Video } from "@styled-icons/boxicons-solid";
|
||||
import { Attachment } from "revolt-api/types/Autumn";
|
||||
import { isFirefox } from "react-device-detect";
|
||||
import { API } from "revolt.js";
|
||||
|
||||
import styles from "./AttachmentActions.module.scss";
|
||||
import classNames from "classnames";
|
||||
|
|
@ -17,7 +18,7 @@ import { AppContext } from "../../../../context/revoltjs/RevoltClient";
|
|||
import IconButton from "../../../ui/IconButton";
|
||||
|
||||
interface Props {
|
||||
attachment: Attachment;
|
||||
attachment: API.File;
|
||||
}
|
||||
|
||||
export default function AttachmentActions({ attachment }: Props) {
|
||||
|
|
@ -51,7 +52,7 @@ export default function AttachmentActions({ attachment }: Props) {
|
|||
href={download_url}
|
||||
className={styles.downloadIcon}
|
||||
download
|
||||
target="_blank"
|
||||
target={isFirefox || window.native ? "_blank" : "_self"}
|
||||
rel="noreferrer">
|
||||
<IconButton>
|
||||
<Download size={24} />
|
||||
|
|
@ -69,7 +70,7 @@ export default function AttachmentActions({ attachment }: Props) {
|
|||
href={download_url}
|
||||
className={styles.downloadIcon}
|
||||
download
|
||||
target="_blank"
|
||||
target={isFirefox || window.native ? "_blank" : "_self"}
|
||||
rel="noreferrer">
|
||||
<IconButton>
|
||||
<Download size={24} />
|
||||
|
|
@ -89,7 +90,7 @@ export default function AttachmentActions({ attachment }: Props) {
|
|||
href={download_url}
|
||||
className={styles.downloadIcon}
|
||||
download
|
||||
target="_blank"
|
||||
target={isFirefox || window.native ? "_blank" : "_self"}
|
||||
rel="noreferrer">
|
||||
<IconButton>
|
||||
<Download size={24} />
|
||||
|
|
@ -118,7 +119,7 @@ export default function AttachmentActions({ attachment }: Props) {
|
|||
href={download_url}
|
||||
className={styles.downloadIcon}
|
||||
download
|
||||
target="_blank"
|
||||
target={isFirefox || window.native ? "_blank" : "_self"}
|
||||
rel="noreferrer">
|
||||
<IconButton>
|
||||
<Download size={24} />
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const Grid = styled.div<{ width: number; height: number }>`
|
|||
--height: ${(props) => props.height}px;
|
||||
|
||||
display: grid;
|
||||
overflow: hidden;
|
||||
aspect-ratio: ${(props) => props.width} / ${(props) => props.height};
|
||||
|
||||
max-width: min(var(--width), var(--attachment-max-width));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Attachment } from "revolt-api/types/Autumn";
|
||||
import { API } from "revolt.js";
|
||||
|
||||
import styles from "./Attachment.module.scss";
|
||||
import classNames from "classnames";
|
||||
|
|
@ -10,12 +10,12 @@ import { AppContext } from "../../../../context/revoltjs/RevoltClient";
|
|||
enum ImageLoadingState {
|
||||
Loading,
|
||||
Loaded,
|
||||
Error
|
||||
Error,
|
||||
}
|
||||
|
||||
type Props = JSX.HTMLAttributes<HTMLImageElement> & {
|
||||
attachment: Attachment;
|
||||
}
|
||||
attachment: API.File;
|
||||
};
|
||||
|
||||
export default function ImageFile({ attachment, ...props }: Props) {
|
||||
const [loading, setLoading] = useState(ImageLoadingState.Loading);
|
||||
|
|
@ -23,25 +23,19 @@ export default function ImageFile({ attachment, ...props }: Props) {
|
|||
const { openScreen } = useIntermediate();
|
||||
const url = client.generateFileURL(attachment)!;
|
||||
|
||||
return <img
|
||||
{...props}
|
||||
src={url}
|
||||
alt={attachment.filename}
|
||||
loading="lazy"
|
||||
className={classNames(styles.image, {
|
||||
[styles.loading]: loading !== ImageLoadingState.Loaded
|
||||
})}
|
||||
onClick={() =>
|
||||
openScreen({ id: "image_viewer", attachment })
|
||||
}
|
||||
onMouseDown={(ev) =>
|
||||
ev.button === 1 && window.open(url, "_blank")
|
||||
}
|
||||
onLoad={() =>
|
||||
setLoading(ImageLoadingState.Loaded)
|
||||
}
|
||||
onError={() =>
|
||||
setLoading(ImageLoadingState.Error)
|
||||
}
|
||||
/>
|
||||
return (
|
||||
<img
|
||||
{...props}
|
||||
src={url}
|
||||
alt={attachment.filename}
|
||||
loading="lazy"
|
||||
className={classNames(styles.image, {
|
||||
[styles.loading]: loading !== ImageLoadingState.Loaded,
|
||||
})}
|
||||
onClick={() => openScreen({ id: "image_viewer", attachment })}
|
||||
onMouseDown={(ev) => ev.button === 1 && window.open(url, "_blank")}
|
||||
onLoad={() => setLoading(ImageLoadingState.Loaded)}
|
||||
onError={() => setLoading(ImageLoadingState.Error)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ import { Reply } from "@styled-icons/boxicons-regular";
|
|||
import { File } from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { RelationshipStatus } from "revolt-api/types/Users";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Message } from "revolt.js/dist/maps/Messages";
|
||||
import { Channel, Message, API } from "revolt.js";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
@ -174,7 +172,7 @@ export const MessageReply = observer(
|
|||
<ReplyBase head={index === 0}>
|
||||
{/*<Reply size={16} />*/}
|
||||
|
||||
{message.author?.relationship === RelationshipStatus.Blocked ? (
|
||||
{message.author?.relationship === "Blocked" ? (
|
||||
<Text id="app.main.channel.misc.blocked_user" />
|
||||
) : (
|
||||
<>
|
||||
|
|
@ -225,9 +223,10 @@ export const MessageReply = observer(
|
|||
)}
|
||||
<Markdown
|
||||
disallowBigEmoji
|
||||
content={(
|
||||
message.content as string
|
||||
).replace(/\n/g, " ")}
|
||||
content={message.content?.replace(
|
||||
/\n/g,
|
||||
" ",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import axios from "axios";
|
||||
import { Attachment } from "revolt-api/types/Autumn";
|
||||
import { API } from "revolt.js";
|
||||
|
||||
import styles from "./Attachment.module.scss";
|
||||
import { Text } from "preact-i18n";
|
||||
import { useContext, useEffect, useState } from "preact/hooks";
|
||||
|
||||
import RequiresOnline from "../../../../context/revoltjs/RequiresOnline";
|
||||
|
|
@ -11,14 +12,16 @@ import {
|
|||
} from "../../../../context/revoltjs/RevoltClient";
|
||||
|
||||
import Preloader from "../../../ui/Preloader";
|
||||
import { Button } from "@revoltchat/ui";
|
||||
|
||||
interface Props {
|
||||
attachment: Attachment;
|
||||
attachment: API.File;
|
||||
}
|
||||
|
||||
const fileCache: { [key: string]: string } = {};
|
||||
|
||||
export default function TextFile({ attachment }: Props) {
|
||||
const [gated, setGated] = useState(attachment.size > 100_000);
|
||||
const [content, setContent] = useState<undefined | string>(undefined);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const status = useContext(StatusContext);
|
||||
|
|
@ -29,13 +32,7 @@ export default function TextFile({ attachment }: Props) {
|
|||
useEffect(() => {
|
||||
if (typeof content !== "undefined") return;
|
||||
if (loading) return;
|
||||
|
||||
if (attachment.size > 100_000) {
|
||||
setContent(
|
||||
"This file is > 100 KB, for your sake I did not load it.\nSee tracking issue here for previews: https://github.com/revoltchat/revite/issues/35",
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (gated) return;
|
||||
|
||||
setLoading(true);
|
||||
|
||||
|
|
@ -60,13 +57,17 @@ export default function TextFile({ attachment }: Props) {
|
|||
setLoading(false);
|
||||
});
|
||||
}
|
||||
}, [content, loading, status, attachment._id, attachment.size, url]);
|
||||
}, [content, loading, gated, status, attachment._id, attachment.size, url]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.textContent}
|
||||
data-loading={typeof content === "undefined"}>
|
||||
{content ? (
|
||||
{gated ? (
|
||||
<Button palette="accent" onClick={() => setGated(false)}>
|
||||
<Text id="app.main.channel.misc.load_file" />
|
||||
</Button>
|
||||
) : content ? (
|
||||
<pre>
|
||||
<code>{content}</code>
|
||||
</pre>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { DownArrowAlt } from "@styled-icons/boxicons-regular";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Channel } from "revolt.js";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import {
|
|||
Notification,
|
||||
} from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { ChannelPermission } from "revolt.js";
|
||||
import { Message as MessageObject } from "revolt.js/dist/maps/Messages";
|
||||
import { Permission } from "revolt.js";
|
||||
import { Message as MessageObject } from "revolt.js";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { openContextMenu } from "preact-context-menu";
|
||||
|
|
@ -131,8 +131,7 @@ export const MessageOverlayBar = observer(({ message, queued }: Props) => {
|
|||
)}
|
||||
{isAuthor ||
|
||||
(message.channel &&
|
||||
message.channel.permission &
|
||||
ChannelPermission.ManageMessages) ? (
|
||||
message.channel.havePermission("ManageMessages")) ? (
|
||||
<Tooltip content="Delete">
|
||||
<Entry
|
||||
onClick={(e) =>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { UpArrowAlt } from "@styled-icons/boxicons-regular";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Channel } from "revolt.js";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { At, Reply as ReplyIcon } from "@styled-icons/boxicons-regular";
|
||||
import { At } from "@styled-icons/boxicons-regular";
|
||||
import { File, XCircle } from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Message } from "revolt.js/dist/maps/Messages";
|
||||
import { Channel, Message } from "revolt.js";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
@ -188,9 +187,10 @@ export default observer(({ channel, replies, setReplies }: Props) => {
|
|||
) : (
|
||||
<Markdown
|
||||
disallowBigEmoji
|
||||
content={(
|
||||
message.content as string
|
||||
).replace(/\n/g, " ")}
|
||||
content={message.content?.replace(
|
||||
/\n/g,
|
||||
" ",
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { RelationshipStatus } from "revolt-api/types/Users";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Channel } from "revolt.js";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
@ -65,7 +64,7 @@ export default observer(({ channel }: Props) => {
|
|||
(x) =>
|
||||
typeof x !== "undefined" &&
|
||||
x._id !== x.client.user!._id &&
|
||||
x.relationship !== RelationshipStatus.Blocked,
|
||||
x.relationship !== "Blocked",
|
||||
);
|
||||
|
||||
if (users.length > 0) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Embed as EmbedI } from "revolt-api/types/Channels";
|
||||
import { API } from "revolt.js";
|
||||
|
||||
import styles from "./Embed.module.scss";
|
||||
import classNames from "classnames";
|
||||
|
|
@ -13,7 +13,7 @@ import Attachment from "../attachments/Attachment";
|
|||
import EmbedMedia from "./EmbedMedia";
|
||||
|
||||
interface Props {
|
||||
embed: EmbedI;
|
||||
embed: API.Embed;
|
||||
}
|
||||
|
||||
const MAX_EMBED_WIDTH = 480;
|
||||
|
|
@ -68,7 +68,8 @@ export default function Embed({ embed }: Props) {
|
|||
mh = embed.video?.height ?? 720;
|
||||
break;
|
||||
}
|
||||
case "Twitch": {
|
||||
case "Twitch":
|
||||
case "Lightspeed": {
|
||||
mw = 1280;
|
||||
mh = 720;
|
||||
break;
|
||||
|
|
@ -89,6 +90,20 @@ export default function Embed({ embed }: Props) {
|
|||
}
|
||||
|
||||
const { width, height } = calculateSize(mw, mh);
|
||||
if (embed.type === "Website" && embed.special?.type === "GIF") {
|
||||
return (
|
||||
<EmbedMedia
|
||||
embed={embed}
|
||||
width={
|
||||
height *
|
||||
((embed.image?.width ?? 0) /
|
||||
(embed.image?.height ?? 0))
|
||||
}
|
||||
height={height}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(styles.embed, styles.website)}
|
||||
|
|
@ -128,7 +143,7 @@ export default function Embed({ embed }: Props) {
|
|||
<a
|
||||
onMouseDown={(ev) =>
|
||||
(ev.button === 0 || ev.button === 1) &&
|
||||
openLink(embed.url)
|
||||
openLink(embed.url!)
|
||||
}
|
||||
className={styles.title}>
|
||||
{embed.title}
|
||||
|
|
@ -181,6 +196,18 @@ export default function Embed({ embed }: Props) {
|
|||
/>
|
||||
);
|
||||
}
|
||||
case "Video": {
|
||||
return (
|
||||
<video
|
||||
className={classNames(styles.embed, styles.image)}
|
||||
style={calculateSize(embed.width, embed.height)}
|
||||
src={client.proxyFile(embed.url)}
|
||||
frameBorder="0"
|
||||
loading="lazy"
|
||||
controls
|
||||
/>
|
||||
);
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
import { Group } from "@styled-icons/boxicons-solid";
|
||||
import { autorun, reaction } from "mobx";
|
||||
import { reaction } from "mobx";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { RetrievedInvite } from "revolt-api/types/Invites";
|
||||
import { Message } from "revolt.js/dist/maps/Messages";
|
||||
import { Message, API } from "revolt.js";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
|
||||
import { useContext, useEffect, useState } from "preact/hooks";
|
||||
|
||||
import { defer } from "../../../../lib/defer";
|
||||
import { isTouchscreenDevice } from "../../../../lib/isTouchscreenDevice";
|
||||
|
||||
import {
|
||||
|
|
@ -85,9 +83,9 @@ export function EmbedInvite({ code }: Props) {
|
|||
const [processing, setProcessing] = useState(false);
|
||||
const [error, setError] = useState<string | undefined>(undefined);
|
||||
const [joinError, setJoinError] = useState<string | undefined>(undefined);
|
||||
const [invite, setInvite] = useState<RetrievedInvite | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const [invite, setInvite] = useState<
|
||||
(API.InviteResponse & { type: "Server" }) | undefined
|
||||
>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
|
|
@ -96,7 +94,9 @@ export function EmbedInvite({ code }: Props) {
|
|||
) {
|
||||
client
|
||||
.fetchInvite(code)
|
||||
.then((data) => setInvite(data))
|
||||
.then((data) =>
|
||||
setInvite(data as API.InviteResponse & { type: "Server" }),
|
||||
)
|
||||
.catch((err) => setError(takeError(err)));
|
||||
}
|
||||
}, [client, code, invite, status]);
|
||||
|
|
@ -139,42 +139,17 @@ export function EmbedInvite({ code }: Props) {
|
|||
) : (
|
||||
<Button
|
||||
onClick={async () => {
|
||||
setProcessing(true);
|
||||
|
||||
try {
|
||||
setProcessing(true);
|
||||
await client.joinInvite(invite);
|
||||
|
||||
if (invite.type === "Server") {
|
||||
if (client.servers.get(invite.server_id)) {
|
||||
history.push(
|
||||
`/server/${invite.server_id}/channel/${invite.channel_id}`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const dispose = reaction(
|
||||
() =>
|
||||
client.servers.get(
|
||||
invite.server_id,
|
||||
),
|
||||
(server) => {
|
||||
if (server) {
|
||||
client.unreads!.markMultipleRead(
|
||||
server.channel_ids,
|
||||
);
|
||||
|
||||
history.push(
|
||||
`/server/${server._id}/channel/${invite.channel_id}`,
|
||||
);
|
||||
|
||||
dispose();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
await client.joinInvite(code);
|
||||
history.push(
|
||||
`/server/${invite.server_id}/channel/${invite.channel_id}`,
|
||||
);
|
||||
} catch (err) {
|
||||
setJoinError(takeError(err));
|
||||
} finally {
|
||||
setProcessing(false);
|
||||
}
|
||||
}}>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { JanuaryEmbed } from "revolt-api/types/January";
|
||||
import { API } from "revolt.js";
|
||||
|
||||
import styles from "./Embed.module.scss";
|
||||
|
||||
|
|
@ -7,7 +7,7 @@ import { useIntermediate } from "../../../../context/intermediate/Intermediate";
|
|||
import { useClient } from "../../../../context/revoltjs/RevoltClient";
|
||||
|
||||
interface Props {
|
||||
embed: JanuaryEmbed;
|
||||
embed: API.Embed;
|
||||
width?: number;
|
||||
height: number;
|
||||
}
|
||||
|
|
@ -47,6 +47,17 @@ export default function EmbedMedia({ embed, width, height }: Props) {
|
|||
style={{ height }}
|
||||
/>
|
||||
);
|
||||
case "Lightspeed":
|
||||
return (
|
||||
<iframe
|
||||
src={`https://next.lightspeed.tv/embed/${embed.special.id}`}
|
||||
frameBorder="0"
|
||||
allowFullScreen
|
||||
scrolling="no"
|
||||
loading="lazy"
|
||||
style={{ height }}
|
||||
/>
|
||||
);
|
||||
case "Spotify":
|
||||
return (
|
||||
<iframe
|
||||
|
|
@ -83,7 +94,21 @@ export default function EmbedMedia({ embed, width, height }: Props) {
|
|||
);
|
||||
}
|
||||
default: {
|
||||
if (embed.image) {
|
||||
if (embed.video) {
|
||||
const url = embed.video.url;
|
||||
return (
|
||||
<video
|
||||
loading="lazy"
|
||||
className={styles.image}
|
||||
style={{ width, height }}
|
||||
src={client.proxyFile(url)}
|
||||
loop={embed.special?.type === "GIF"}
|
||||
controls={embed.special?.type !== "GIF"}
|
||||
autoPlay={embed.special?.type === "GIF"}
|
||||
muted={embed.special?.type === "GIF" ? true : undefined}
|
||||
/>
|
||||
);
|
||||
} else if (embed.image) {
|
||||
const url = embed.image.url;
|
||||
return (
|
||||
<img
|
||||
|
|
@ -94,7 +119,7 @@ export default function EmbedMedia({ embed, width, height }: Props) {
|
|||
onClick={() =>
|
||||
openScreen({
|
||||
id: "image_viewer",
|
||||
embed: embed.image,
|
||||
embed: embed.image!,
|
||||
})
|
||||
}
|
||||
onMouseDown={(ev) =>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { LinkExternal } from "@styled-icons/boxicons-regular";
|
||||
import { EmbedImage } from "revolt-api/types/January";
|
||||
import { API } from "revolt.js";
|
||||
|
||||
import styles from "./Embed.module.scss";
|
||||
|
||||
import IconButton from "../../../ui/IconButton";
|
||||
|
||||
interface Props {
|
||||
embed: EmbedImage;
|
||||
embed: API.Image;
|
||||
}
|
||||
|
||||
export default function EmbedMediaActions({ embed }: Props) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,23 @@
|
|||
import { Shield } from "@styled-icons/boxicons-regular";
|
||||
import { Badges } from "revolt-api/types/Users";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { Localizer, Text } from "preact-i18n";
|
||||
|
||||
import Tooltip from "../Tooltip";
|
||||
|
||||
enum Badges {
|
||||
Developer = 1,
|
||||
Translator = 2,
|
||||
Supporter = 4,
|
||||
ResponsibleDisclosure = 8,
|
||||
Founder = 16,
|
||||
PlatformModeration = 32,
|
||||
ActiveSupporter = 64,
|
||||
Paw = 128,
|
||||
EarlyAdopter = 256,
|
||||
ReservedRelevantJokeBadge1 = 512,
|
||||
}
|
||||
|
||||
const BadgesBase = styled.div`
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { User } from "revolt.js";
|
||||
|
||||
import Checkbox, { CheckboxProps } from "../../ui/Checkbox";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Cog } from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Link } from "react-router-dom";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { User } from "revolt.js";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { openContextMenu } from "preact-context-menu";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { User } from "revolt.js";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { Children } from "../../../types/Preact";
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { VolumeMute, MicrophoneOff } from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { Masquerade } from "revolt-api/types/Channels";
|
||||
import { Presence } from "revolt-api/types/Users";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { User, API } from "revolt.js";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
|
|
@ -18,17 +16,17 @@ type VoiceStatus = "muted" | "deaf";
|
|||
interface Props extends IconBaseProps<User> {
|
||||
status?: boolean;
|
||||
voice?: VoiceStatus;
|
||||
masquerade?: Masquerade;
|
||||
masquerade?: API.Masquerade;
|
||||
showServerIdentity?: boolean;
|
||||
}
|
||||
|
||||
export function useStatusColour(user?: User) {
|
||||
const theme = useApplicationState().settings.theme;
|
||||
|
||||
return user?.online && user?.status?.presence !== Presence.Invisible
|
||||
? user?.status?.presence === Presence.Idle
|
||||
return user?.online && user?.status?.presence !== "Invisible"
|
||||
? user?.status?.presence === "Idle"
|
||||
? theme.getVariable("status-away")
|
||||
: user?.status?.presence === Presence.Busy
|
||||
: user?.status?.presence === "Busy"
|
||||
? theme.getVariable("status-busy")
|
||||
: theme.getVariable("status-online")
|
||||
: theme.getVariable("status-invisible");
|
||||
|
|
@ -76,7 +74,7 @@ export default observer(
|
|||
|
||||
let { url } = props;
|
||||
if (masquerade?.avatar) {
|
||||
url = masquerade.avatar;
|
||||
url = client.proxyFile(masquerade.avatar);
|
||||
} else if (!url) {
|
||||
let override;
|
||||
if (target && showServerIdentity) {
|
||||
|
|
@ -95,7 +93,7 @@ export default observer(
|
|||
|
||||
url =
|
||||
client.generateFileURL(
|
||||
override ?? target?.avatar ?? attachment,
|
||||
override ?? target?.avatar ?? attachment ?? undefined,
|
||||
{ max_side: 256 },
|
||||
animate,
|
||||
) ?? (target ? target.defaultAvatarURL : fallback);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { Masquerade } from "revolt-api/types/Channels";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { Nullable } from "revolt.js/dist/util/null";
|
||||
import { User, API } from "revolt.js";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { Ref } from "preact";
|
||||
|
|
@ -32,7 +30,7 @@ const BotBadge = styled.div`
|
|||
type UsernameProps = JSX.HTMLAttributes<HTMLElement> & {
|
||||
user?: User;
|
||||
prefixAt?: boolean;
|
||||
masquerade?: Masquerade;
|
||||
masquerade?: API.Masquerade;
|
||||
showServerIdentity?: boolean | "both";
|
||||
|
||||
innerRef?: Ref<any>;
|
||||
|
|
@ -93,7 +91,11 @@ export const Username = observer(
|
|||
)}
|
||||
</span>
|
||||
<BotBadge>
|
||||
<Text id="app.main.channel.bot" />
|
||||
{masquerade ? (
|
||||
<Text id="app.main.channel.bridge" />
|
||||
) : (
|
||||
<Text id="app.main.channel.bot" />
|
||||
)}
|
||||
</BotBadge>
|
||||
</>
|
||||
);
|
||||
|
|
@ -120,7 +122,7 @@ export default function UserShort({
|
|||
user?: User;
|
||||
size?: number;
|
||||
prefixAt?: boolean;
|
||||
masquerade?: Masquerade;
|
||||
masquerade?: API.Masquerade;
|
||||
showServerIdentity?: boolean;
|
||||
}) {
|
||||
const { openScreen } = useIntermediate();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { Presence } from "revolt-api/types/Users";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { User, API } from "revolt.js";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
|
|
@ -25,15 +24,15 @@ export default observer(({ user, tooltip }: Props) => {
|
|||
return <>{user.status.text}</>;
|
||||
}
|
||||
|
||||
if (user.status?.presence === Presence.Busy) {
|
||||
if (user.status?.presence === "Busy") {
|
||||
return <Text id="app.status.busy" />;
|
||||
}
|
||||
|
||||
if (user.status?.presence === Presence.Idle) {
|
||||
if (user.status?.presence === "Idle") {
|
||||
return <Text id="app.status.idle" />;
|
||||
}
|
||||
|
||||
if (user.status?.presence === Presence.Invisible) {
|
||||
if (user.status?.presence === "Invisible") {
|
||||
return <Text id="app.status.offline" />;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Suspense, lazy } from "preact/compat";
|
|||
const Renderer = lazy(() => import("./Renderer"));
|
||||
|
||||
export interface MarkdownProps {
|
||||
content?: string;
|
||||
content?: string | null;
|
||||
disallowBigEmoji?: boolean;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,6 @@ import "katex/dist/katex.min.css";
|
|||
import MarkdownIt from "markdown-it";
|
||||
// @ts-expect-error No typings.
|
||||
import MarkdownEmoji from "markdown-it-emoji/dist/markdown-it-emoji-bare";
|
||||
// @ts-expect-error No typings.
|
||||
import MarkdownSub from "markdown-it-sub";
|
||||
// @ts-expect-error No typings.
|
||||
import MarkdownSup from "markdown-it-sup";
|
||||
import { RE_MENTIONS } from "revolt.js";
|
||||
|
||||
import styles from "./Markdown.module.scss";
|
||||
|
|
@ -64,8 +60,6 @@ export const md: MarkdownIt = MarkdownIt({
|
|||
.disable("image")
|
||||
.use(MarkdownEmoji, { defs: emojiDictionary })
|
||||
.use(MarkdownSpoilers)
|
||||
.use(MarkdownSup)
|
||||
.use(MarkdownSub)
|
||||
.use(MarkdownKatex, {
|
||||
throwOnError: false,
|
||||
maxExpand: 0,
|
||||
|
|
@ -129,7 +123,7 @@ export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
|
|||
const { openLink } = useIntermediate();
|
||||
|
||||
if (typeof content === "undefined") return null;
|
||||
if (content.length === 0) return null;
|
||||
if (!content || content.length === 0) return null;
|
||||
|
||||
// We replace the message with the mention at the time of render.
|
||||
// We don't care if the mention changes.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { X } from "@styled-icons/boxicons-regular";
|
||||
import { Crown } from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Presence } from "revolt-api/types/Users";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { User, Channel } from "revolt.js";
|
||||
|
||||
import styles from "./Item.module.scss";
|
||||
import classNames from "classnames";
|
||||
|
|
@ -65,7 +63,7 @@ export const UserButton = observer((props: UserProps) => {
|
|||
data-alert={typeof alert === "string"}
|
||||
data-online={
|
||||
typeof channel !== "undefined" ||
|
||||
(user.online && user.status?.presence !== Presence.Invisible)
|
||||
(user.online && user.status?.presence !== "Invisible")
|
||||
}
|
||||
{...useTriggerEvents("Menu", {
|
||||
user: user._id,
|
||||
|
|
|
|||
|
|
@ -4,12 +4,14 @@ import { useContext } from "preact/hooks";
|
|||
import {
|
||||
ClientStatus,
|
||||
StatusContext,
|
||||
useClient,
|
||||
} from "../../../context/revoltjs/RevoltClient";
|
||||
|
||||
import Banner from "../../ui/Banner";
|
||||
|
||||
export default function ConnectionStatus() {
|
||||
const status = useContext(StatusContext);
|
||||
const client = useClient();
|
||||
|
||||
if (status === ClientStatus.OFFLINE) {
|
||||
return (
|
||||
|
|
@ -20,7 +22,10 @@ export default function ConnectionStatus() {
|
|||
} else if (status === ClientStatus.DISCONNECTED) {
|
||||
return (
|
||||
<Banner>
|
||||
<Text id="app.special.status.disconnected" />
|
||||
<Text id="app.special.status.disconnected" /> <br />
|
||||
<a onClick={() => client.websocket.connect()}>
|
||||
<Text id="app.special.status.reconnect" />
|
||||
</a>
|
||||
</Banner>
|
||||
);
|
||||
} else if (status === ClientStatus.CONNECTING) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import {
|
|||
} from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Link, useLocation, useParams } from "react-router-dom";
|
||||
import { RelationshipStatus } from "revolt-api/types/Users";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
@ -47,14 +46,16 @@ export default observer(() => {
|
|||
const { pathname } = useLocation();
|
||||
const client = useContext(AppContext);
|
||||
const state = useApplicationState();
|
||||
const { channel: currentChannel } = useParams<{ channel: string }>();
|
||||
const { channel: channel_id } = useParams<{ channel: string }>();
|
||||
const { openScreen } = useIntermediate();
|
||||
|
||||
const channels = [...client.channels.values()].filter(
|
||||
(x) => x.channel_type === "DirectMessage" || x.channel_type === "Group",
|
||||
(x) =>
|
||||
(x.channel_type === "DirectMessage" && x.active) ||
|
||||
x.channel_type === "Group",
|
||||
);
|
||||
|
||||
const obj = client.channels.get(currentChannel);
|
||||
const channel = client.channels.get(channel_id);
|
||||
|
||||
// ! FIXME: move this globally
|
||||
// Track what page the user was last on (in home page).
|
||||
|
|
@ -66,7 +67,7 @@ export default observer(() => {
|
|||
|
||||
// ! FIXME: must be a better way
|
||||
const incoming = [...client.users.values()].filter(
|
||||
(user) => user?.relationship === RelationshipStatus.Incoming,
|
||||
(user) => user?.relationship === "Incoming",
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
@ -104,9 +105,10 @@ export default observer(() => {
|
|||
</>
|
||||
)}
|
||||
<ConditionalLink
|
||||
active={obj?.channel_type === "SavedMessages"}
|
||||
active={channel?.channel_type === "SavedMessages"}
|
||||
to="/open/saved">
|
||||
<ButtonItem active={obj?.channel_type === "SavedMessages"}>
|
||||
<ButtonItem
|
||||
active={channel?.channel_type === "SavedMessages"}>
|
||||
<Notepad size={20} />
|
||||
<span>
|
||||
<Text id="app.navigation.tabs.saved" />
|
||||
|
|
@ -152,7 +154,7 @@ export default observer(() => {
|
|||
return (
|
||||
<ConditionalLink
|
||||
key={channel._id}
|
||||
active={channel._id === currentChannel}
|
||||
active={channel._id === channel_id}
|
||||
to={`/channel/${channel._id}`}>
|
||||
<ChannelButton
|
||||
user={user}
|
||||
|
|
@ -165,7 +167,7 @@ export default observer(() => {
|
|||
: undefined
|
||||
}
|
||||
alertCount={mentionCount}
|
||||
active={channel._id === currentChannel}
|
||||
active={channel._id === channel_id}
|
||||
/>
|
||||
</ConditionalLink>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@ import { Plus } from "@styled-icons/boxicons-regular";
|
|||
import { Cog, Compass } from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Link, useHistory, useLocation, useParams } from "react-router-dom";
|
||||
import { RelationshipStatus } from "revolt-api/types/Users";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
|
||||
import { Ref } from "preact";
|
||||
import { useTriggerEvents } from "preact-context-menu";
|
||||
|
||||
import ConditionalLink from "../../../lib/ConditionalLink";
|
||||
|
|
@ -248,7 +246,7 @@ export default observer(() => {
|
|||
const { openScreen } = useIntermediate();
|
||||
|
||||
let alertCount = [...client.users.values()].filter(
|
||||
(x) => x.relationship === RelationshipStatus.Incoming,
|
||||
(x) => x.relationship === "Incoming",
|
||||
).length;
|
||||
|
||||
const homeActive =
|
||||
|
|
@ -290,7 +288,7 @@ export default observer(() => {
|
|||
{channels
|
||||
.filter(
|
||||
(x) =>
|
||||
(x.channel_type === "DirectMessage" ||
|
||||
((x.channel_type === "DirectMessage" && x.active) ||
|
||||
x.channel_type === "Group") &&
|
||||
x.unread,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { Redirect, useParams } from "react-router";
|
||||
import { Server } from "revolt.js/dist/maps/Servers";
|
||||
import { Server } from "revolt.js";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
|
||||
import { Ref } from "preact";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Channel } from "revolt.js";
|
||||
|
||||
import { getRenderer } from "../../../lib/renderer/Singleton";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { Link } from "react-router-dom";
|
||||
import { GroupedVirtuoso } from "react-virtuoso";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { Channel, User } from "revolt.js";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
|
|||
|
|
@ -2,11 +2,7 @@
|
|||
import { autorun } from "mobx";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { Role } from "revolt-api/types/Servers";
|
||||
import { Presence } from "revolt-api/types/Users";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Server } from "revolt.js/dist/maps/Servers";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { Channel, Server, User, API } from "revolt.js";
|
||||
|
||||
import { useContext, useEffect, useState } from "preact/hooks";
|
||||
|
||||
|
|
@ -62,7 +58,7 @@ function useEntries(
|
|||
.map((id) => {
|
||||
return [id, roles![id], roles![id].rank ?? 0] as [
|
||||
string,
|
||||
Role,
|
||||
API.Role,
|
||||
number,
|
||||
];
|
||||
})
|
||||
|
|
@ -96,7 +92,7 @@ function useEntries(
|
|||
const sort = member?.nickname ?? u.username;
|
||||
const entry = [u, sort] as [User, string];
|
||||
|
||||
if (!u.online || u.status?.presence === Presence.Invisible) {
|
||||
if (!u.online || u.status?.presence === "Invisible") {
|
||||
categories.offline.push(entry);
|
||||
} else {
|
||||
if (isServer) {
|
||||
|
|
@ -164,7 +160,7 @@ function useEntries(
|
|||
useEffect(() => {
|
||||
return autorun(() => sort(generateKeys()));
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
}, [channel]);
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Link, useParams } from "react-router-dom";
|
||||
import { Message as MessageI } from "revolt.js/dist/maps/Messages";
|
||||
import { Message as MessageI } from "revolt.js";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
import { API, Channel, Member, Server } from "revolt.js";
|
||||
import { Permission } from "revolt.js";
|
||||
|
||||
import { PermissionSelect } from "./PermissionSelect";
|
||||
|
||||
interface Props {
|
||||
value: API.OverrideField | number;
|
||||
onChange: (v: API.OverrideField | number) => void;
|
||||
|
||||
target?: Channel | Server;
|
||||
filter?: (keyof typeof Permission)[];
|
||||
}
|
||||
|
||||
export function PermissionList({ value, onChange, filter, target }: Props) {
|
||||
return (
|
||||
<>
|
||||
{(Object.keys(Permission) as (keyof typeof Permission)[])
|
||||
.filter(
|
||||
(key) =>
|
||||
![
|
||||
"GrantAllSafe",
|
||||
"TimeoutMembers",
|
||||
"ReadMessageHistory",
|
||||
"Speak",
|
||||
"Video",
|
||||
"MuteMembers",
|
||||
"DeafenMembers",
|
||||
"MoveMembers",
|
||||
"ManageWebhooks",
|
||||
].includes(key) &&
|
||||
(!filter || filter.includes(key)),
|
||||
)
|
||||
.map((x) => (
|
||||
<PermissionSelect
|
||||
id={x}
|
||||
key={x}
|
||||
permission={Permission[x]}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
target={target}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
import { Lock } from "@styled-icons/boxicons-solid";
|
||||
import Long from "long";
|
||||
import { API, Channel, Member, Server } from "revolt.js";
|
||||
import { Permission } from "revolt.js";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
import { useMemo } from "preact/hooks";
|
||||
|
||||
import Checkbox from "../../ui/Checkbox";
|
||||
import { OverrideSwitch } from "@revoltchat/ui";
|
||||
|
||||
interface PermissionSelectProps {
|
||||
id: keyof typeof Permission;
|
||||
target?: Channel | Server;
|
||||
permission: number;
|
||||
value: API.OverrideField | number;
|
||||
onChange: (value: API.OverrideField | number) => void;
|
||||
}
|
||||
|
||||
type State = "Allow" | "Neutral" | "Deny";
|
||||
|
||||
const PermissionEntry = styled.label<{ disabled?: boolean }>`
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
margin: 8px 0;
|
||||
display: flex;
|
||||
font-size: 1.1em;
|
||||
align-items: center;
|
||||
|
||||
.title {
|
||||
flex-grow: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.lock {
|
||||
margin-inline-start: 4px;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 0.8em;
|
||||
color: var(--secondary-foreground);
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.disabled &&
|
||||
css`
|
||||
color: var(--tertiary-foreground);
|
||||
`}
|
||||
`;
|
||||
|
||||
export function PermissionSelect({
|
||||
id,
|
||||
permission,
|
||||
value,
|
||||
onChange,
|
||||
target,
|
||||
}: PermissionSelectProps) {
|
||||
const state: State = useMemo(() => {
|
||||
if (typeof value === "object") {
|
||||
if (Long.fromNumber(value.d).and(permission).eq(permission)) {
|
||||
return "Deny";
|
||||
}
|
||||
|
||||
if (Long.fromNumber(value.a).and(permission).eq(permission)) {
|
||||
return "Allow";
|
||||
}
|
||||
|
||||
return "Neutral";
|
||||
} else {
|
||||
if (Long.fromNumber(value).and(permission).eq(permission)) {
|
||||
return "Allow";
|
||||
}
|
||||
|
||||
return "Neutral";
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
function onSwitch(state: State) {
|
||||
if (typeof value !== "object") throw "!";
|
||||
|
||||
// Convert to Long so we can do bitwise ops.
|
||||
let allow = Long.fromNumber(value.a);
|
||||
let deny = Long.fromNumber(value.d);
|
||||
|
||||
// Clear the current permission value.
|
||||
if (allow.and(permission).eq(permission)) {
|
||||
allow = allow.xor(permission);
|
||||
}
|
||||
|
||||
if (deny.and(permission).eq(permission)) {
|
||||
deny = deny.xor(permission);
|
||||
}
|
||||
|
||||
// Apply the current permission state.
|
||||
if (state === "Allow") {
|
||||
allow = allow.or(permission);
|
||||
}
|
||||
|
||||
if (state === "Deny") {
|
||||
deny = deny.or(permission);
|
||||
}
|
||||
|
||||
// Invoke state change.
|
||||
onChange({
|
||||
a: allow.toNumber(),
|
||||
d: deny.toNumber(),
|
||||
});
|
||||
}
|
||||
|
||||
const member =
|
||||
target &&
|
||||
(target instanceof Server ? target.member : target.server?.member);
|
||||
|
||||
const disabled = member && !member.hasPermission(target!, id);
|
||||
|
||||
return (
|
||||
<PermissionEntry disabled={disabled}>
|
||||
<span class="title">
|
||||
<span>
|
||||
<Text id={`permissions.${id}.t`}>{id}</Text>
|
||||
{disabled && <Lock className="lock" size={14} />}
|
||||
</span>
|
||||
<span class="description">
|
||||
<Text id={`permissions.${id}.d`} />
|
||||
</span>
|
||||
</span>
|
||||
{typeof value === "object" ? (
|
||||
<OverrideSwitch
|
||||
disabled={disabled}
|
||||
state={state}
|
||||
onChange={onSwitch}
|
||||
/>
|
||||
) : (
|
||||
<Checkbox
|
||||
disabled={disabled}
|
||||
checked={state === "Allow"}
|
||||
onChange={() =>
|
||||
onChange(
|
||||
Long.fromNumber(value, false)
|
||||
.xor(permission)
|
||||
.toNumber(),
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</PermissionEntry>
|
||||
);
|
||||
}
|
||||
|
|
@ -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 };
|
||||
|
|
@ -89,7 +89,7 @@ export interface CheckboxProps {
|
|||
disabled?: boolean;
|
||||
contrast?: boolean;
|
||||
className?: string;
|
||||
children: Children;
|
||||
children?: Children;
|
||||
description?: Children;
|
||||
onChange: (state: boolean) => void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-expect-error No typings.
|
||||
import rgba from "color-rgba";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Helmet } from "react-helmet";
|
||||
|
|
@ -102,7 +103,7 @@ export const FONTS: Record<Fonts, { name: string; load: () => void }> = {
|
|||
},
|
||||
},
|
||||
|
||||
"OpenDyslexic": {
|
||||
OpenDyslexic: {
|
||||
name: "OpenDyslexic",
|
||||
load: async () => {
|
||||
await import("@fontsource/opendyslexic/400.css");
|
||||
|
|
@ -319,6 +320,14 @@ const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
|
|||
:root {
|
||||
${(props) => generateVariables(props.theme)}
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.theme["min-opacity"] === 1 &&
|
||||
`
|
||||
* {
|
||||
backdrop-filter: unset !important;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
export const generateVariables = (theme: Theme) => {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
import { Prompt } from "react-router";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import type { Attachment } from "revolt-api/types/Autumn";
|
||||
import { Bot } from "revolt-api/types/Bots";
|
||||
import { TextChannel, VoiceChannel } from "revolt-api/types/Channels";
|
||||
import type { EmbedImage } from "revolt-api/types/January";
|
||||
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 { User } from "revolt.js/dist/maps/Users";
|
||||
import { API, Channel, Message, Server, User } from "revolt.js";
|
||||
|
||||
import { createContext } from "preact";
|
||||
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
|
||||
|
|
@ -31,6 +24,7 @@ export type Screen =
|
|||
| { id: "clipboard"; text: string }
|
||||
| { id: "token_reveal"; token: string; username: string }
|
||||
| { id: "external_link_prompt"; link: string }
|
||||
| { id: "sessions", confirm: () => void }
|
||||
| {
|
||||
id: "_prompt";
|
||||
question: Children;
|
||||
|
|
@ -61,7 +55,11 @@ export type Screen =
|
|||
| {
|
||||
type: "create_channel";
|
||||
target: Server;
|
||||
cb?: (channel: TextChannel | VoiceChannel) => void;
|
||||
cb?: (
|
||||
channel: Channel & {
|
||||
channel_type: "TextChannel" | "VoiceChannel";
|
||||
},
|
||||
) => void;
|
||||
}
|
||||
| { type: "create_category"; target: Server }
|
||||
))
|
||||
|
|
@ -101,11 +99,11 @@ export type Screen =
|
|||
omit?: string[];
|
||||
callback: (users: string[]) => Promise<void>;
|
||||
}
|
||||
| { id: "image_viewer"; attachment?: Attachment; embed?: EmbedImage }
|
||||
| { id: "image_viewer"; attachment?: API.File; embed?: API.Image }
|
||||
| { id: "channel_info"; channel: Channel }
|
||||
| { id: "pending_requests"; users: User[] }
|
||||
| { id: "modify_account"; field: "username" | "email" | "password" }
|
||||
| { id: "create_bot"; onCreate: (bot: Bot) => void }
|
||||
| { id: "create_bot"; onCreate: (bot: API.Bot) => void }
|
||||
| {
|
||||
id: "server_identity";
|
||||
server: Server;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ import { InputModal } from "./modals/Input";
|
|||
import { OnboardingModal } from "./modals/Onboarding";
|
||||
import { PromptModal } from "./modals/Prompt";
|
||||
import { SignedOutModal } from "./modals/SignedOut";
|
||||
import {ExternalLinkModal} from "./modals/ExternalLinkPrompt";
|
||||
import { ExternalLinkModal} from "./modals/ExternalLinkPrompt";
|
||||
import { SessionsModal } from "./modals/SessionsPrompt";
|
||||
import { TokenRevealModal } from "./modals/TokenReveal";
|
||||
|
||||
export interface Props {
|
||||
|
|
@ -40,6 +41,8 @@ export default function Modals({ screen, openScreen }: Props) {
|
|||
return <OnboardingModal onClose={onClose} {...screen} />;
|
||||
case "external_link_prompt":
|
||||
return <ExternalLinkModal onClose={onClose} {...screen} />;
|
||||
case "sessions":
|
||||
return <SessionsModal onClose={onClose} {...screen} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export function ErrorModal({ onClose, error }: Props) {
|
|||
return (
|
||||
<Modal
|
||||
visible={true}
|
||||
onClose={() => false}
|
||||
onClose={onClose}
|
||||
title={<Text id="app.special.modals.error" />}
|
||||
actions={[
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useHistory } from "react-router";
|
||||
import { Server } from "revolt.js/dist/maps/Servers";
|
||||
import { Server } from "revolt.js";
|
||||
import { ulid } from "ulid";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
@ -69,6 +69,7 @@ export function InputModal({
|
|||
)}
|
||||
<InputBox
|
||||
value={value}
|
||||
style={{ width: "100%" }}
|
||||
onChange={(e) => setValue(e.currentTarget.value)}
|
||||
/>
|
||||
</Modal>
|
||||
|
|
@ -101,7 +102,6 @@ export function SpecialInputModal(props: SpecialProps) {
|
|||
callback={async (name) => {
|
||||
const group = await client.channels.createGroup({
|
||||
name,
|
||||
nonce: ulid(),
|
||||
users: [],
|
||||
});
|
||||
|
||||
|
|
@ -130,7 +130,6 @@ export function SpecialInputModal(props: SpecialProps) {
|
|||
callback={async (name) => {
|
||||
const server = await client.servers.createServer({
|
||||
name,
|
||||
nonce: ulid(),
|
||||
});
|
||||
|
||||
history.push(`/server/${server._id}`);
|
||||
|
|
@ -159,7 +158,7 @@ export function SpecialInputModal(props: SpecialProps) {
|
|||
onClose={onClose}
|
||||
question={<Text id="app.context_menu.set_custom_status" />}
|
||||
field={<Text id="app.context_menu.custom_status" />}
|
||||
defaultValue={client.user?.status?.text}
|
||||
defaultValue={client.user?.status?.text ?? undefined}
|
||||
callback={(text) =>
|
||||
client.users.edit({
|
||||
status: {
|
||||
|
|
@ -177,11 +176,8 @@ export function SpecialInputModal(props: SpecialProps) {
|
|||
onClose={onClose}
|
||||
question={"Add Friend"}
|
||||
callback={(username) =>
|
||||
client
|
||||
.req(
|
||||
"PUT",
|
||||
`/users/${username}/friend` as "/users/id/friend",
|
||||
)
|
||||
client.api
|
||||
.put(`/users/${username as ""}/friend`)
|
||||
.then(undefined)
|
||||
}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@
|
|||
h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
max-height: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
&.form {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { TextChannel, VoiceChannel } from "revolt-api/types/Channels";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Message as MessageI } from "revolt.js/dist/maps/Messages";
|
||||
import { Server } from "revolt.js/dist/maps/Servers";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { Channel, Message as MessageI, Server, User } from "revolt.js";
|
||||
import { ulid } from "ulid";
|
||||
|
||||
import styles from "./Prompt.module.scss";
|
||||
|
|
@ -74,7 +70,11 @@ type SpecialProps = { onClose: () => void } & (
|
|||
| {
|
||||
type: "create_channel";
|
||||
target: Server;
|
||||
cb?: (channel: TextChannel | VoiceChannel) => void;
|
||||
cb?: (
|
||||
channel: Channel & {
|
||||
channel_type: "TextChannel" | "VoiceChannel";
|
||||
},
|
||||
) => void;
|
||||
}
|
||||
| { type: "create_category"; target: Server }
|
||||
);
|
||||
|
|
@ -254,7 +254,7 @@ export const SpecialPromptModal = observer((props: SpecialProps) => {
|
|||
|
||||
props.target
|
||||
.createInvite()
|
||||
.then((code) => setCode(code))
|
||||
.then(({ _id }) => setCode(_id))
|
||||
.catch((err) => setError(takeError(err)))
|
||||
.finally(() => setProcessing(false));
|
||||
}, [props.target]);
|
||||
|
|
@ -429,11 +429,10 @@ export const SpecialPromptModal = observer((props: SpecialProps) => {
|
|||
await props.target.createChannel({
|
||||
type,
|
||||
name,
|
||||
nonce: ulid(),
|
||||
});
|
||||
|
||||
if (props.cb) {
|
||||
props.cb(channel);
|
||||
props.cb(channel as any);
|
||||
} else {
|
||||
history.push(
|
||||
`/server/${props.target._id}/channel/${channel._id}`,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import { Text } from "preact-i18n";
|
||||
|
||||
import Modal from "../../../components/ui/Modal";
|
||||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
confirm: () => void;
|
||||
}
|
||||
|
||||
export function SessionsModal({ onClose, confirm}: Props) {
|
||||
return (
|
||||
<Modal
|
||||
visible={true}
|
||||
onClose={onClose}
|
||||
title={<Text id={"app.special.modals.sessions.title"} />}
|
||||
actions={[
|
||||
{
|
||||
onClick: () => {
|
||||
onClose()
|
||||
},
|
||||
confirmation: true,
|
||||
contrast: true,
|
||||
accent: true,
|
||||
children: <Text id="app.special.modals.actions.back"/>
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
confirm()
|
||||
onClose()
|
||||
},
|
||||
confirmation: true,
|
||||
children: <Text id="app.special.modals.sessions.accept"/>
|
||||
}
|
||||
]}>
|
||||
<Text id="app.special.modals.sessions.short" /> <br />
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { X } from "@styled-icons/boxicons-regular";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Channel } from "revolt.js";
|
||||
|
||||
import styles from "./ChannelInfo.module.scss";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { SubmitHandler, useForm } from "react-hook-form";
|
||||
import { Bot } from "revolt-api/types/Bots";
|
||||
import { API } from "revolt.js";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
import { useContext, useState } from "preact/hooks";
|
||||
|
|
@ -13,7 +13,7 @@ import { takeError } from "../../revoltjs/util";
|
|||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
onCreate: (bot: Bot) => void;
|
||||
onCreate: (bot: API.Bot) => void;
|
||||
}
|
||||
|
||||
interface FormInputs {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { Attachment, AttachmentMetadata } from "revolt-api/types/Autumn";
|
||||
import { EmbedImage } from "revolt-api/types/January";
|
||||
import { API } from "revolt.js";
|
||||
|
||||
import styles from "./ImageViewer.module.scss";
|
||||
|
||||
|
|
@ -12,11 +11,11 @@ import { useClient } from "../../revoltjs/RevoltClient";
|
|||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
embed?: EmbedImage;
|
||||
attachment?: Attachment;
|
||||
embed?: API.Image;
|
||||
attachment?: API.File;
|
||||
}
|
||||
|
||||
type ImageMetadata = AttachmentMetadata & { type: "Image" };
|
||||
type ImageMetadata = API.Metadata & { type: "Image" };
|
||||
|
||||
export function ImageViewer({ attachment, embed, onClose }: Props) {
|
||||
if (attachment && attachment.metadata.type !== "Image") {
|
||||
|
|
|
|||
|
|
@ -43,19 +43,19 @@ export function ModifyAccountModal({ onClose, field }: Props) {
|
|||
|
||||
try {
|
||||
if (field === "email") {
|
||||
await client.req("PATCH", "/auth/account/change/email", {
|
||||
await client.api.patch("/auth/account/change/email", {
|
||||
current_password: password,
|
||||
email: new_email,
|
||||
});
|
||||
onClose();
|
||||
} else if (field === "password") {
|
||||
await client.req("PATCH", "/auth/account/change/password", {
|
||||
await client.api.patch("/auth/account/change/password", {
|
||||
current_password: password,
|
||||
password: new_password,
|
||||
});
|
||||
onClose();
|
||||
} else if (field === "username") {
|
||||
await client.req("PATCH", "/users/id/username", {
|
||||
await client.api.patch("/users/@me/username", {
|
||||
username: new_username,
|
||||
password,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { User } from "revolt.js";
|
||||
|
||||
import styles from "./UserPicker.module.scss";
|
||||
import { Text } from "preact-i18n";
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { Server } from "revolt.js/dist/maps/Servers";
|
||||
import styled, { css } from "styled-components/macro";
|
||||
import { Server } from "revolt.js";
|
||||
|
||||
import styles from "./ServerIdentityModal.module.scss";
|
||||
import { Text } from "preact-i18n";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
import { noop } from "../../../lib/js";
|
||||
|
||||
import Button from "../../../components/ui/Button";
|
||||
import InputBox from "../../../components/ui/InputBox";
|
||||
import Modal from "../../../components/ui/Modal";
|
||||
|
|
@ -57,8 +58,12 @@ export const ServerIdentityModal = observer(({ server, onClose }: Props) => {
|
|||
fileType="avatars"
|
||||
behaviour="upload"
|
||||
maxFileSize={4_000_000}
|
||||
onUpload={(avatar) => member.edit({ avatar })}
|
||||
remove={() => member.edit({ remove: "Avatar" })}
|
||||
onUpload={(avatar) =>
|
||||
member.edit({ avatar }).then(noop)
|
||||
}
|
||||
remove={() =>
|
||||
member.edit({ remove: ["Avatar"] }).then(noop)
|
||||
}
|
||||
defaultPreview={client.user?.generateAvatarURL(
|
||||
{
|
||||
max_side: 256,
|
||||
|
|
@ -92,7 +97,7 @@ export const ServerIdentityModal = observer(({ server, onClose }: Props) => {
|
|||
<Button
|
||||
plain
|
||||
onClick={() =>
|
||||
member.edit({ remove: "Nickname" })
|
||||
member.edit({ remove: ["Nickname"] })
|
||||
}>
|
||||
<Text id="app.special.modals.actions.remove" />
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import { RelationshipStatus } from "revolt-api/types/Users";
|
||||
|
||||
import styles from "./UserPicker.module.scss";
|
||||
import { Text } from "preact-i18n";
|
||||
import { useState } from "preact/hooks";
|
||||
|
|
@ -37,7 +35,7 @@ export function UserPicker(props: Props) {
|
|||
.filter(
|
||||
(x) =>
|
||||
x &&
|
||||
x.relationship === RelationshipStatus.Friend &&
|
||||
x.relationship === "Friend" &&
|
||||
!omit.includes(x._id),
|
||||
)
|
||||
.map((x) => (
|
||||
|
|
|
|||
|
|
@ -100,6 +100,10 @@
|
|||
background: var(--primary-background);
|
||||
border-radius: 0 0 var(--border-radius) var(--border-radius);
|
||||
|
||||
.markdown {
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.empty {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@ import {
|
|||
} from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Link, useHistory } from "react-router-dom";
|
||||
import { Profile, RelationshipStatus } from "revolt-api/types/Users";
|
||||
import { UserPermission } from "revolt.js/dist/api/permissions";
|
||||
import { Route } from "revolt.js/dist/api/routes";
|
||||
import { UserPermission, API } from "revolt.js";
|
||||
|
||||
import styles from "./UserProfile.module.scss";
|
||||
import { Localizer, Text } from "preact-i18n";
|
||||
|
|
@ -44,18 +42,18 @@ interface Props {
|
|||
user_id: string;
|
||||
dummy?: boolean;
|
||||
onClose?: () => void;
|
||||
dummyProfile?: Profile;
|
||||
dummyProfile?: API.UserProfile;
|
||||
}
|
||||
|
||||
export const UserProfile = observer(
|
||||
({ user_id, onClose, dummy, dummyProfile }: Props) => {
|
||||
const { openScreen, writeClipboard } = useIntermediate();
|
||||
|
||||
const [profile, setProfile] = useState<undefined | null | Profile>(
|
||||
undefined,
|
||||
);
|
||||
const [profile, setProfile] = useState<
|
||||
undefined | null | API.UserProfile
|
||||
>(undefined);
|
||||
const [mutual, setMutual] = useState<
|
||||
undefined | null | Route<"GET", "/users/id/mutual">["response"]
|
||||
undefined | null | API.MutualResponse
|
||||
>(undefined);
|
||||
const [isPublicBot, setIsPublicBot] = useState<
|
||||
undefined | null | boolean
|
||||
|
|
@ -139,7 +137,11 @@ export const UserProfile = observer(
|
|||
|
||||
const backgroundURL =
|
||||
profile &&
|
||||
client.generateFileURL(profile.background, { width: 1000 }, true);
|
||||
client.generateFileURL(
|
||||
profile.background as any,
|
||||
{ width: 1000 },
|
||||
true,
|
||||
);
|
||||
|
||||
const badges = user.badges ?? 0;
|
||||
const flags = user.flags ?? 0;
|
||||
|
|
@ -198,7 +200,7 @@ export const UserProfile = observer(
|
|||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
{user.relationship === RelationshipStatus.Friend && (
|
||||
{user.relationship === "Friend" && (
|
||||
<Localizer>
|
||||
<Tooltip
|
||||
content={
|
||||
|
|
@ -214,28 +216,26 @@ export const UserProfile = observer(
|
|||
</Tooltip>
|
||||
</Localizer>
|
||||
)}
|
||||
{user.relationship === RelationshipStatus.User &&
|
||||
!dummy && (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
onClose?.();
|
||||
history.push(`/settings/profile`);
|
||||
}}>
|
||||
<Edit size={28} />
|
||||
</IconButton>
|
||||
)}
|
||||
{user.relationship === "User" && !dummy && (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
onClose?.();
|
||||
history.push(`/settings/profile`);
|
||||
}}>
|
||||
<Edit size={28} />
|
||||
</IconButton>
|
||||
)}
|
||||
{!user.bot &&
|
||||
flags != 2 &&
|
||||
flags != 4 &&
|
||||
(user.relationship ===
|
||||
RelationshipStatus.Incoming ||
|
||||
user.relationship ===
|
||||
RelationshipStatus.None) && (
|
||||
(user.relationship === "Incoming" ||
|
||||
user.relationship === "None" ||
|
||||
user.relationship === null) && (
|
||||
<IconButton onClick={() => user.addFriend()}>
|
||||
<UserPlus size={28} />
|
||||
</IconButton>
|
||||
)}
|
||||
{user.relationship === RelationshipStatus.Outgoing && (
|
||||
{user.relationship === "Outgoing" && (
|
||||
<IconButton onClick={() => user.removeFriend()}>
|
||||
<UserX size={28} />
|
||||
</IconButton>
|
||||
|
|
@ -247,7 +247,7 @@ export const UserProfile = observer(
|
|||
onClick={() => setTab("profile")}>
|
||||
<Text id="app.special.popovers.user_profile.profile" />
|
||||
</div>
|
||||
{user.relationship !== RelationshipStatus.User && (
|
||||
{user.relationship !== "User" && (
|
||||
<>
|
||||
<div
|
||||
data-active={tab === "friends"}
|
||||
|
|
@ -338,7 +338,9 @@ export const UserProfile = observer(
|
|||
<Text id="app.special.popovers.user_profile.sub.information" />
|
||||
</div>
|
||||
)}
|
||||
<Markdown content={profile?.content} />
|
||||
<div className={styles.markdown}>
|
||||
<Markdown content={profile?.content} />
|
||||
</div>
|
||||
{/*<div className={styles.category}><Text id="app.special.popovers.user_profile.sub.connections" /></div>*/}
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ export function grabFiles(
|
|||
input.addEventListener("change", async (e) => {
|
||||
const files = (e.currentTarget as HTMLInputElement)?.files;
|
||||
if (!files) return;
|
||||
|
||||
for (const file of files) {
|
||||
if (file.size > maxFileSize) {
|
||||
return tooLarge();
|
||||
|
|
@ -184,6 +185,7 @@ export function FileUploader(props: Props) {
|
|||
id: "error",
|
||||
error: "FileTooLarge",
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
files.push(blob);
|
||||
|
|
@ -212,6 +214,7 @@ export function FileUploader(props: Props) {
|
|||
for (const item of dropped) {
|
||||
if (item.size > props.maxFileSize) {
|
||||
openScreen({ id: "error", error: "FileTooLarge" });
|
||||
continue;
|
||||
}
|
||||
|
||||
files.push(item);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import { Route, Switch, useHistory, useParams } from "react-router-dom";
|
||||
import { Presence, RelationshipStatus } from "revolt-api/types/Users";
|
||||
import { Message } from "revolt.js/dist/maps/Messages";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { Message, User } from "revolt.js";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import { useCallback, useContext, useEffect } from "preact/hooks";
|
||||
|
|
@ -84,7 +82,7 @@ function Notifier() {
|
|||
}
|
||||
|
||||
let body, icon;
|
||||
if (typeof msg.content === "string") {
|
||||
if (msg.content) {
|
||||
body = client.markdownToText(msg.content);
|
||||
|
||||
if (msg.masquerade?.avatar) {
|
||||
|
|
@ -92,22 +90,23 @@ function Notifier() {
|
|||
} else {
|
||||
icon = msg.author?.generateAvatarURL({ max_side: 256 });
|
||||
}
|
||||
} else {
|
||||
} else if (msg.system) {
|
||||
const users = client.users;
|
||||
switch (msg.content.type) {
|
||||
|
||||
switch (msg.system.type) {
|
||||
case "user_added":
|
||||
case "user_remove":
|
||||
{
|
||||
const user = users.get(msg.content.id);
|
||||
const user = users.get(msg.system.id);
|
||||
body = translate(
|
||||
`app.main.channel.system.${
|
||||
msg.content.type === "user_added"
|
||||
msg.system.type === "user_added"
|
||||
? "added_by"
|
||||
: "removed_by"
|
||||
}`,
|
||||
{
|
||||
user: user?.username,
|
||||
other_user: users.get(msg.content.by)
|
||||
other_user: users.get(msg.system.by)
|
||||
?.username,
|
||||
},
|
||||
);
|
||||
|
|
@ -121,9 +120,9 @@ function Notifier() {
|
|||
case "user_kicked":
|
||||
case "user_banned":
|
||||
{
|
||||
const user = users.get(msg.content.id);
|
||||
const user = users.get(msg.system.id);
|
||||
body = translate(
|
||||
`app.main.channel.system.${msg.content.type}`,
|
||||
`app.main.channel.system.${msg.system.type}`,
|
||||
{ user: user?.username },
|
||||
);
|
||||
icon = user?.generateAvatarURL({
|
||||
|
|
@ -133,12 +132,12 @@ function Notifier() {
|
|||
break;
|
||||
case "channel_renamed":
|
||||
{
|
||||
const user = users.get(msg.content.by);
|
||||
const user = users.get(msg.system.by);
|
||||
body = translate(
|
||||
`app.main.channel.system.channel_renamed`,
|
||||
{
|
||||
user: users.get(msg.content.by)?.username,
|
||||
name: msg.content.name,
|
||||
user: users.get(msg.system.by)?.username,
|
||||
name: msg.system.name,
|
||||
},
|
||||
);
|
||||
icon = user?.generateAvatarURL({
|
||||
|
|
@ -149,10 +148,10 @@ function Notifier() {
|
|||
case "channel_description_changed":
|
||||
case "channel_icon_changed":
|
||||
{
|
||||
const user = users.get(msg.content.by);
|
||||
const user = users.get(msg.system.by);
|
||||
body = translate(
|
||||
`app.main.channel.system.${msg.content.type}`,
|
||||
{ user: users.get(msg.content.by)?.username },
|
||||
`app.main.channel.system.${msg.system.type}`,
|
||||
{ user: users.get(msg.system.by)?.username },
|
||||
);
|
||||
icon = user?.generateAvatarURL({
|
||||
max_side: 256,
|
||||
|
|
@ -210,17 +209,17 @@ function Notifier() {
|
|||
|
||||
const relationship = useCallback(
|
||||
async (user: User) => {
|
||||
if (client.user?.status?.presence === Presence.Busy) return;
|
||||
if (client.user?.status?.presence === "Busy") return;
|
||||
if (!showNotification) return;
|
||||
|
||||
let event;
|
||||
switch (user.relationship) {
|
||||
case RelationshipStatus.Incoming:
|
||||
case "Incoming":
|
||||
event = translate("notifications.sent_request", {
|
||||
person: user.username,
|
||||
});
|
||||
break;
|
||||
case RelationshipStatus.Friend:
|
||||
case "Friend":
|
||||
event = translate("notifications.now_friends", {
|
||||
person: user.username,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export default observer(({ children }: Props) => {
|
|||
|
||||
useEffect(() => {
|
||||
if (navigator.onLine) {
|
||||
state.config.createClient().req("GET", "/").then(state.config.set);
|
||||
state.config.createClient().api.get("/").then(state.config.set);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ export default observer(({ children }: Props) => {
|
|||
}
|
||||
}, [state.auth.getSession()]);
|
||||
|
||||
useEffect(() => registerEvents(state.auth, setStatus, client), [client]);
|
||||
useEffect(() => registerEvents(state, setStatus, client), [client]);
|
||||
|
||||
if (!loaded || status === ClientStatus.LOADING) {
|
||||
return <Preloader type="spinner" />;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* This file monitors the message cache to delete any queued messages that have already sent.
|
||||
*/
|
||||
import { Message } from "revolt.js/dist/maps/Messages";
|
||||
import { Message } from "revolt.js";
|
||||
|
||||
import { useContext, useEffect } from "preact/hooks";
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
import { useEffect } from "preact/hooks";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
import { Client } from "revolt.js/dist";
|
||||
import { Client, Server } from "revolt.js";
|
||||
|
||||
import { StateUpdater } from "preact/hooks";
|
||||
|
||||
import Auth from "../../mobx/stores/Auth";
|
||||
import { deleteRenderer } from "../../lib/renderer/Singleton";
|
||||
|
||||
import State from "../../mobx/State";
|
||||
|
||||
import { resetMemberSidebarFetched } from "../../components/navigation/right/MemberSidebar";
|
||||
import { ClientStatus } from "./RevoltClient";
|
||||
|
||||
export function registerEvents(
|
||||
auth: Auth,
|
||||
state: State,
|
||||
setStatus: StateUpdater<ClientStatus>,
|
||||
client: Client,
|
||||
) {
|
||||
|
|
@ -25,9 +27,22 @@ export function registerEvents(
|
|||
},
|
||||
|
||||
logout: () => {
|
||||
auth.logout();
|
||||
state.auth.logout();
|
||||
state.reset();
|
||||
setStatus(ClientStatus.READY);
|
||||
},
|
||||
|
||||
"channel/delete": (channel_id: string) => {
|
||||
deleteRenderer(channel_id);
|
||||
},
|
||||
|
||||
"server/delete": (_, server: Server) => {
|
||||
if (server) {
|
||||
for (const channel_id of server.channel_ids) {
|
||||
deleteRenderer(channel_id);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
import { Channel } from "revolt.js";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
|
|
@ -6,23 +6,27 @@ import { Children } from "../../types/Preact";
|
|||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function takeError(error: any): string {
|
||||
const type = error?.response?.data?.type;
|
||||
const id = type;
|
||||
if (!type) {
|
||||
if (
|
||||
error?.response?.status === 401 ||
|
||||
error?.response?.status === 403
|
||||
) {
|
||||
return "Unauthorized";
|
||||
} else if (error && !!error.isAxiosError && !error.response) {
|
||||
return "NetworkError";
|
||||
if (error.response) {
|
||||
const status = error.response.status;
|
||||
if (error.response.type) {
|
||||
return error.response.type;
|
||||
}
|
||||
|
||||
console.error(error);
|
||||
return "UnknownError";
|
||||
switch (status) {
|
||||
case 429:
|
||||
return "TooManyRequests";
|
||||
case 401:
|
||||
case 403:
|
||||
return "Unauthorized";
|
||||
default:
|
||||
return "UnknownError";
|
||||
}
|
||||
} else if (error.request) {
|
||||
return "NetworkError";
|
||||
}
|
||||
|
||||
return id;
|
||||
console.error(error);
|
||||
return "UnknownError";
|
||||
}
|
||||
|
||||
export function getChannelName(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ type NativeConfig = {
|
|||
frame: boolean;
|
||||
build: Build;
|
||||
discordRPC: boolean;
|
||||
minimiseToTray: boolean;
|
||||
hardwareAcceleration: boolean;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -11,18 +11,10 @@ import {
|
|||
Trash,
|
||||
} from "@styled-icons/boxicons-regular";
|
||||
import { Cog, UserVoice } from "@styled-icons/boxicons-solid";
|
||||
import { isFirefox } from "react-device-detect";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { Attachment } from "revolt-api/types/Autumn";
|
||||
import { Presence, RelationshipStatus } from "revolt-api/types/Users";
|
||||
import {
|
||||
ChannelPermission,
|
||||
ServerPermission,
|
||||
UserPermission,
|
||||
} from "revolt.js/dist/api/permissions";
|
||||
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 { User } from "revolt.js/dist/maps/Users";
|
||||
import { Channel, Message, Server, User, API } from "revolt.js";
|
||||
import { Permission, UserPermission } from "revolt.js";
|
||||
|
||||
import {
|
||||
ContextMenuWithData,
|
||||
|
|
@ -60,7 +52,7 @@ interface ContextMenuData {
|
|||
server_list?: string;
|
||||
channel?: string;
|
||||
message?: Message;
|
||||
attachment?: Attachment;
|
||||
attachment?: API.File;
|
||||
|
||||
unread?: boolean;
|
||||
queued?: QueuedMessage;
|
||||
|
|
@ -82,9 +74,9 @@ type Action =
|
|||
| { action: "quote_message"; content: string }
|
||||
| { action: "edit_message"; id: string }
|
||||
| { action: "delete_message"; target: Message }
|
||||
| { action: "open_file"; attachment: Attachment }
|
||||
| { action: "save_file"; attachment: Attachment }
|
||||
| { action: "copy_file_link"; attachment: Attachment }
|
||||
| { action: "open_file"; attachment: API.File }
|
||||
| { action: "save_file"; attachment: API.File }
|
||||
| { action: "copy_file_link"; attachment: API.File }
|
||||
| { action: "open_link"; link: string }
|
||||
| { action: "copy_link"; link: string }
|
||||
| { action: "remove_member"; channel: Channel; user: User }
|
||||
|
|
@ -97,7 +89,7 @@ type Action =
|
|||
| { action: "add_friend"; user: User }
|
||||
| { action: "remove_friend"; user: User }
|
||||
| { action: "cancel_friend"; user: User }
|
||||
| { action: "set_presence"; presence: Presence }
|
||||
| { action: "set_presence"; presence: API.Presence }
|
||||
| { action: "set_status" }
|
||||
| { action: "clear_status" }
|
||||
| { action: "create_channel"; target: Server }
|
||||
|
|
@ -295,7 +287,7 @@ export default function ContextMenus() {
|
|||
"attachments",
|
||||
"attachments/download",
|
||||
),
|
||||
"_blank",
|
||||
isFirefox || window.native ? "_blank" : "_self",
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
|
@ -505,9 +497,8 @@ export default function ContextMenus() {
|
|||
|
||||
if (server_list) {
|
||||
const server = client.servers.get(server_list)!;
|
||||
const permissions = server.permission;
|
||||
if (server) {
|
||||
if (permissions & ServerPermission.ManageChannels) {
|
||||
if (server.havePermission("ManageChannel")) {
|
||||
generateAction({
|
||||
action: "create_category",
|
||||
target: server,
|
||||
|
|
@ -517,7 +508,8 @@ export default function ContextMenus() {
|
|||
target: server,
|
||||
});
|
||||
}
|
||||
if (permissions & ServerPermission.ManageServer)
|
||||
|
||||
if (server.havePermission("ManageServer"))
|
||||
generateAction({
|
||||
action: "open_server_settings",
|
||||
id: server_list,
|
||||
|
|
@ -589,64 +581,70 @@ export default function ContextMenus() {
|
|||
}
|
||||
|
||||
if (user) {
|
||||
if (!user.bot) {
|
||||
let actions: Action["action"][];
|
||||
switch (user.relationship) {
|
||||
case RelationshipStatus.User:
|
||||
actions = [];
|
||||
break;
|
||||
case RelationshipStatus.Friend:
|
||||
actions = ["remove_friend", "block_user"];
|
||||
break;
|
||||
case RelationshipStatus.Incoming:
|
||||
let actions: (Action["action"] | boolean)[];
|
||||
switch (user.relationship) {
|
||||
case "User":
|
||||
actions = [];
|
||||
break;
|
||||
case "Friend":
|
||||
actions = [
|
||||
!user.bot && "remove_friend",
|
||||
"block_user",
|
||||
];
|
||||
break;
|
||||
case "Incoming":
|
||||
actions = [
|
||||
"add_friend",
|
||||
"cancel_friend",
|
||||
"block_user",
|
||||
];
|
||||
break;
|
||||
case "Outgoing":
|
||||
actions = [
|
||||
!user.bot && "cancel_friend",
|
||||
"block_user",
|
||||
];
|
||||
break;
|
||||
case "Blocked":
|
||||
actions = ["unblock_user"];
|
||||
break;
|
||||
case "BlockedOther":
|
||||
actions = ["block_user"];
|
||||
break;
|
||||
case "None":
|
||||
default:
|
||||
if ((user.flags && 2) || (user.flags && 4)) {
|
||||
actions = ["block_user"];
|
||||
} else {
|
||||
actions = [
|
||||
"add_friend",
|
||||
"cancel_friend",
|
||||
!user.bot && "add_friend",
|
||||
"block_user",
|
||||
];
|
||||
break;
|
||||
case RelationshipStatus.Outgoing:
|
||||
actions = ["cancel_friend", "block_user"];
|
||||
break;
|
||||
case RelationshipStatus.Blocked:
|
||||
actions = ["unblock_user"];
|
||||
break;
|
||||
case RelationshipStatus.BlockedOther:
|
||||
actions = ["block_user"];
|
||||
break;
|
||||
case RelationshipStatus.None:
|
||||
default:
|
||||
if (
|
||||
(user.flags && 2) ||
|
||||
(user.flags && 4)
|
||||
) {
|
||||
actions = ["block_user"];
|
||||
} else {
|
||||
actions = ["add_friend", "block_user"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (userPermissions & UserPermission.ViewProfile) {
|
||||
generateAction({
|
||||
action: "view_profile",
|
||||
user,
|
||||
});
|
||||
}
|
||||
if (userPermissions & UserPermission.ViewProfile) {
|
||||
generateAction({
|
||||
action: "view_profile",
|
||||
user,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
user._id !== userId &&
|
||||
userPermissions & UserPermission.SendMessage
|
||||
) {
|
||||
generateAction({
|
||||
action: "message_user",
|
||||
user,
|
||||
});
|
||||
}
|
||||
if (
|
||||
user._id !== userId &&
|
||||
userPermissions & UserPermission.SendMessage
|
||||
) {
|
||||
generateAction({
|
||||
action: "message_user",
|
||||
user,
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < actions.length; i++) {
|
||||
// Typescript can't determine that user the actions are linked together correctly
|
||||
for (let i = 0; i < actions.length; i++) {
|
||||
let action = actions[i];
|
||||
if (action) {
|
||||
generateAction({
|
||||
action: actions[i],
|
||||
action,
|
||||
user,
|
||||
} as unknown as Action);
|
||||
}
|
||||
|
|
@ -673,9 +671,7 @@ export default function ContextMenus() {
|
|||
userId !== uid &&
|
||||
uid !== server.owner
|
||||
) {
|
||||
if (
|
||||
serverPermissions & ServerPermission.KickMembers
|
||||
)
|
||||
if (serverPermissions & Permission.KickMembers)
|
||||
generateAction(
|
||||
{
|
||||
action: "kick_member",
|
||||
|
|
@ -688,7 +684,7 @@ export default function ContextMenus() {
|
|||
"var(--error)", // the only relevant part really
|
||||
);
|
||||
|
||||
if (serverPermissions & ServerPermission.BanMembers)
|
||||
if (serverPermissions & Permission.BanMembers)
|
||||
generateAction(
|
||||
{
|
||||
action: "ban_member",
|
||||
|
|
@ -718,8 +714,7 @@ export default function ContextMenus() {
|
|||
if (message && !queued) {
|
||||
const sendPermission =
|
||||
message.channel &&
|
||||
message.channel.permission &
|
||||
ChannelPermission.SendMessage;
|
||||
message.channel.permission & Permission.SendMessage;
|
||||
|
||||
if (sendPermission) {
|
||||
generateAction({
|
||||
|
|
@ -759,8 +754,7 @@ export default function ContextMenus() {
|
|||
|
||||
if (
|
||||
message.author_id === userId ||
|
||||
channelPermissions &
|
||||
ChannelPermission.ManageMessages
|
||||
channelPermissions & Permission.ManageMessages
|
||||
) {
|
||||
generateAction({
|
||||
action: "delete_message",
|
||||
|
|
@ -903,7 +897,7 @@ export default function ContextMenus() {
|
|||
case "VoiceChannel":
|
||||
if (
|
||||
channelPermissions &
|
||||
ChannelPermission.InviteOthers
|
||||
Permission.InviteOthers
|
||||
) {
|
||||
generateAction({
|
||||
action: "create_invite",
|
||||
|
|
@ -913,7 +907,7 @@ export default function ContextMenus() {
|
|||
|
||||
if (
|
||||
serverPermissions &
|
||||
ServerPermission.ManageServer
|
||||
Permission.ManageServer
|
||||
)
|
||||
generateAction(
|
||||
{
|
||||
|
|
@ -926,7 +920,7 @@ export default function ContextMenus() {
|
|||
|
||||
if (
|
||||
serverPermissions &
|
||||
ServerPermission.ManageChannels
|
||||
Permission.ManageChannel
|
||||
)
|
||||
generateAction({
|
||||
action: "delete_channel",
|
||||
|
|
@ -958,20 +952,15 @@ export default function ContextMenus() {
|
|||
);
|
||||
|
||||
if (
|
||||
serverPermissions &
|
||||
ServerPermission.ChangeNickname ||
|
||||
serverPermissions &
|
||||
ServerPermission.ChangeAvatar
|
||||
serverPermissions & Permission.ChangeNickname ||
|
||||
serverPermissions & Permission.ChangeAvatar
|
||||
)
|
||||
generateAction(
|
||||
{ action: "edit_identity", target: server },
|
||||
"edit_identity",
|
||||
);
|
||||
|
||||
if (
|
||||
serverPermissions &
|
||||
ServerPermission.ManageServer
|
||||
)
|
||||
if (serverPermissions & Permission.ManageServer)
|
||||
generateAction(
|
||||
{
|
||||
action: "open_server_settings",
|
||||
|
|
@ -1060,7 +1049,7 @@ export default function ContextMenus() {
|
|||
<MenuItem
|
||||
data={{
|
||||
action: "set_presence",
|
||||
presence: Presence.Online,
|
||||
presence: "Online",
|
||||
}}
|
||||
disabled={!isOnline}>
|
||||
<div className="indicator online" />
|
||||
|
|
@ -1069,7 +1058,7 @@ export default function ContextMenus() {
|
|||
<MenuItem
|
||||
data={{
|
||||
action: "set_presence",
|
||||
presence: Presence.Idle,
|
||||
presence: "Idle",
|
||||
}}
|
||||
disabled={!isOnline}>
|
||||
<div className="indicator idle" />
|
||||
|
|
@ -1078,7 +1067,7 @@ export default function ContextMenus() {
|
|||
<MenuItem
|
||||
data={{
|
||||
action: "set_presence",
|
||||
presence: Presence.Busy,
|
||||
presence: "Busy",
|
||||
}}
|
||||
disabled={!isOnline}>
|
||||
<div className="indicator busy" />
|
||||
|
|
@ -1087,7 +1076,7 @@ export default function ContextMenus() {
|
|||
<MenuItem
|
||||
data={{
|
||||
action: "set_presence",
|
||||
presence: Presence.Invisible,
|
||||
presence: "Invisible",
|
||||
}}
|
||||
disabled={!isOnline}>
|
||||
<div className="indicator invisible" />
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
import { Server } from "revolt.js";
|
||||
|
||||
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";
|
||||
import { Message } from "revolt.js";
|
||||
import { Nullable } from "revolt.js";
|
||||
|
||||
import { SimpleRenderer } from "./simple/SimpleRenderer";
|
||||
import { RendererRoutines, ScrollState } from "./types";
|
||||
|
|
@ -222,3 +222,7 @@ export function getRenderer(channel: Channel) {
|
|||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
export function deleteRenderer(channel_id: string) {
|
||||
delete renderers[channel_id];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Message } from "revolt.js/dist/maps/Messages";
|
||||
import { Message } from "revolt.js";
|
||||
|
||||
import { ChannelRenderer } from "./Singleton";
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import { types } from "mediasoup-client";
|
|||
|
||||
import { Device, Producer, Transport } from "mediasoup-client/lib/types";
|
||||
|
||||
import { useApplicationState } from "../../mobx/State";
|
||||
|
||||
import Signaling from "./Signaling";
|
||||
import {
|
||||
ProduceType,
|
||||
|
|
@ -58,6 +60,8 @@ export default class VoiceClient extends EventEmitter<VoiceEvents> {
|
|||
|
||||
this.isDeaf = false;
|
||||
|
||||
const state = useApplicationState();
|
||||
|
||||
this.signaling.on(
|
||||
"data",
|
||||
(json) => {
|
||||
|
|
@ -65,11 +69,13 @@ export default class VoiceClient extends EventEmitter<VoiceEvents> {
|
|||
switch (json.type) {
|
||||
case WSEventType.UserJoined: {
|
||||
this.participants.set(data.id, {});
|
||||
state.settings.sounds.playSound("call_join");
|
||||
this.emit("userJoined", data.id);
|
||||
break;
|
||||
}
|
||||
case WSEventType.UserLeft: {
|
||||
this.participants.delete(data.id);
|
||||
state.settings.sounds.playSound("call_leave");
|
||||
this.emit("userLeft", data.id);
|
||||
|
||||
if (this.recvTransport) this.stopConsume(data.id);
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
import { Nullable, toNullable } from "revolt.js";
|
||||
|
||||
import type { ProduceType, VoiceUser } from "./Types";
|
||||
import type VoiceClient from "./VoiceClient";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// @ts-expect-error No typings.
|
||||
import stringify from "json-stringify-deterministic";
|
||||
import localforage from "localforage";
|
||||
import { makeAutoObservable, reaction } from "mobx";
|
||||
import { makeAutoObservable, reaction, runInAction } from "mobx";
|
||||
import { Client } from "revolt.js";
|
||||
|
||||
import { reportError } from "../lib/ErrorBoundary";
|
||||
|
|
@ -184,10 +184,12 @@ export default class State {
|
|||
}
|
||||
|
||||
if (Object.keys(obj).length > 0) {
|
||||
client.syncSetSettings(
|
||||
obj as any,
|
||||
revision,
|
||||
);
|
||||
if (client.websocket.connected) {
|
||||
client.syncSetSettings(
|
||||
obj as any,
|
||||
revision,
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -198,12 +200,14 @@ export default class State {
|
|||
}
|
||||
|
||||
this.sync.setRevision(id, revision);
|
||||
client.syncSetSettings(
|
||||
(
|
||||
store as unknown as Syncable
|
||||
).toSyncable(),
|
||||
revision,
|
||||
);
|
||||
if (client.websocket.connected) {
|
||||
client.syncSetSettings(
|
||||
(
|
||||
store as unknown as Syncable
|
||||
).toSyncable(),
|
||||
revision,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -263,6 +267,26 @@ export default class State {
|
|||
// Post-hydration, init plugins.
|
||||
this.plugins.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset known state values.
|
||||
*/
|
||||
reset() {
|
||||
runInAction(() => {
|
||||
this.draft = new Draft();
|
||||
this.experiments = new Experiments();
|
||||
this.layout = new Layout();
|
||||
this.notifications = new NotificationOptions();
|
||||
this.queue = new MessageQueue();
|
||||
this.settings = new Settings();
|
||||
this.sync = new Sync(this);
|
||||
|
||||
this.save();
|
||||
|
||||
this.persistent = [];
|
||||
this.register();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var state: State;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue