mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-22 15:10:57 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
4541a34cef
138 changed files with 10981 additions and 6400 deletions
|
@ -5,3 +5,6 @@ dist_injected
|
||||||
node_modules
|
node_modules
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
|
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
|
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
|
@ -1,2 +0,0 @@
|
||||||
ko_fi: insertish
|
|
||||||
custom: https://insrt.uk/donate
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: Bug report
|
name: Bug report
|
||||||
description: File a bug report
|
description: File a bug report
|
||||||
title: "[Bug Report]"
|
title: "bug: "
|
||||||
labels: ["bug"]
|
labels: ["bug"]
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
|
@ -52,7 +52,7 @@ body:
|
||||||
id: desktop
|
id: desktop
|
||||||
attributes:
|
attributes:
|
||||||
label: Desktop
|
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:
|
options:
|
||||||
- label: Yes, this bug is specific to Revolt Desktop and is *not* an issue with Revolt Desktop itself.
|
- label: Yes, this bug is specific to Revolt Desktop and is *not* an issue with Revolt Desktop itself.
|
||||||
required: false
|
required: false
|
|
@ -1,6 +1,6 @@
|
||||||
name: Feature request
|
name: Feature request
|
||||||
description: Make a feature request
|
description: Make a feature request
|
||||||
title: "[Feature Request]"
|
title: "feature request: "
|
||||||
labels: ["enhancement"]
|
labels: ["enhancement"]
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
24
.github/SECURITY.md
vendored
24
.github/SECURITY.md
vendored
|
@ -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.
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -7,6 +7,9 @@ dist-ssr
|
||||||
*.log
|
*.log
|
||||||
/.idea
|
/.idea
|
||||||
|
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/install-state.gz
|
||||||
|
|
||||||
public/assets
|
public/assets
|
||||||
public/assets_*
|
public/assets_*
|
||||||
!public/assets_default
|
!public/assets_default
|
||||||
|
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true
|
||||||
"compile-hero.disable-compile-files-on-did-save-code": true
|
|
||||||
}
|
}
|
||||||
|
|
546
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
Normal file
546
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
9
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
Normal file
9
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
28
.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
vendored
Normal file
28
.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
785
.yarn/releases/yarn-3.2.0.cjs
vendored
Executable file
785
.yarn/releases/yarn-3.2.0.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
11
.yarnrc.yml
Normal file
11
.yarnrc.yml
Normal file
|
@ -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
|
FROM node:16-buster AS builder
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
COPY package*.json ./
|
|
||||||
|
|
||||||
RUN yarn --no-cache
|
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
COPY .env.build .env
|
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
|
WORKDIR /usr/src/app
|
||||||
COPY --from=builder /usr/src/app .
|
COPY --from=builder /usr/src/app .
|
||||||
|
|
||||||
|
|
2
external/lang
vendored
2
external/lang
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 6a72c5c952eedfbeb8a193a8a4b97927cc44cd6f
|
Subproject commit bac88cffd196a2afacf7d726e4f7ef19bd6bd94c
|
27
package.json
27
package.json
|
@ -4,6 +4,7 @@
|
||||||
"dev": "node scripts/setup_assets.js --check && vite",
|
"dev": "node scripts/setup_assets.js --check && vite",
|
||||||
"pull": "node scripts/setup_assets.js",
|
"pull": "node scripts/setup_assets.js",
|
||||||
"build": "rimraf build && node scripts/setup_assets.js --check && vite build",
|
"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",
|
"preview": "vite preview",
|
||||||
"lint": "eslint src/**/*.{js,jsx,ts,tsx}",
|
"lint": "eslint src/**/*.{js,jsx,ts,tsx}",
|
||||||
"fmt": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'",
|
"fmt": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'",
|
||||||
|
@ -61,7 +62,6 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/bitter": "^4.5.0",
|
"@fontsource/bitter": "^4.5.0",
|
||||||
"@insertish/vite-plugin-babel-macros": "^1.0.5",
|
"@insertish/vite-plugin-babel-macros": "^1.0.5",
|
||||||
"color-rgba": "^2.3.0",
|
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"klaw": "^3.0.0",
|
"klaw": "^3.0.0",
|
||||||
"react-beautiful-dnd": "^13.1.0",
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
|
@ -69,7 +69,7 @@
|
||||||
"vite": "^2.6.14"
|
"vite": "^2.6.14"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-proposal-decorators": "^7.16.7",
|
"@babel/plugin-proposal-decorators": "^7.17.9",
|
||||||
"@fontsource/atkinson-hyperlegible": "^4.4.5",
|
"@fontsource/atkinson-hyperlegible": "^4.4.5",
|
||||||
"@fontsource/comic-neue": "^4.4.5",
|
"@fontsource/comic-neue": "^4.4.5",
|
||||||
"@fontsource/fira-code": "^4.4.5",
|
"@fontsource/fira-code": "^4.4.5",
|
||||||
|
@ -91,16 +91,16 @@
|
||||||
"@fontsource/ubuntu-mono": "^4.4.5",
|
"@fontsource/ubuntu-mono": "^4.4.5",
|
||||||
"@hcaptcha/react-hcaptcha": "^0.3.6",
|
"@hcaptcha/react-hcaptcha": "^0.3.6",
|
||||||
"@preact/preset-vite": "^2.0.0",
|
"@preact/preset-vite": "^2.0.0",
|
||||||
|
"@revoltchat/ui": "1.0.31",
|
||||||
"@rollup/plugin-replace": "^2.4.2",
|
"@rollup/plugin-replace": "^2.4.2",
|
||||||
"@styled-icons/boxicons-logos": "^10.34.0",
|
"@styled-icons/boxicons-logos": "^10.38.0",
|
||||||
"@styled-icons/boxicons-regular": "^10.34.0",
|
"@styled-icons/boxicons-regular": "^10.38.0",
|
||||||
"@styled-icons/boxicons-solid": "^10.37.0",
|
"@styled-icons/boxicons-solid": "^10.38.0",
|
||||||
"@styled-icons/simple-icons": "^10.33.0",
|
"@styled-icons/simple-icons": "^10.33.0",
|
||||||
"@tippyjs/react": "^4.2.5",
|
"@tippyjs/react": "^4.2.5",
|
||||||
"@traptitech/markdown-it-katex": "^3.4.3",
|
"@traptitech/markdown-it-katex": "^3.4.3",
|
||||||
"@traptitech/markdown-it-spoiler": "^1.1.6",
|
"@traptitech/markdown-it-spoiler": "^1.1.6",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^2.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^2.0.2",
|
||||||
"@types/color-rgba": "^2.1.0",
|
|
||||||
"@types/lodash.defaultsdeep": "^4.6.6",
|
"@types/lodash.defaultsdeep": "^4.6.6",
|
||||||
"@types/lodash.isequal": "^4.5.5",
|
"@types/lodash.isequal": "^4.5.5",
|
||||||
"@types/markdown-it": "^12.0.2",
|
"@types/markdown-it": "^12.0.2",
|
||||||
|
@ -111,13 +111,13 @@
|
||||||
"@types/react-helmet": "^6.1.1",
|
"@types/react-helmet": "^6.1.1",
|
||||||
"@types/react-router-dom": "^5.1.7",
|
"@types/react-router-dom": "^5.1.7",
|
||||||
"@types/react-scroll": "^1.8.2",
|
"@types/react-scroll": "^1.8.2",
|
||||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
|
||||||
"@types/styled-components": "^5.1.10",
|
"@types/styled-components": "^5.1.10",
|
||||||
"@types/twemoji": "^12.1.1",
|
"@types/twemoji": "^12.1.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.27.0",
|
"@typescript-eslint/eslint-plugin": "^4.27.0",
|
||||||
"@typescript-eslint/parser": "^4.27.0",
|
"@typescript-eslint/parser": "^4.27.0",
|
||||||
"@vitejs/plugin-legacy": "^1.7.1",
|
"@vitejs/plugin-legacy": "^1.7.1",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
|
"color-rgba": "^2.4.0",
|
||||||
"dayjs": "^1.10.6",
|
"dayjs": "^1.10.6",
|
||||||
"detect-browser": "^5.2.0",
|
"detect-browser": "^5.2.0",
|
||||||
"eslint": "^7.28.0",
|
"eslint": "^7.28.0",
|
||||||
|
@ -127,13 +127,12 @@
|
||||||
"localforage": "^1.9.0",
|
"localforage": "^1.9.0",
|
||||||
"lodash.defaultsdeep": "^4.6.1",
|
"lodash.defaultsdeep": "^4.6.1",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
|
"long": "^5.2.0",
|
||||||
"markdown-it": "^12.0.6",
|
"markdown-it": "^12.0.6",
|
||||||
"markdown-it-emoji": "^2.0.0",
|
"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",
|
"mediasoup-client": "npm:@insertish/mediasoup-client@3.6.36-esnext",
|
||||||
"mobx": "^6.3.2",
|
"mobx": "^6.3.2",
|
||||||
"mobx-react-lite": "^3.2.0",
|
"mobx-react-lite": "^3.3.0",
|
||||||
"preact": "^10.5.14",
|
"preact": "^10.5.14",
|
||||||
"preact-context-menu": "0.4.0-patch.0",
|
"preact-context-menu": "0.4.0-patch.0",
|
||||||
"preact-i18n": "^2.4.0-preactx",
|
"preact-i18n": "^2.4.0-preactx",
|
||||||
|
@ -145,10 +144,8 @@
|
||||||
"react-overlapping-panels": "1.2.2",
|
"react-overlapping-panels": "1.2.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scroll": "^1.8.2",
|
"react-scroll": "^1.8.2",
|
||||||
"react-virtualized-auto-sizer": "^1.0.5",
|
|
||||||
"react-virtuoso": "^1.10.4",
|
"react-virtuoso": "^1.10.4",
|
||||||
"revolt-api": "^0.5.3-alpha.12",
|
"revolt.js": "6.0.0-2",
|
||||||
"revolt.js": "^5.2.8",
|
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"sass": "^1.35.1",
|
"sass": "^1.35.1",
|
||||||
"shade-blend-color": "^1.0.0",
|
"shade-blend-color": "^1.0.0",
|
||||||
|
@ -160,10 +157,10 @@
|
||||||
"vite-plugin-pwa": "^0.11.13",
|
"vite-plugin-pwa": "^0.11.13",
|
||||||
"workbox-precaching": "^6.1.5"
|
"workbox-precaching": "^6.1.5"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@1.22.17",
|
|
||||||
"name": "client",
|
"name": "client",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": "https://github.com/revoltchat/revite.git",
|
"repository": "https://github.com/revoltchat/revite.git",
|
||||||
"author": "Paul <paulmakles@gmail.com>",
|
"author": "Paul <paulmakles@gmail.com>",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"packageManager": "yarn@3.2.0"
|
||||||
}
|
}
|
1
public/assets_default/badges/amog.svg
Normal file
1
public/assets_default/badges/amog.svg
Normal file
|
@ -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
public/assets_default/badges/founder.svg
Normal file
1
public/assets_default/badges/founder.svg
Normal file
|
@ -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
public/assets_default/badges/moderation.svg
Normal file
1
public/assets_default/badges/moderation.svg
Normal file
|
@ -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
public/assets_default/badges/paw.svg
Normal file
1
public/assets_default/badges/paw.svg
Normal file
|
@ -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
public/assets_default/badges/raccoon.svg
Normal file
1
public/assets_default/badges/raccoon.svg
Normal file
|
@ -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
public/assets_default/badges/supporter.svg
Normal file
1
public/assets_default/badges/supporter.svg
Normal file
|
@ -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 { observer } from "mobx-react-lite";
|
||||||
import { useHistory } from "react-router-dom";
|
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 styled from "styled-components/macro";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
import { Channel, User } from "revolt.js";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
|
||||||
import styled, { css } from "styled-components/macro";
|
import styled, { css } from "styled-components/macro";
|
||||||
|
|
||||||
import { StateUpdater, useState } from "preact/hooks";
|
import { StateUpdater, useState } from "preact/hooks";
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import { Hash, VolumeFull } from "@styled-icons/boxicons-regular";
|
import { Hash, VolumeFull } from "@styled-icons/boxicons-regular";
|
||||||
import { observer } from "mobx-react-lite";
|
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 { useContext } from "preact/hooks";
|
||||||
|
|
||||||
import { AppContext } from "../../context/revoltjs/RevoltClient";
|
import { AppContext } from "../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
import { ImageIconBase, IconBaseProps } from "./IconBase";
|
|
||||||
import fallback from "./assets/group.png";
|
import fallback from "./assets/group.png";
|
||||||
|
|
||||||
|
import { ImageIconBase, IconBaseProps } from "./IconBase";
|
||||||
|
|
||||||
interface Props extends IconBaseProps<Channel> {
|
interface Props extends IconBaseProps<Channel> {
|
||||||
isServerChannel?: boolean;
|
isServerChannel?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +33,7 @@ export default observer(
|
||||||
...imgProps
|
...imgProps
|
||||||
} = props;
|
} = props;
|
||||||
const iconURL = client.generateFileURL(
|
const iconURL = client.generateFileURL(
|
||||||
target?.icon ?? attachment,
|
target?.icon ?? attachment ?? undefined,
|
||||||
{ max_side: 256 },
|
{ max_side: 256 },
|
||||||
animate,
|
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 styled, { css } from "styled-components/macro";
|
||||||
|
|
||||||
import { Ref } from "preact";
|
import { Ref } from "preact";
|
||||||
|
@ -6,7 +7,7 @@ import { Ref } from "preact";
|
||||||
export interface IconBaseProps<T> {
|
export interface IconBaseProps<T> {
|
||||||
target?: T;
|
target?: T;
|
||||||
url?: string;
|
url?: string;
|
||||||
attachment?: Attachment;
|
attachment?: Nullable<API.File>;
|
||||||
|
|
||||||
size: number;
|
size: number;
|
||||||
hover?: boolean;
|
hover?: boolean;
|
||||||
|
|
|
@ -2,14 +2,11 @@ import { Check } from "@styled-icons/boxicons-regular";
|
||||||
import { Cog } from "@styled-icons/boxicons-solid";
|
import { Cog } from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { ServerPermission } from "revolt.js/dist/api/permissions";
|
import { Server } from "revolt.js";
|
||||||
import { Server } from "revolt.js/dist/maps/Servers";
|
|
||||||
import styled, { css } from "styled-components/macro";
|
import styled, { css } from "styled-components/macro";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
|
||||||
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
|
||||||
|
|
||||||
import IconButton from "../ui/IconButton";
|
import IconButton from "../ui/IconButton";
|
||||||
|
|
||||||
import Tooltip from "./Tooltip";
|
import Tooltip from "./Tooltip";
|
||||||
|
@ -125,7 +122,7 @@ export default observer(({ server }: Props) => {
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
<div className="title">{server.name}</div>
|
<div className="title">{server.name}</div>
|
||||||
{(server.permission & ServerPermission.ManageServer) > 0 && (
|
{server.havePermission("ManageServer") && (
|
||||||
<Link to={`/server/${server._id}/settings`}>
|
<Link to={`/server/${server._id}/settings`}>
|
||||||
<IconButton>
|
<IconButton>
|
||||||
<Cog size={20} />
|
<Cog size={20} />
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { observer } from "mobx-react-lite";
|
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 styled from "styled-components/macro";
|
||||||
|
|
||||||
import { useContext } from "preact/hooks";
|
import { useContext } from "preact/hooks";
|
||||||
|
@ -39,7 +39,7 @@ export default observer(
|
||||||
const { target, attachment, size, animate, server_name, ...imgProps } =
|
const { target, attachment, size, animate, server_name, ...imgProps } =
|
||||||
props;
|
props;
|
||||||
const iconURL = client.generateFileURL(
|
const iconURL = client.generateFileURL(
|
||||||
target?.icon ?? attachment,
|
target?.icon ?? attachment ?? undefined,
|
||||||
{ max_side: 256 },
|
{ max_side: 256 },
|
||||||
animate,
|
animate,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { observer } from "mobx-react-lite";
|
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 { useTriggerEvents } from "preact-context-menu";
|
||||||
import { memo } from "preact/compat";
|
import { memo } from "preact/compat";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { observer } from "mobx-react-lite";
|
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 styled, { css, keyframes } from "styled-components/macro";
|
||||||
import { decodeTime } from "ulid";
|
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 Axios, { CancelTokenSource } from "axios";
|
||||||
|
import Long from "long";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { ChannelPermission } from "revolt.js/dist/api/permissions";
|
import {
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
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 styled, { css } from "styled-components/macro";
|
||||||
import { ulid } from "ulid";
|
import { ulid } from "ulid";
|
||||||
|
|
||||||
|
@ -125,6 +133,11 @@ const FileAction = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ThisCodeWillBeReplacedAnywaysSoIMightAsWellJustDoItThisWay__Padding = styled.div`
|
||||||
|
width: 16px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// For sed replacement
|
// For sed replacement
|
||||||
|
@ -150,7 +163,7 @@ export default observer(({ channel }: Props) => {
|
||||||
|
|
||||||
const renderer = getRenderer(channel);
|
const renderer = getRenderer(channel);
|
||||||
|
|
||||||
if (!(channel.permission & ChannelPermission.SendMessage)) {
|
if (!channel.havePermission("SendMessage")) {
|
||||||
return (
|
return (
|
||||||
<Base>
|
<Base>
|
||||||
<Blocked>
|
<Blocked>
|
||||||
|
@ -231,7 +244,7 @@ export default observer(({ channel }: Props) => {
|
||||||
);
|
);
|
||||||
renderer.messages.reverse();
|
renderer.messages.reverse();
|
||||||
|
|
||||||
if (msg) {
|
if (msg?.content) {
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
let [_, toReplace, newText, flags] = content.split(/\//);
|
let [_, toReplace, newText, flags] = content.split(/\//);
|
||||||
|
|
||||||
|
@ -493,7 +506,7 @@ export default observer(({ channel }: Props) => {
|
||||||
setReplies={setReplies}
|
setReplies={setReplies}
|
||||||
/>
|
/>
|
||||||
<Base>
|
<Base>
|
||||||
{channel.permission & ChannelPermission.UploadFiles ? (
|
{channel.havePermission("UploadFiles") ? (
|
||||||
<FileAction>
|
<FileAction>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
size={24}
|
size={24}
|
||||||
|
@ -530,7 +543,9 @@ export default observer(({ channel }: Props) => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FileAction>
|
</FileAction>
|
||||||
) : undefined}
|
) : (
|
||||||
|
<ThisCodeWillBeReplacedAnywaysSoIMightAsWellJustDoItThisWay__Padding />
|
||||||
|
)}
|
||||||
<TextAreaAutoSize
|
<TextAreaAutoSize
|
||||||
autoFocus
|
autoFocus
|
||||||
hideBorder
|
hideBorder
|
||||||
|
|
|
@ -11,8 +11,7 @@ import {
|
||||||
MessageSquareEdit,
|
MessageSquareEdit,
|
||||||
} from "@styled-icons/boxicons-solid";
|
} from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { SystemMessage as SystemMessageI } from "revolt-api/types/Channels";
|
import { Message, API } from "revolt.js";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
|
||||||
import styled from "styled-components/macro";
|
import styled from "styled-components/macro";
|
||||||
|
|
||||||
import { useTriggerEvents } from "preact-context-menu";
|
import { useTriggerEvents } from "preact-context-menu";
|
||||||
|
@ -75,13 +74,11 @@ export const SystemMessage = observer(
|
||||||
({ attachContext, message, highlight, hideInfo }: Props) => {
|
({ attachContext, message, highlight, hideInfo }: Props) => {
|
||||||
const data = message.asSystemMessage;
|
const data = message.asSystemMessage;
|
||||||
const SystemMessageIcon =
|
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) {
|
switch (data.type) {
|
||||||
case "text":
|
|
||||||
children = <span>{data.content}</span>;
|
|
||||||
break;
|
|
||||||
case "user_added":
|
case "user_added":
|
||||||
case "user_remove":
|
case "user_remove":
|
||||||
children = (
|
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 styles from "./Attachment.module.scss";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
@ -14,7 +14,7 @@ import Spoiler from "./Spoiler";
|
||||||
import TextFile from "./TextFile";
|
import TextFile from "./TextFile";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
attachment: AttachmentI;
|
attachment: API.File;
|
||||||
hasContent?: boolean;
|
hasContent?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,8 @@ import {
|
||||||
Download,
|
Download,
|
||||||
} from "@styled-icons/boxicons-regular";
|
} from "@styled-icons/boxicons-regular";
|
||||||
import { File, Video } from "@styled-icons/boxicons-solid";
|
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 styles from "./AttachmentActions.module.scss";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
@ -17,7 +18,7 @@ import { AppContext } from "../../../../context/revoltjs/RevoltClient";
|
||||||
import IconButton from "../../../ui/IconButton";
|
import IconButton from "../../../ui/IconButton";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
attachment: Attachment;
|
attachment: API.File;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AttachmentActions({ attachment }: Props) {
|
export default function AttachmentActions({ attachment }: Props) {
|
||||||
|
@ -51,7 +52,7 @@ export default function AttachmentActions({ attachment }: Props) {
|
||||||
href={download_url}
|
href={download_url}
|
||||||
className={styles.downloadIcon}
|
className={styles.downloadIcon}
|
||||||
download
|
download
|
||||||
target="_blank"
|
target={isFirefox || window.native ? "_blank" : "_self"}
|
||||||
rel="noreferrer">
|
rel="noreferrer">
|
||||||
<IconButton>
|
<IconButton>
|
||||||
<Download size={24} />
|
<Download size={24} />
|
||||||
|
@ -69,7 +70,7 @@ export default function AttachmentActions({ attachment }: Props) {
|
||||||
href={download_url}
|
href={download_url}
|
||||||
className={styles.downloadIcon}
|
className={styles.downloadIcon}
|
||||||
download
|
download
|
||||||
target="_blank"
|
target={isFirefox || window.native ? "_blank" : "_self"}
|
||||||
rel="noreferrer">
|
rel="noreferrer">
|
||||||
<IconButton>
|
<IconButton>
|
||||||
<Download size={24} />
|
<Download size={24} />
|
||||||
|
@ -89,7 +90,7 @@ export default function AttachmentActions({ attachment }: Props) {
|
||||||
href={download_url}
|
href={download_url}
|
||||||
className={styles.downloadIcon}
|
className={styles.downloadIcon}
|
||||||
download
|
download
|
||||||
target="_blank"
|
target={isFirefox || window.native ? "_blank" : "_self"}
|
||||||
rel="noreferrer">
|
rel="noreferrer">
|
||||||
<IconButton>
|
<IconButton>
|
||||||
<Download size={24} />
|
<Download size={24} />
|
||||||
|
@ -118,7 +119,7 @@ export default function AttachmentActions({ attachment }: Props) {
|
||||||
href={download_url}
|
href={download_url}
|
||||||
className={styles.downloadIcon}
|
className={styles.downloadIcon}
|
||||||
download
|
download
|
||||||
target="_blank"
|
target={isFirefox || window.native ? "_blank" : "_self"}
|
||||||
rel="noreferrer">
|
rel="noreferrer">
|
||||||
<IconButton>
|
<IconButton>
|
||||||
<Download size={24} />
|
<Download size={24} />
|
||||||
|
|
|
@ -9,6 +9,7 @@ const Grid = styled.div<{ width: number; height: number }>`
|
||||||
--height: ${(props) => props.height}px;
|
--height: ${(props) => props.height}px;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
|
overflow: hidden;
|
||||||
aspect-ratio: ${(props) => props.width} / ${(props) => props.height};
|
aspect-ratio: ${(props) => props.width} / ${(props) => props.height};
|
||||||
|
|
||||||
max-width: min(var(--width), var(--attachment-max-width));
|
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 styles from "./Attachment.module.scss";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
@ -10,12 +10,12 @@ import { AppContext } from "../../../../context/revoltjs/RevoltClient";
|
||||||
enum ImageLoadingState {
|
enum ImageLoadingState {
|
||||||
Loading,
|
Loading,
|
||||||
Loaded,
|
Loaded,
|
||||||
Error
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = JSX.HTMLAttributes<HTMLImageElement> & {
|
type Props = JSX.HTMLAttributes<HTMLImageElement> & {
|
||||||
attachment: Attachment;
|
attachment: API.File;
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function ImageFile({ attachment, ...props }: Props) {
|
export default function ImageFile({ attachment, ...props }: Props) {
|
||||||
const [loading, setLoading] = useState(ImageLoadingState.Loading);
|
const [loading, setLoading] = useState(ImageLoadingState.Loading);
|
||||||
|
@ -23,25 +23,19 @@ export default function ImageFile({ attachment, ...props }: Props) {
|
||||||
const { openScreen } = useIntermediate();
|
const { openScreen } = useIntermediate();
|
||||||
const url = client.generateFileURL(attachment)!;
|
const url = client.generateFileURL(attachment)!;
|
||||||
|
|
||||||
return <img
|
return (
|
||||||
|
<img
|
||||||
{...props}
|
{...props}
|
||||||
src={url}
|
src={url}
|
||||||
alt={attachment.filename}
|
alt={attachment.filename}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
className={classNames(styles.image, {
|
className={classNames(styles.image, {
|
||||||
[styles.loading]: loading !== ImageLoadingState.Loaded
|
[styles.loading]: loading !== ImageLoadingState.Loaded,
|
||||||
})}
|
})}
|
||||||
onClick={() =>
|
onClick={() => openScreen({ id: "image_viewer", attachment })}
|
||||||
openScreen({ id: "image_viewer", attachment })
|
onMouseDown={(ev) => ev.button === 1 && window.open(url, "_blank")}
|
||||||
}
|
onLoad={() => setLoading(ImageLoadingState.Loaded)}
|
||||||
onMouseDown={(ev) =>
|
onError={() => setLoading(ImageLoadingState.Error)}
|
||||||
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 { File } from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { RelationshipStatus } from "revolt-api/types/Users";
|
import { Channel, Message, API } from "revolt.js";
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
|
||||||
import styled, { css } from "styled-components/macro";
|
import styled, { css } from "styled-components/macro";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
@ -174,7 +172,7 @@ export const MessageReply = observer(
|
||||||
<ReplyBase head={index === 0}>
|
<ReplyBase head={index === 0}>
|
||||||
{/*<Reply size={16} />*/}
|
{/*<Reply size={16} />*/}
|
||||||
|
|
||||||
{message.author?.relationship === RelationshipStatus.Blocked ? (
|
{message.author?.relationship === "Blocked" ? (
|
||||||
<Text id="app.main.channel.misc.blocked_user" />
|
<Text id="app.main.channel.misc.blocked_user" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
@ -225,9 +223,10 @@ export const MessageReply = observer(
|
||||||
)}
|
)}
|
||||||
<Markdown
|
<Markdown
|
||||||
disallowBigEmoji
|
disallowBigEmoji
|
||||||
content={(
|
content={message.content?.replace(
|
||||||
message.content as string
|
/\n/g,
|
||||||
).replace(/\n/g, " ")}
|
" ",
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { Attachment } from "revolt-api/types/Autumn";
|
import { API } from "revolt.js";
|
||||||
|
|
||||||
import styles from "./Attachment.module.scss";
|
import styles from "./Attachment.module.scss";
|
||||||
|
import { Text } from "preact-i18n";
|
||||||
import { useContext, useEffect, useState } from "preact/hooks";
|
import { useContext, useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
import RequiresOnline from "../../../../context/revoltjs/RequiresOnline";
|
import RequiresOnline from "../../../../context/revoltjs/RequiresOnline";
|
||||||
|
@ -11,14 +12,16 @@ import {
|
||||||
} from "../../../../context/revoltjs/RevoltClient";
|
} from "../../../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
import Preloader from "../../../ui/Preloader";
|
import Preloader from "../../../ui/Preloader";
|
||||||
|
import { Button } from "@revoltchat/ui";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
attachment: Attachment;
|
attachment: API.File;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileCache: { [key: string]: string } = {};
|
const fileCache: { [key: string]: string } = {};
|
||||||
|
|
||||||
export default function TextFile({ attachment }: Props) {
|
export default function TextFile({ attachment }: Props) {
|
||||||
|
const [gated, setGated] = useState(attachment.size > 100_000);
|
||||||
const [content, setContent] = useState<undefined | string>(undefined);
|
const [content, setContent] = useState<undefined | string>(undefined);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const status = useContext(StatusContext);
|
const status = useContext(StatusContext);
|
||||||
|
@ -29,13 +32,7 @@ export default function TextFile({ attachment }: Props) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof content !== "undefined") return;
|
if (typeof content !== "undefined") return;
|
||||||
if (loading) return;
|
if (loading) return;
|
||||||
|
if (gated) 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
|
@ -60,13 +57,17 @@ export default function TextFile({ attachment }: Props) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [content, loading, status, attachment._id, attachment.size, url]);
|
}, [content, loading, gated, status, attachment._id, attachment.size, url]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={styles.textContent}
|
className={styles.textContent}
|
||||||
data-loading={typeof content === "undefined"}>
|
data-loading={typeof content === "undefined"}>
|
||||||
{content ? (
|
{gated ? (
|
||||||
|
<Button palette="accent" onClick={() => setGated(false)}>
|
||||||
|
<Text id="app.main.channel.misc.load_file" />
|
||||||
|
</Button>
|
||||||
|
) : content ? (
|
||||||
<pre>
|
<pre>
|
||||||
<code>{content}</code>
|
<code>{content}</code>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { DownArrowAlt } from "@styled-icons/boxicons-regular";
|
import { DownArrowAlt } from "@styled-icons/boxicons-regular";
|
||||||
import { observer } from "mobx-react-lite";
|
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 styled, { css } from "styled-components/macro";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
|
|
@ -7,8 +7,8 @@ import {
|
||||||
Notification,
|
Notification,
|
||||||
} from "@styled-icons/boxicons-solid";
|
} from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { ChannelPermission } from "revolt.js";
|
import { Permission } from "revolt.js";
|
||||||
import { Message as MessageObject } from "revolt.js/dist/maps/Messages";
|
import { Message as MessageObject } from "revolt.js";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { openContextMenu } from "preact-context-menu";
|
import { openContextMenu } from "preact-context-menu";
|
||||||
|
@ -131,8 +131,7 @@ export const MessageOverlayBar = observer(({ message, queued }: Props) => {
|
||||||
)}
|
)}
|
||||||
{isAuthor ||
|
{isAuthor ||
|
||||||
(message.channel &&
|
(message.channel &&
|
||||||
message.channel.permission &
|
message.channel.havePermission("ManageMessages")) ? (
|
||||||
ChannelPermission.ManageMessages) ? (
|
|
||||||
<Tooltip content="Delete">
|
<Tooltip content="Delete">
|
||||||
<Entry
|
<Entry
|
||||||
onClick={(e) =>
|
onClick={(e) =>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { UpArrowAlt } from "@styled-icons/boxicons-regular";
|
import { UpArrowAlt } from "@styled-icons/boxicons-regular";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
import { Channel } from "revolt.js";
|
||||||
import { decodeTime } from "ulid";
|
import { decodeTime } from "ulid";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
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 { File, XCircle } from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
import { Channel, Message } from "revolt.js";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
|
||||||
import styled from "styled-components/macro";
|
import styled from "styled-components/macro";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
@ -188,9 +187,10 @@ export default observer(({ channel, replies, setReplies }: Props) => {
|
||||||
) : (
|
) : (
|
||||||
<Markdown
|
<Markdown
|
||||||
disallowBigEmoji
|
disallowBigEmoji
|
||||||
content={(
|
content={message.content?.replace(
|
||||||
message.content as string
|
/\n/g,
|
||||||
).replace(/\n/g, " ")}
|
" ",
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { RelationshipStatus } from "revolt-api/types/Users";
|
import { Channel } from "revolt.js";
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
|
||||||
import styled from "styled-components/macro";
|
import styled from "styled-components/macro";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
@ -65,7 +64,7 @@ export default observer(({ channel }: Props) => {
|
||||||
(x) =>
|
(x) =>
|
||||||
typeof x !== "undefined" &&
|
typeof x !== "undefined" &&
|
||||||
x._id !== x.client.user!._id &&
|
x._id !== x.client.user!._id &&
|
||||||
x.relationship !== RelationshipStatus.Blocked,
|
x.relationship !== "Blocked",
|
||||||
);
|
);
|
||||||
|
|
||||||
if (users.length > 0) {
|
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 styles from "./Embed.module.scss";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
@ -13,7 +13,7 @@ import Attachment from "../attachments/Attachment";
|
||||||
import EmbedMedia from "./EmbedMedia";
|
import EmbedMedia from "./EmbedMedia";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
embed: EmbedI;
|
embed: API.Embed;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_EMBED_WIDTH = 480;
|
const MAX_EMBED_WIDTH = 480;
|
||||||
|
@ -68,7 +68,8 @@ export default function Embed({ embed }: Props) {
|
||||||
mh = embed.video?.height ?? 720;
|
mh = embed.video?.height ?? 720;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "Twitch": {
|
case "Twitch":
|
||||||
|
case "Lightspeed": {
|
||||||
mw = 1280;
|
mw = 1280;
|
||||||
mh = 720;
|
mh = 720;
|
||||||
break;
|
break;
|
||||||
|
@ -89,6 +90,20 @@ export default function Embed({ embed }: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { width, height } = calculateSize(mw, mh);
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(styles.embed, styles.website)}
|
className={classNames(styles.embed, styles.website)}
|
||||||
|
@ -128,7 +143,7 @@ export default function Embed({ embed }: Props) {
|
||||||
<a
|
<a
|
||||||
onMouseDown={(ev) =>
|
onMouseDown={(ev) =>
|
||||||
(ev.button === 0 || ev.button === 1) &&
|
(ev.button === 0 || ev.button === 1) &&
|
||||||
openLink(embed.url)
|
openLink(embed.url!)
|
||||||
}
|
}
|
||||||
className={styles.title}>
|
className={styles.title}>
|
||||||
{embed.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:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
import { Group } from "@styled-icons/boxicons-solid";
|
import { Group } from "@styled-icons/boxicons-solid";
|
||||||
import { autorun, reaction } from "mobx";
|
import { reaction } from "mobx";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { RetrievedInvite } from "revolt-api/types/Invites";
|
import { Message, API } from "revolt.js";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
|
||||||
import styled, { css } from "styled-components/macro";
|
import styled, { css } from "styled-components/macro";
|
||||||
|
|
||||||
import { useContext, useEffect, useState } from "preact/hooks";
|
import { useContext, useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
import { defer } from "../../../../lib/defer";
|
|
||||||
import { isTouchscreenDevice } from "../../../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../../../lib/isTouchscreenDevice";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -85,9 +83,9 @@ export function EmbedInvite({ code }: Props) {
|
||||||
const [processing, setProcessing] = useState(false);
|
const [processing, setProcessing] = useState(false);
|
||||||
const [error, setError] = useState<string | undefined>(undefined);
|
const [error, setError] = useState<string | undefined>(undefined);
|
||||||
const [joinError, setJoinError] = useState<string | undefined>(undefined);
|
const [joinError, setJoinError] = useState<string | undefined>(undefined);
|
||||||
const [invite, setInvite] = useState<RetrievedInvite | undefined>(
|
const [invite, setInvite] = useState<
|
||||||
undefined,
|
(API.InviteResponse & { type: "Server" }) | undefined
|
||||||
);
|
>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
|
@ -96,7 +94,9 @@ export function EmbedInvite({ code }: Props) {
|
||||||
) {
|
) {
|
||||||
client
|
client
|
||||||
.fetchInvite(code)
|
.fetchInvite(code)
|
||||||
.then((data) => setInvite(data))
|
.then((data) =>
|
||||||
|
setInvite(data as API.InviteResponse & { type: "Server" }),
|
||||||
|
)
|
||||||
.catch((err) => setError(takeError(err)));
|
.catch((err) => setError(takeError(err)));
|
||||||
}
|
}
|
||||||
}, [client, code, invite, status]);
|
}, [client, code, invite, status]);
|
||||||
|
@ -139,42 +139,17 @@ export function EmbedInvite({ code }: Props) {
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
try {
|
|
||||||
setProcessing(true);
|
setProcessing(true);
|
||||||
|
|
||||||
if (invite.type === "Server") {
|
try {
|
||||||
if (client.servers.get(invite.server_id)) {
|
await client.joinInvite(invite);
|
||||||
|
|
||||||
history.push(
|
history.push(
|
||||||
`/server/${invite.server_id}/channel/${invite.channel_id}`,
|
`/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);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setJoinError(takeError(err));
|
setJoinError(takeError(err));
|
||||||
|
} finally {
|
||||||
setProcessing(false);
|
setProcessing(false);
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* eslint-disable react-hooks/rules-of-hooks */
|
/* 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";
|
import styles from "./Embed.module.scss";
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { useIntermediate } from "../../../../context/intermediate/Intermediate";
|
||||||
import { useClient } from "../../../../context/revoltjs/RevoltClient";
|
import { useClient } from "../../../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
embed: JanuaryEmbed;
|
embed: API.Embed;
|
||||||
width?: number;
|
width?: number;
|
||||||
height: number;
|
height: number;
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,17 @@ export default function EmbedMedia({ embed, width, height }: Props) {
|
||||||
style={{ height }}
|
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":
|
case "Spotify":
|
||||||
return (
|
return (
|
||||||
<iframe
|
<iframe
|
||||||
|
@ -83,7 +94,21 @@ export default function EmbedMedia({ embed, width, height }: Props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
default: {
|
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;
|
const url = embed.image.url;
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
|
@ -94,7 +119,7 @@ export default function EmbedMedia({ embed, width, height }: Props) {
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
openScreen({
|
openScreen({
|
||||||
id: "image_viewer",
|
id: "image_viewer",
|
||||||
embed: embed.image,
|
embed: embed.image!,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
onMouseDown={(ev) =>
|
onMouseDown={(ev) =>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { LinkExternal } from "@styled-icons/boxicons-regular";
|
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 styles from "./Embed.module.scss";
|
||||||
|
|
||||||
import IconButton from "../../../ui/IconButton";
|
import IconButton from "../../../ui/IconButton";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
embed: EmbedImage;
|
embed: API.Image;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function EmbedMediaActions({ embed }: Props) {
|
export default function EmbedMediaActions({ embed }: Props) {
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
import { Shield } from "@styled-icons/boxicons-regular";
|
import { Shield } from "@styled-icons/boxicons-regular";
|
||||||
import { Badges } from "revolt-api/types/Users";
|
|
||||||
import styled from "styled-components/macro";
|
import styled from "styled-components/macro";
|
||||||
|
|
||||||
import { Localizer, Text } from "preact-i18n";
|
import { Localizer, Text } from "preact-i18n";
|
||||||
|
|
||||||
import Tooltip from "../Tooltip";
|
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`
|
const BadgesBase = styled.div`
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
display: flex;
|
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";
|
import Checkbox, { CheckboxProps } from "../../ui/Checkbox";
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Cog } from "@styled-icons/boxicons-solid";
|
import { Cog } from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Link } from "react-router-dom";
|
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 styled from "styled-components/macro";
|
||||||
|
|
||||||
import { openContextMenu } from "preact-context-menu";
|
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 styled from "styled-components/macro";
|
||||||
|
|
||||||
import { Children } from "../../../types/Preact";
|
import { Children } from "../../../types/Preact";
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { VolumeMute, MicrophoneOff } from "@styled-icons/boxicons-solid";
|
import { VolumeMute, MicrophoneOff } from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { Masquerade } from "revolt-api/types/Channels";
|
import { User, API } from "revolt.js";
|
||||||
import { Presence } from "revolt-api/types/Users";
|
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
|
||||||
import styled, { css } from "styled-components/macro";
|
import styled, { css } from "styled-components/macro";
|
||||||
|
|
||||||
import { useApplicationState } from "../../../mobx/State";
|
import { useApplicationState } from "../../../mobx/State";
|
||||||
|
@ -18,17 +16,17 @@ type VoiceStatus = "muted" | "deaf";
|
||||||
interface Props extends IconBaseProps<User> {
|
interface Props extends IconBaseProps<User> {
|
||||||
status?: boolean;
|
status?: boolean;
|
||||||
voice?: VoiceStatus;
|
voice?: VoiceStatus;
|
||||||
masquerade?: Masquerade;
|
masquerade?: API.Masquerade;
|
||||||
showServerIdentity?: boolean;
|
showServerIdentity?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useStatusColour(user?: User) {
|
export function useStatusColour(user?: User) {
|
||||||
const theme = useApplicationState().settings.theme;
|
const theme = useApplicationState().settings.theme;
|
||||||
|
|
||||||
return user?.online && user?.status?.presence !== Presence.Invisible
|
return user?.online && user?.status?.presence !== "Invisible"
|
||||||
? user?.status?.presence === Presence.Idle
|
? user?.status?.presence === "Idle"
|
||||||
? theme.getVariable("status-away")
|
? theme.getVariable("status-away")
|
||||||
: user?.status?.presence === Presence.Busy
|
: user?.status?.presence === "Busy"
|
||||||
? theme.getVariable("status-busy")
|
? theme.getVariable("status-busy")
|
||||||
: theme.getVariable("status-online")
|
: theme.getVariable("status-online")
|
||||||
: theme.getVariable("status-invisible");
|
: theme.getVariable("status-invisible");
|
||||||
|
@ -76,7 +74,7 @@ export default observer(
|
||||||
|
|
||||||
let { url } = props;
|
let { url } = props;
|
||||||
if (masquerade?.avatar) {
|
if (masquerade?.avatar) {
|
||||||
url = masquerade.avatar;
|
url = client.proxyFile(masquerade.avatar);
|
||||||
} else if (!url) {
|
} else if (!url) {
|
||||||
let override;
|
let override;
|
||||||
if (target && showServerIdentity) {
|
if (target && showServerIdentity) {
|
||||||
|
@ -95,7 +93,7 @@ export default observer(
|
||||||
|
|
||||||
url =
|
url =
|
||||||
client.generateFileURL(
|
client.generateFileURL(
|
||||||
override ?? target?.avatar ?? attachment,
|
override ?? target?.avatar ?? attachment ?? undefined,
|
||||||
{ max_side: 256 },
|
{ max_side: 256 },
|
||||||
animate,
|
animate,
|
||||||
) ?? (target ? target.defaultAvatarURL : fallback);
|
) ?? (target ? target.defaultAvatarURL : fallback);
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { Masquerade } from "revolt-api/types/Channels";
|
import { User, API } from "revolt.js";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
|
||||||
import { Nullable } from "revolt.js/dist/util/null";
|
|
||||||
import styled from "styled-components/macro";
|
import styled from "styled-components/macro";
|
||||||
|
|
||||||
import { Ref } from "preact";
|
import { Ref } from "preact";
|
||||||
|
@ -32,7 +30,7 @@ const BotBadge = styled.div`
|
||||||
type UsernameProps = JSX.HTMLAttributes<HTMLElement> & {
|
type UsernameProps = JSX.HTMLAttributes<HTMLElement> & {
|
||||||
user?: User;
|
user?: User;
|
||||||
prefixAt?: boolean;
|
prefixAt?: boolean;
|
||||||
masquerade?: Masquerade;
|
masquerade?: API.Masquerade;
|
||||||
showServerIdentity?: boolean | "both";
|
showServerIdentity?: boolean | "both";
|
||||||
|
|
||||||
innerRef?: Ref<any>;
|
innerRef?: Ref<any>;
|
||||||
|
@ -93,7 +91,11 @@ export const Username = observer(
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<BotBadge>
|
<BotBadge>
|
||||||
|
{masquerade ? (
|
||||||
|
<Text id="app.main.channel.bridge" />
|
||||||
|
) : (
|
||||||
<Text id="app.main.channel.bot" />
|
<Text id="app.main.channel.bot" />
|
||||||
|
)}
|
||||||
</BotBadge>
|
</BotBadge>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -120,7 +122,7 @@ export default function UserShort({
|
||||||
user?: User;
|
user?: User;
|
||||||
size?: number;
|
size?: number;
|
||||||
prefixAt?: boolean;
|
prefixAt?: boolean;
|
||||||
masquerade?: Masquerade;
|
masquerade?: API.Masquerade;
|
||||||
showServerIdentity?: boolean;
|
showServerIdentity?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { openScreen } = useIntermediate();
|
const { openScreen } = useIntermediate();
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Presence } from "revolt-api/types/Users";
|
import { User, API } from "revolt.js";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
|
||||||
|
@ -25,15 +24,15 @@ export default observer(({ user, tooltip }: Props) => {
|
||||||
return <>{user.status.text}</>;
|
return <>{user.status.text}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.status?.presence === Presence.Busy) {
|
if (user.status?.presence === "Busy") {
|
||||||
return <Text id="app.status.busy" />;
|
return <Text id="app.status.busy" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.status?.presence === Presence.Idle) {
|
if (user.status?.presence === "Idle") {
|
||||||
return <Text id="app.status.idle" />;
|
return <Text id="app.status.idle" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.status?.presence === Presence.Invisible) {
|
if (user.status?.presence === "Invisible") {
|
||||||
return <Text id="app.status.offline" />;
|
return <Text id="app.status.offline" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Suspense, lazy } from "preact/compat";
|
||||||
const Renderer = lazy(() => import("./Renderer"));
|
const Renderer = lazy(() => import("./Renderer"));
|
||||||
|
|
||||||
export interface MarkdownProps {
|
export interface MarkdownProps {
|
||||||
content?: string;
|
content?: string | null;
|
||||||
disallowBigEmoji?: boolean;
|
disallowBigEmoji?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,6 @@ import "katex/dist/katex.min.css";
|
||||||
import MarkdownIt from "markdown-it";
|
import MarkdownIt from "markdown-it";
|
||||||
// @ts-expect-error No typings.
|
// @ts-expect-error No typings.
|
||||||
import MarkdownEmoji from "markdown-it-emoji/dist/markdown-it-emoji-bare";
|
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 { RE_MENTIONS } from "revolt.js";
|
||||||
|
|
||||||
import styles from "./Markdown.module.scss";
|
import styles from "./Markdown.module.scss";
|
||||||
|
@ -64,8 +60,6 @@ export const md: MarkdownIt = MarkdownIt({
|
||||||
.disable("image")
|
.disable("image")
|
||||||
.use(MarkdownEmoji, { defs: emojiDictionary })
|
.use(MarkdownEmoji, { defs: emojiDictionary })
|
||||||
.use(MarkdownSpoilers)
|
.use(MarkdownSpoilers)
|
||||||
.use(MarkdownSup)
|
|
||||||
.use(MarkdownSub)
|
|
||||||
.use(MarkdownKatex, {
|
.use(MarkdownKatex, {
|
||||||
throwOnError: false,
|
throwOnError: false,
|
||||||
maxExpand: 0,
|
maxExpand: 0,
|
||||||
|
@ -129,7 +123,7 @@ export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
|
||||||
const { openLink } = useIntermediate();
|
const { openLink } = useIntermediate();
|
||||||
|
|
||||||
if (typeof content === "undefined") return null;
|
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 replace the message with the mention at the time of render.
|
||||||
// We don't care if the mention changes.
|
// We don't care if the mention changes.
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { X } from "@styled-icons/boxicons-regular";
|
import { X } from "@styled-icons/boxicons-regular";
|
||||||
import { Crown } from "@styled-icons/boxicons-solid";
|
import { Crown } from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Presence } from "revolt-api/types/Users";
|
import { User, Channel } from "revolt.js";
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
|
||||||
|
|
||||||
import styles from "./Item.module.scss";
|
import styles from "./Item.module.scss";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
@ -65,7 +63,7 @@ export const UserButton = observer((props: UserProps) => {
|
||||||
data-alert={typeof alert === "string"}
|
data-alert={typeof alert === "string"}
|
||||||
data-online={
|
data-online={
|
||||||
typeof channel !== "undefined" ||
|
typeof channel !== "undefined" ||
|
||||||
(user.online && user.status?.presence !== Presence.Invisible)
|
(user.online && user.status?.presence !== "Invisible")
|
||||||
}
|
}
|
||||||
{...useTriggerEvents("Menu", {
|
{...useTriggerEvents("Menu", {
|
||||||
user: user._id,
|
user: user._id,
|
||||||
|
|
|
@ -4,12 +4,14 @@ import { useContext } from "preact/hooks";
|
||||||
import {
|
import {
|
||||||
ClientStatus,
|
ClientStatus,
|
||||||
StatusContext,
|
StatusContext,
|
||||||
|
useClient,
|
||||||
} from "../../../context/revoltjs/RevoltClient";
|
} from "../../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
import Banner from "../../ui/Banner";
|
import Banner from "../../ui/Banner";
|
||||||
|
|
||||||
export default function ConnectionStatus() {
|
export default function ConnectionStatus() {
|
||||||
const status = useContext(StatusContext);
|
const status = useContext(StatusContext);
|
||||||
|
const client = useClient();
|
||||||
|
|
||||||
if (status === ClientStatus.OFFLINE) {
|
if (status === ClientStatus.OFFLINE) {
|
||||||
return (
|
return (
|
||||||
|
@ -20,7 +22,10 @@ export default function ConnectionStatus() {
|
||||||
} else if (status === ClientStatus.DISCONNECTED) {
|
} else if (status === ClientStatus.DISCONNECTED) {
|
||||||
return (
|
return (
|
||||||
<Banner>
|
<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>
|
</Banner>
|
||||||
);
|
);
|
||||||
} else if (status === ClientStatus.CONNECTING) {
|
} else if (status === ClientStatus.CONNECTING) {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import {
|
||||||
} from "@styled-icons/boxicons-solid";
|
} from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Link, useLocation, useParams } from "react-router-dom";
|
import { Link, useLocation, useParams } from "react-router-dom";
|
||||||
import { RelationshipStatus } from "revolt-api/types/Users";
|
|
||||||
import styled, { css } from "styled-components/macro";
|
import styled, { css } from "styled-components/macro";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
@ -47,14 +46,16 @@ export default observer(() => {
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const client = useContext(AppContext);
|
const client = useContext(AppContext);
|
||||||
const state = useApplicationState();
|
const state = useApplicationState();
|
||||||
const { channel: currentChannel } = useParams<{ channel: string }>();
|
const { channel: channel_id } = useParams<{ channel: string }>();
|
||||||
const { openScreen } = useIntermediate();
|
const { openScreen } = useIntermediate();
|
||||||
|
|
||||||
const channels = [...client.channels.values()].filter(
|
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
|
// ! FIXME: move this globally
|
||||||
// Track what page the user was last on (in home page).
|
// Track what page the user was last on (in home page).
|
||||||
|
@ -66,7 +67,7 @@ export default observer(() => {
|
||||||
|
|
||||||
// ! FIXME: must be a better way
|
// ! FIXME: must be a better way
|
||||||
const incoming = [...client.users.values()].filter(
|
const incoming = [...client.users.values()].filter(
|
||||||
(user) => user?.relationship === RelationshipStatus.Incoming,
|
(user) => user?.relationship === "Incoming",
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -104,9 +105,10 @@ export default observer(() => {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<ConditionalLink
|
<ConditionalLink
|
||||||
active={obj?.channel_type === "SavedMessages"}
|
active={channel?.channel_type === "SavedMessages"}
|
||||||
to="/open/saved">
|
to="/open/saved">
|
||||||
<ButtonItem active={obj?.channel_type === "SavedMessages"}>
|
<ButtonItem
|
||||||
|
active={channel?.channel_type === "SavedMessages"}>
|
||||||
<Notepad size={20} />
|
<Notepad size={20} />
|
||||||
<span>
|
<span>
|
||||||
<Text id="app.navigation.tabs.saved" />
|
<Text id="app.navigation.tabs.saved" />
|
||||||
|
@ -152,7 +154,7 @@ export default observer(() => {
|
||||||
return (
|
return (
|
||||||
<ConditionalLink
|
<ConditionalLink
|
||||||
key={channel._id}
|
key={channel._id}
|
||||||
active={channel._id === currentChannel}
|
active={channel._id === channel_id}
|
||||||
to={`/channel/${channel._id}`}>
|
to={`/channel/${channel._id}`}>
|
||||||
<ChannelButton
|
<ChannelButton
|
||||||
user={user}
|
user={user}
|
||||||
|
@ -165,7 +167,7 @@ export default observer(() => {
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
alertCount={mentionCount}
|
alertCount={mentionCount}
|
||||||
active={channel._id === currentChannel}
|
active={channel._id === channel_id}
|
||||||
/>
|
/>
|
||||||
</ConditionalLink>
|
</ConditionalLink>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,10 +2,8 @@ import { Plus } from "@styled-icons/boxicons-regular";
|
||||||
import { Cog, Compass } from "@styled-icons/boxicons-solid";
|
import { Cog, Compass } from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Link, useHistory, useLocation, useParams } from "react-router-dom";
|
import { Link, useHistory, useLocation, useParams } from "react-router-dom";
|
||||||
import { RelationshipStatus } from "revolt-api/types/Users";
|
|
||||||
import styled, { css } from "styled-components/macro";
|
import styled, { css } from "styled-components/macro";
|
||||||
|
|
||||||
import { Ref } from "preact";
|
|
||||||
import { useTriggerEvents } from "preact-context-menu";
|
import { useTriggerEvents } from "preact-context-menu";
|
||||||
|
|
||||||
import ConditionalLink from "../../../lib/ConditionalLink";
|
import ConditionalLink from "../../../lib/ConditionalLink";
|
||||||
|
@ -248,7 +246,7 @@ export default observer(() => {
|
||||||
const { openScreen } = useIntermediate();
|
const { openScreen } = useIntermediate();
|
||||||
|
|
||||||
let alertCount = [...client.users.values()].filter(
|
let alertCount = [...client.users.values()].filter(
|
||||||
(x) => x.relationship === RelationshipStatus.Incoming,
|
(x) => x.relationship === "Incoming",
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
const homeActive =
|
const homeActive =
|
||||||
|
@ -290,7 +288,7 @@ export default observer(() => {
|
||||||
{channels
|
{channels
|
||||||
.filter(
|
.filter(
|
||||||
(x) =>
|
(x) =>
|
||||||
(x.channel_type === "DirectMessage" ||
|
((x.channel_type === "DirectMessage" && x.active) ||
|
||||||
x.channel_type === "Group") &&
|
x.channel_type === "Group") &&
|
||||||
x.unread,
|
x.unread,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Redirect, useParams } from "react-router";
|
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 styled, { css } from "styled-components/macro";
|
||||||
|
|
||||||
import { Ref } from "preact";
|
import { Ref } from "preact";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-disable react-hooks/rules-of-hooks */
|
/* eslint-disable react-hooks/rules-of-hooks */
|
||||||
import { observer } from "mobx-react-lite";
|
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";
|
import { getRenderer } from "../../../lib/renderer/Singleton";
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { GroupedVirtuoso } from "react-virtuoso";
|
import { GroupedVirtuoso } from "react-virtuoso";
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
import { Channel, User } from "revolt.js";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
|
||||||
import styled, { css } from "styled-components/macro";
|
import styled, { css } from "styled-components/macro";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
|
|
@ -2,11 +2,7 @@
|
||||||
import { autorun } from "mobx";
|
import { autorun } from "mobx";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { Role } from "revolt-api/types/Servers";
|
import { Channel, Server, User, API } from "revolt.js";
|
||||||
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 { useContext, useEffect, useState } from "preact/hooks";
|
import { useContext, useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
|
@ -62,7 +58,7 @@ function useEntries(
|
||||||
.map((id) => {
|
.map((id) => {
|
||||||
return [id, roles![id], roles![id].rank ?? 0] as [
|
return [id, roles![id], roles![id].rank ?? 0] as [
|
||||||
string,
|
string,
|
||||||
Role,
|
API.Role,
|
||||||
number,
|
number,
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
|
@ -96,7 +92,7 @@ function useEntries(
|
||||||
const sort = member?.nickname ?? u.username;
|
const sort = member?.nickname ?? u.username;
|
||||||
const entry = [u, sort] as [User, string];
|
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);
|
categories.offline.push(entry);
|
||||||
} else {
|
} else {
|
||||||
if (isServer) {
|
if (isServer) {
|
||||||
|
@ -164,7 +160,7 @@ function useEntries(
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return autorun(() => sort(generateKeys()));
|
return autorun(() => sort(generateKeys()));
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, []);
|
}, [channel]);
|
||||||
|
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Link, useParams } from "react-router-dom";
|
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 styled from "styled-components/macro";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
|
45
src/components/settings/roles/PermissionList.tsx
Normal file
45
src/components/settings/roles/PermissionList.tsx
Normal file
|
@ -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}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
151
src/components/settings/roles/PermissionSelect.tsx
Normal file
151
src/components/settings/roles/PermissionSelect.tsx
Normal file
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
12
src/components/settings/roles/RoleSelection.ts
Normal file
12
src/components/settings/roles/RoleSelection.ts
Normal file
|
@ -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;
|
disabled?: boolean;
|
||||||
contrast?: boolean;
|
contrast?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
children: Children;
|
children?: Children;
|
||||||
description?: Children;
|
description?: Children;
|
||||||
onChange: (state: boolean) => void;
|
onChange: (state: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-expect-error No typings.
|
||||||
import rgba from "color-rgba";
|
import rgba from "color-rgba";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
|
@ -102,7 +103,7 @@ export const FONTS: Record<Fonts, { name: string; load: () => void }> = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"OpenDyslexic": {
|
OpenDyslexic: {
|
||||||
name: "OpenDyslexic",
|
name: "OpenDyslexic",
|
||||||
load: async () => {
|
load: async () => {
|
||||||
await import("@fontsource/opendyslexic/400.css");
|
await import("@fontsource/opendyslexic/400.css");
|
||||||
|
@ -319,6 +320,14 @@ const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
|
||||||
:root {
|
:root {
|
||||||
${(props) => generateVariables(props.theme)}
|
${(props) => generateVariables(props.theme)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
${(props) =>
|
||||||
|
props.theme["min-opacity"] === 1 &&
|
||||||
|
`
|
||||||
|
* {
|
||||||
|
backdrop-filter: unset !important;
|
||||||
|
}
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const generateVariables = (theme: Theme) => {
|
export const generateVariables = (theme: Theme) => {
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
import { Prompt } from "react-router";
|
import { Prompt } from "react-router";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import type { Attachment } from "revolt-api/types/Autumn";
|
import { API, Channel, Message, Server, User } from "revolt.js";
|
||||||
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 { createContext } from "preact";
|
import { createContext } from "preact";
|
||||||
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
|
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
|
||||||
|
@ -31,6 +24,7 @@ export type Screen =
|
||||||
| { id: "clipboard"; text: string }
|
| { id: "clipboard"; text: string }
|
||||||
| { id: "token_reveal"; token: string; username: string }
|
| { id: "token_reveal"; token: string; username: string }
|
||||||
| { id: "external_link_prompt"; link: string }
|
| { id: "external_link_prompt"; link: string }
|
||||||
|
| { id: "sessions", confirm: () => void }
|
||||||
| {
|
| {
|
||||||
id: "_prompt";
|
id: "_prompt";
|
||||||
question: Children;
|
question: Children;
|
||||||
|
@ -61,7 +55,11 @@ export type Screen =
|
||||||
| {
|
| {
|
||||||
type: "create_channel";
|
type: "create_channel";
|
||||||
target: Server;
|
target: Server;
|
||||||
cb?: (channel: TextChannel | VoiceChannel) => void;
|
cb?: (
|
||||||
|
channel: Channel & {
|
||||||
|
channel_type: "TextChannel" | "VoiceChannel";
|
||||||
|
},
|
||||||
|
) => void;
|
||||||
}
|
}
|
||||||
| { type: "create_category"; target: Server }
|
| { type: "create_category"; target: Server }
|
||||||
))
|
))
|
||||||
|
@ -101,11 +99,11 @@ export type Screen =
|
||||||
omit?: string[];
|
omit?: string[];
|
||||||
callback: (users: string[]) => Promise<void>;
|
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: "channel_info"; channel: Channel }
|
||||||
| { id: "pending_requests"; users: User[] }
|
| { id: "pending_requests"; users: User[] }
|
||||||
| { id: "modify_account"; field: "username" | "email" | "password" }
|
| { 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";
|
id: "server_identity";
|
||||||
server: Server;
|
server: Server;
|
||||||
|
|
|
@ -9,7 +9,8 @@ import { InputModal } from "./modals/Input";
|
||||||
import { OnboardingModal } from "./modals/Onboarding";
|
import { OnboardingModal } from "./modals/Onboarding";
|
||||||
import { PromptModal } from "./modals/Prompt";
|
import { PromptModal } from "./modals/Prompt";
|
||||||
import { SignedOutModal } from "./modals/SignedOut";
|
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";
|
import { TokenRevealModal } from "./modals/TokenReveal";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
@ -40,6 +41,8 @@ export default function Modals({ screen, openScreen }: Props) {
|
||||||
return <OnboardingModal onClose={onClose} {...screen} />;
|
return <OnboardingModal onClose={onClose} {...screen} />;
|
||||||
case "external_link_prompt":
|
case "external_link_prompt":
|
||||||
return <ExternalLinkModal onClose={onClose} {...screen} />;
|
return <ExternalLinkModal onClose={onClose} {...screen} />;
|
||||||
|
case "sessions":
|
||||||
|
return <SessionsModal onClose={onClose} {...screen} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -11,7 +11,7 @@ export function ErrorModal({ onClose, error }: Props) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
visible={true}
|
visible={true}
|
||||||
onClose={() => false}
|
onClose={onClose}
|
||||||
title={<Text id="app.special.modals.error" />}
|
title={<Text id="app.special.modals.error" />}
|
||||||
actions={[
|
actions={[
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { useHistory } from "react-router";
|
import { useHistory } from "react-router";
|
||||||
import { Server } from "revolt.js/dist/maps/Servers";
|
import { Server } from "revolt.js";
|
||||||
import { ulid } from "ulid";
|
import { ulid } from "ulid";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
@ -69,6 +69,7 @@ export function InputModal({
|
||||||
)}
|
)}
|
||||||
<InputBox
|
<InputBox
|
||||||
value={value}
|
value={value}
|
||||||
|
style={{ width: "100%" }}
|
||||||
onChange={(e) => setValue(e.currentTarget.value)}
|
onChange={(e) => setValue(e.currentTarget.value)}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -101,7 +102,6 @@ export function SpecialInputModal(props: SpecialProps) {
|
||||||
callback={async (name) => {
|
callback={async (name) => {
|
||||||
const group = await client.channels.createGroup({
|
const group = await client.channels.createGroup({
|
||||||
name,
|
name,
|
||||||
nonce: ulid(),
|
|
||||||
users: [],
|
users: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -130,7 +130,6 @@ export function SpecialInputModal(props: SpecialProps) {
|
||||||
callback={async (name) => {
|
callback={async (name) => {
|
||||||
const server = await client.servers.createServer({
|
const server = await client.servers.createServer({
|
||||||
name,
|
name,
|
||||||
nonce: ulid(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
history.push(`/server/${server._id}`);
|
history.push(`/server/${server._id}`);
|
||||||
|
@ -159,7 +158,7 @@ export function SpecialInputModal(props: SpecialProps) {
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
question={<Text id="app.context_menu.set_custom_status" />}
|
question={<Text id="app.context_menu.set_custom_status" />}
|
||||||
field={<Text id="app.context_menu.custom_status" />}
|
field={<Text id="app.context_menu.custom_status" />}
|
||||||
defaultValue={client.user?.status?.text}
|
defaultValue={client.user?.status?.text ?? undefined}
|
||||||
callback={(text) =>
|
callback={(text) =>
|
||||||
client.users.edit({
|
client.users.edit({
|
||||||
status: {
|
status: {
|
||||||
|
@ -177,11 +176,8 @@ export function SpecialInputModal(props: SpecialProps) {
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
question={"Add Friend"}
|
question={"Add Friend"}
|
||||||
callback={(username) =>
|
callback={(username) =>
|
||||||
client
|
client.api
|
||||||
.req(
|
.put(`/users/${username as ""}/friend`)
|
||||||
"PUT",
|
|
||||||
`/users/${username}/friend` as "/users/id/friend",
|
|
||||||
)
|
|
||||||
.then(undefined)
|
.then(undefined)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -16,6 +16,10 @@
|
||||||
h1 {
|
h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-height: 80px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.form {
|
&.form {
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { TextChannel, VoiceChannel } from "revolt-api/types/Channels";
|
import { Channel, Message as MessageI, Server, User } from "revolt.js";
|
||||||
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 { ulid } from "ulid";
|
import { ulid } from "ulid";
|
||||||
|
|
||||||
import styles from "./Prompt.module.scss";
|
import styles from "./Prompt.module.scss";
|
||||||
|
@ -74,7 +70,11 @@ type SpecialProps = { onClose: () => void } & (
|
||||||
| {
|
| {
|
||||||
type: "create_channel";
|
type: "create_channel";
|
||||||
target: Server;
|
target: Server;
|
||||||
cb?: (channel: TextChannel | VoiceChannel) => void;
|
cb?: (
|
||||||
|
channel: Channel & {
|
||||||
|
channel_type: "TextChannel" | "VoiceChannel";
|
||||||
|
},
|
||||||
|
) => void;
|
||||||
}
|
}
|
||||||
| { type: "create_category"; target: Server }
|
| { type: "create_category"; target: Server }
|
||||||
);
|
);
|
||||||
|
@ -254,7 +254,7 @@ export const SpecialPromptModal = observer((props: SpecialProps) => {
|
||||||
|
|
||||||
props.target
|
props.target
|
||||||
.createInvite()
|
.createInvite()
|
||||||
.then((code) => setCode(code))
|
.then(({ _id }) => setCode(_id))
|
||||||
.catch((err) => setError(takeError(err)))
|
.catch((err) => setError(takeError(err)))
|
||||||
.finally(() => setProcessing(false));
|
.finally(() => setProcessing(false));
|
||||||
}, [props.target]);
|
}, [props.target]);
|
||||||
|
@ -429,11 +429,10 @@ export const SpecialPromptModal = observer((props: SpecialProps) => {
|
||||||
await props.target.createChannel({
|
await props.target.createChannel({
|
||||||
type,
|
type,
|
||||||
name,
|
name,
|
||||||
nonce: ulid(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (props.cb) {
|
if (props.cb) {
|
||||||
props.cb(channel);
|
props.cb(channel as any);
|
||||||
} else {
|
} else {
|
||||||
history.push(
|
history.push(
|
||||||
`/server/${props.target._id}/channel/${channel._id}`,
|
`/server/${props.target._id}/channel/${channel._id}`,
|
||||||
|
|
38
src/context/intermediate/modals/SessionsPrompt.tsx
Normal file
38
src/context/intermediate/modals/SessionsPrompt.tsx
Normal file
|
@ -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 { X } from "@styled-icons/boxicons-regular";
|
||||||
import { observer } from "mobx-react-lite";
|
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";
|
import styles from "./ChannelInfo.module.scss";
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { SubmitHandler, useForm } from "react-hook-form";
|
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 { Text } from "preact-i18n";
|
||||||
import { useContext, useState } from "preact/hooks";
|
import { useContext, useState } from "preact/hooks";
|
||||||
|
@ -13,7 +13,7 @@ import { takeError } from "../../revoltjs/util";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onCreate: (bot: Bot) => void;
|
onCreate: (bot: API.Bot) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FormInputs {
|
interface FormInputs {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* eslint-disable react-hooks/rules-of-hooks */
|
/* eslint-disable react-hooks/rules-of-hooks */
|
||||||
import { Attachment, AttachmentMetadata } from "revolt-api/types/Autumn";
|
import { API } from "revolt.js";
|
||||||
import { EmbedImage } from "revolt-api/types/January";
|
|
||||||
|
|
||||||
import styles from "./ImageViewer.module.scss";
|
import styles from "./ImageViewer.module.scss";
|
||||||
|
|
||||||
|
@ -12,11 +11,11 @@ import { useClient } from "../../revoltjs/RevoltClient";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
embed?: EmbedImage;
|
embed?: API.Image;
|
||||||
attachment?: Attachment;
|
attachment?: API.File;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageMetadata = AttachmentMetadata & { type: "Image" };
|
type ImageMetadata = API.Metadata & { type: "Image" };
|
||||||
|
|
||||||
export function ImageViewer({ attachment, embed, onClose }: Props) {
|
export function ImageViewer({ attachment, embed, onClose }: Props) {
|
||||||
if (attachment && attachment.metadata.type !== "Image") {
|
if (attachment && attachment.metadata.type !== "Image") {
|
||||||
|
|
|
@ -43,19 +43,19 @@ export function ModifyAccountModal({ onClose, field }: Props) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (field === "email") {
|
if (field === "email") {
|
||||||
await client.req("PATCH", "/auth/account/change/email", {
|
await client.api.patch("/auth/account/change/email", {
|
||||||
current_password: password,
|
current_password: password,
|
||||||
email: new_email,
|
email: new_email,
|
||||||
});
|
});
|
||||||
onClose();
|
onClose();
|
||||||
} else if (field === "password") {
|
} else if (field === "password") {
|
||||||
await client.req("PATCH", "/auth/account/change/password", {
|
await client.api.patch("/auth/account/change/password", {
|
||||||
current_password: password,
|
current_password: password,
|
||||||
password: new_password,
|
password: new_password,
|
||||||
});
|
});
|
||||||
onClose();
|
onClose();
|
||||||
} else if (field === "username") {
|
} else if (field === "username") {
|
||||||
await client.req("PATCH", "/users/id/username", {
|
await client.api.patch("/users/@me/username", {
|
||||||
username: new_username,
|
username: new_username,
|
||||||
password,
|
password,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { observer } from "mobx-react-lite";
|
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 styles from "./UserPicker.module.scss";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Server } from "revolt.js/dist/maps/Servers";
|
import { Server } from "revolt.js";
|
||||||
import styled, { css } from "styled-components/macro";
|
|
||||||
|
|
||||||
import styles from "./ServerIdentityModal.module.scss";
|
import styles from "./ServerIdentityModal.module.scss";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
|
import { noop } from "../../../lib/js";
|
||||||
|
|
||||||
import Button from "../../../components/ui/Button";
|
import Button from "../../../components/ui/Button";
|
||||||
import InputBox from "../../../components/ui/InputBox";
|
import InputBox from "../../../components/ui/InputBox";
|
||||||
import Modal from "../../../components/ui/Modal";
|
import Modal from "../../../components/ui/Modal";
|
||||||
|
@ -57,8 +58,12 @@ export const ServerIdentityModal = observer(({ server, onClose }: Props) => {
|
||||||
fileType="avatars"
|
fileType="avatars"
|
||||||
behaviour="upload"
|
behaviour="upload"
|
||||||
maxFileSize={4_000_000}
|
maxFileSize={4_000_000}
|
||||||
onUpload={(avatar) => member.edit({ avatar })}
|
onUpload={(avatar) =>
|
||||||
remove={() => member.edit({ remove: "Avatar" })}
|
member.edit({ avatar }).then(noop)
|
||||||
|
}
|
||||||
|
remove={() =>
|
||||||
|
member.edit({ remove: ["Avatar"] }).then(noop)
|
||||||
|
}
|
||||||
defaultPreview={client.user?.generateAvatarURL(
|
defaultPreview={client.user?.generateAvatarURL(
|
||||||
{
|
{
|
||||||
max_side: 256,
|
max_side: 256,
|
||||||
|
@ -92,7 +97,7 @@ export const ServerIdentityModal = observer(({ server, onClose }: Props) => {
|
||||||
<Button
|
<Button
|
||||||
plain
|
plain
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
member.edit({ remove: "Nickname" })
|
member.edit({ remove: ["Nickname"] })
|
||||||
}>
|
}>
|
||||||
<Text id="app.special.modals.actions.remove" />
|
<Text id="app.special.modals.actions.remove" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { RelationshipStatus } from "revolt-api/types/Users";
|
|
||||||
|
|
||||||
import styles from "./UserPicker.module.scss";
|
import styles from "./UserPicker.module.scss";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
|
@ -37,7 +35,7 @@ export function UserPicker(props: Props) {
|
||||||
.filter(
|
.filter(
|
||||||
(x) =>
|
(x) =>
|
||||||
x &&
|
x &&
|
||||||
x.relationship === RelationshipStatus.Friend &&
|
x.relationship === "Friend" &&
|
||||||
!omit.includes(x._id),
|
!omit.includes(x._id),
|
||||||
)
|
)
|
||||||
.map((x) => (
|
.map((x) => (
|
||||||
|
|
|
@ -100,6 +100,10 @@
|
||||||
background: var(--primary-background);
|
background: var(--primary-background);
|
||||||
border-radius: 0 0 var(--border-radius) var(--border-radius);
|
border-radius: 0 0 var(--border-radius) var(--border-radius);
|
||||||
|
|
||||||
|
.markdown {
|
||||||
|
user-select: text;
|
||||||
|
}
|
||||||
|
|
||||||
.empty {
|
.empty {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|
|
@ -9,9 +9,7 @@ import {
|
||||||
} from "@styled-icons/boxicons-solid";
|
} from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Link, useHistory } from "react-router-dom";
|
import { Link, useHistory } from "react-router-dom";
|
||||||
import { Profile, RelationshipStatus } from "revolt-api/types/Users";
|
import { UserPermission, API } from "revolt.js";
|
||||||
import { UserPermission } from "revolt.js/dist/api/permissions";
|
|
||||||
import { Route } from "revolt.js/dist/api/routes";
|
|
||||||
|
|
||||||
import styles from "./UserProfile.module.scss";
|
import styles from "./UserProfile.module.scss";
|
||||||
import { Localizer, Text } from "preact-i18n";
|
import { Localizer, Text } from "preact-i18n";
|
||||||
|
@ -44,18 +42,18 @@ interface Props {
|
||||||
user_id: string;
|
user_id: string;
|
||||||
dummy?: boolean;
|
dummy?: boolean;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
dummyProfile?: Profile;
|
dummyProfile?: API.UserProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UserProfile = observer(
|
export const UserProfile = observer(
|
||||||
({ user_id, onClose, dummy, dummyProfile }: Props) => {
|
({ user_id, onClose, dummy, dummyProfile }: Props) => {
|
||||||
const { openScreen, writeClipboard } = useIntermediate();
|
const { openScreen, writeClipboard } = useIntermediate();
|
||||||
|
|
||||||
const [profile, setProfile] = useState<undefined | null | Profile>(
|
const [profile, setProfile] = useState<
|
||||||
undefined,
|
undefined | null | API.UserProfile
|
||||||
);
|
>(undefined);
|
||||||
const [mutual, setMutual] = useState<
|
const [mutual, setMutual] = useState<
|
||||||
undefined | null | Route<"GET", "/users/id/mutual">["response"]
|
undefined | null | API.MutualResponse
|
||||||
>(undefined);
|
>(undefined);
|
||||||
const [isPublicBot, setIsPublicBot] = useState<
|
const [isPublicBot, setIsPublicBot] = useState<
|
||||||
undefined | null | boolean
|
undefined | null | boolean
|
||||||
|
@ -139,7 +137,11 @@ export const UserProfile = observer(
|
||||||
|
|
||||||
const backgroundURL =
|
const backgroundURL =
|
||||||
profile &&
|
profile &&
|
||||||
client.generateFileURL(profile.background, { width: 1000 }, true);
|
client.generateFileURL(
|
||||||
|
profile.background as any,
|
||||||
|
{ width: 1000 },
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
const badges = user.badges ?? 0;
|
const badges = user.badges ?? 0;
|
||||||
const flags = user.flags ?? 0;
|
const flags = user.flags ?? 0;
|
||||||
|
@ -198,7 +200,7 @@ export const UserProfile = observer(
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
{user.relationship === RelationshipStatus.Friend && (
|
{user.relationship === "Friend" && (
|
||||||
<Localizer>
|
<Localizer>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={
|
content={
|
||||||
|
@ -214,8 +216,7 @@ export const UserProfile = observer(
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Localizer>
|
</Localizer>
|
||||||
)}
|
)}
|
||||||
{user.relationship === RelationshipStatus.User &&
|
{user.relationship === "User" && !dummy && (
|
||||||
!dummy && (
|
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onClose?.();
|
onClose?.();
|
||||||
|
@ -227,15 +228,14 @@ export const UserProfile = observer(
|
||||||
{!user.bot &&
|
{!user.bot &&
|
||||||
flags != 2 &&
|
flags != 2 &&
|
||||||
flags != 4 &&
|
flags != 4 &&
|
||||||
(user.relationship ===
|
(user.relationship === "Incoming" ||
|
||||||
RelationshipStatus.Incoming ||
|
user.relationship === "None" ||
|
||||||
user.relationship ===
|
user.relationship === null) && (
|
||||||
RelationshipStatus.None) && (
|
|
||||||
<IconButton onClick={() => user.addFriend()}>
|
<IconButton onClick={() => user.addFriend()}>
|
||||||
<UserPlus size={28} />
|
<UserPlus size={28} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
{user.relationship === RelationshipStatus.Outgoing && (
|
{user.relationship === "Outgoing" && (
|
||||||
<IconButton onClick={() => user.removeFriend()}>
|
<IconButton onClick={() => user.removeFriend()}>
|
||||||
<UserX size={28} />
|
<UserX size={28} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
@ -247,7 +247,7 @@ export const UserProfile = observer(
|
||||||
onClick={() => setTab("profile")}>
|
onClick={() => setTab("profile")}>
|
||||||
<Text id="app.special.popovers.user_profile.profile" />
|
<Text id="app.special.popovers.user_profile.profile" />
|
||||||
</div>
|
</div>
|
||||||
{user.relationship !== RelationshipStatus.User && (
|
{user.relationship !== "User" && (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
data-active={tab === "friends"}
|
data-active={tab === "friends"}
|
||||||
|
@ -338,7 +338,9 @@ export const UserProfile = observer(
|
||||||
<Text id="app.special.popovers.user_profile.sub.information" />
|
<Text id="app.special.popovers.user_profile.sub.information" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div className={styles.markdown}>
|
||||||
<Markdown content={profile?.content} />
|
<Markdown content={profile?.content} />
|
||||||
|
</div>
|
||||||
{/*<div className={styles.category}><Text id="app.special.popovers.user_profile.sub.connections" /></div>*/}
|
{/*<div className={styles.category}><Text id="app.special.popovers.user_profile.sub.connections" /></div>*/}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -94,6 +94,7 @@ export function grabFiles(
|
||||||
input.addEventListener("change", async (e) => {
|
input.addEventListener("change", async (e) => {
|
||||||
const files = (e.currentTarget as HTMLInputElement)?.files;
|
const files = (e.currentTarget as HTMLInputElement)?.files;
|
||||||
if (!files) return;
|
if (!files) return;
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (file.size > maxFileSize) {
|
if (file.size > maxFileSize) {
|
||||||
return tooLarge();
|
return tooLarge();
|
||||||
|
@ -184,6 +185,7 @@ export function FileUploader(props: Props) {
|
||||||
id: "error",
|
id: "error",
|
||||||
error: "FileTooLarge",
|
error: "FileTooLarge",
|
||||||
});
|
});
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
files.push(blob);
|
files.push(blob);
|
||||||
|
@ -212,6 +214,7 @@ export function FileUploader(props: Props) {
|
||||||
for (const item of dropped) {
|
for (const item of dropped) {
|
||||||
if (item.size > props.maxFileSize) {
|
if (item.size > props.maxFileSize) {
|
||||||
openScreen({ id: "error", error: "FileTooLarge" });
|
openScreen({ id: "error", error: "FileTooLarge" });
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
files.push(item);
|
files.push(item);
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import { Route, Switch, useHistory, useParams } from "react-router-dom";
|
import { Route, Switch, useHistory, useParams } from "react-router-dom";
|
||||||
import { Presence, RelationshipStatus } from "revolt-api/types/Users";
|
import { Message, User } from "revolt.js";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
|
||||||
import { decodeTime } from "ulid";
|
import { decodeTime } from "ulid";
|
||||||
|
|
||||||
import { useCallback, useContext, useEffect } from "preact/hooks";
|
import { useCallback, useContext, useEffect } from "preact/hooks";
|
||||||
|
@ -84,7 +82,7 @@ function Notifier() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let body, icon;
|
let body, icon;
|
||||||
if (typeof msg.content === "string") {
|
if (msg.content) {
|
||||||
body = client.markdownToText(msg.content);
|
body = client.markdownToText(msg.content);
|
||||||
|
|
||||||
if (msg.masquerade?.avatar) {
|
if (msg.masquerade?.avatar) {
|
||||||
|
@ -92,22 +90,23 @@ function Notifier() {
|
||||||
} else {
|
} else {
|
||||||
icon = msg.author?.generateAvatarURL({ max_side: 256 });
|
icon = msg.author?.generateAvatarURL({ max_side: 256 });
|
||||||
}
|
}
|
||||||
} else {
|
} else if (msg.system) {
|
||||||
const users = client.users;
|
const users = client.users;
|
||||||
switch (msg.content.type) {
|
|
||||||
|
switch (msg.system.type) {
|
||||||
case "user_added":
|
case "user_added":
|
||||||
case "user_remove":
|
case "user_remove":
|
||||||
{
|
{
|
||||||
const user = users.get(msg.content.id);
|
const user = users.get(msg.system.id);
|
||||||
body = translate(
|
body = translate(
|
||||||
`app.main.channel.system.${
|
`app.main.channel.system.${
|
||||||
msg.content.type === "user_added"
|
msg.system.type === "user_added"
|
||||||
? "added_by"
|
? "added_by"
|
||||||
: "removed_by"
|
: "removed_by"
|
||||||
}`,
|
}`,
|
||||||
{
|
{
|
||||||
user: user?.username,
|
user: user?.username,
|
||||||
other_user: users.get(msg.content.by)
|
other_user: users.get(msg.system.by)
|
||||||
?.username,
|
?.username,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -121,9 +120,9 @@ function Notifier() {
|
||||||
case "user_kicked":
|
case "user_kicked":
|
||||||
case "user_banned":
|
case "user_banned":
|
||||||
{
|
{
|
||||||
const user = users.get(msg.content.id);
|
const user = users.get(msg.system.id);
|
||||||
body = translate(
|
body = translate(
|
||||||
`app.main.channel.system.${msg.content.type}`,
|
`app.main.channel.system.${msg.system.type}`,
|
||||||
{ user: user?.username },
|
{ user: user?.username },
|
||||||
);
|
);
|
||||||
icon = user?.generateAvatarURL({
|
icon = user?.generateAvatarURL({
|
||||||
|
@ -133,12 +132,12 @@ function Notifier() {
|
||||||
break;
|
break;
|
||||||
case "channel_renamed":
|
case "channel_renamed":
|
||||||
{
|
{
|
||||||
const user = users.get(msg.content.by);
|
const user = users.get(msg.system.by);
|
||||||
body = translate(
|
body = translate(
|
||||||
`app.main.channel.system.channel_renamed`,
|
`app.main.channel.system.channel_renamed`,
|
||||||
{
|
{
|
||||||
user: users.get(msg.content.by)?.username,
|
user: users.get(msg.system.by)?.username,
|
||||||
name: msg.content.name,
|
name: msg.system.name,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
icon = user?.generateAvatarURL({
|
icon = user?.generateAvatarURL({
|
||||||
|
@ -149,10 +148,10 @@ function Notifier() {
|
||||||
case "channel_description_changed":
|
case "channel_description_changed":
|
||||||
case "channel_icon_changed":
|
case "channel_icon_changed":
|
||||||
{
|
{
|
||||||
const user = users.get(msg.content.by);
|
const user = users.get(msg.system.by);
|
||||||
body = translate(
|
body = translate(
|
||||||
`app.main.channel.system.${msg.content.type}`,
|
`app.main.channel.system.${msg.system.type}`,
|
||||||
{ user: users.get(msg.content.by)?.username },
|
{ user: users.get(msg.system.by)?.username },
|
||||||
);
|
);
|
||||||
icon = user?.generateAvatarURL({
|
icon = user?.generateAvatarURL({
|
||||||
max_side: 256,
|
max_side: 256,
|
||||||
|
@ -210,17 +209,17 @@ function Notifier() {
|
||||||
|
|
||||||
const relationship = useCallback(
|
const relationship = useCallback(
|
||||||
async (user: User) => {
|
async (user: User) => {
|
||||||
if (client.user?.status?.presence === Presence.Busy) return;
|
if (client.user?.status?.presence === "Busy") return;
|
||||||
if (!showNotification) return;
|
if (!showNotification) return;
|
||||||
|
|
||||||
let event;
|
let event;
|
||||||
switch (user.relationship) {
|
switch (user.relationship) {
|
||||||
case RelationshipStatus.Incoming:
|
case "Incoming":
|
||||||
event = translate("notifications.sent_request", {
|
event = translate("notifications.sent_request", {
|
||||||
person: user.username,
|
person: user.username,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case RelationshipStatus.Friend:
|
case "Friend":
|
||||||
event = translate("notifications.now_friends", {
|
event = translate("notifications.now_friends", {
|
||||||
person: user.username,
|
person: user.username,
|
||||||
});
|
});
|
||||||
|
|
|
@ -50,7 +50,7 @@ export default observer(({ children }: Props) => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (navigator.onLine) {
|
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()]);
|
}, [state.auth.getSession()]);
|
||||||
|
|
||||||
useEffect(() => registerEvents(state.auth, setStatus, client), [client]);
|
useEffect(() => registerEvents(state, setStatus, client), [client]);
|
||||||
|
|
||||||
if (!loaded || status === ClientStatus.LOADING) {
|
if (!loaded || status === ClientStatus.LOADING) {
|
||||||
return <Preloader type="spinner" />;
|
return <Preloader type="spinner" />;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* This file monitors the message cache to delete any queued messages that have already sent.
|
* 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";
|
import { useContext, useEffect } from "preact/hooks";
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* This file monitors changes to settings and syncs them to the server.
|
* 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";
|
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 { 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 { resetMemberSidebarFetched } from "../../components/navigation/right/MemberSidebar";
|
||||||
import { ClientStatus } from "./RevoltClient";
|
import { ClientStatus } from "./RevoltClient";
|
||||||
|
|
||||||
export function registerEvents(
|
export function registerEvents(
|
||||||
auth: Auth,
|
state: State,
|
||||||
setStatus: StateUpdater<ClientStatus>,
|
setStatus: StateUpdater<ClientStatus>,
|
||||||
client: Client,
|
client: Client,
|
||||||
) {
|
) {
|
||||||
|
@ -25,9 +27,22 @@ export function registerEvents(
|
||||||
},
|
},
|
||||||
|
|
||||||
logout: () => {
|
logout: () => {
|
||||||
auth.logout();
|
state.auth.logout();
|
||||||
|
state.reset();
|
||||||
setStatus(ClientStatus.READY);
|
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) {
|
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";
|
import { Text } from "preact-i18n";
|
||||||
|
|
||||||
|
@ -6,23 +6,27 @@ import { Children } from "../../types/Preact";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export function takeError(error: any): string {
|
export function takeError(error: any): string {
|
||||||
const type = error?.response?.data?.type;
|
if (error.response) {
|
||||||
const id = type;
|
const status = error.response.status;
|
||||||
if (!type) {
|
if (error.response.type) {
|
||||||
if (
|
return error.response.type;
|
||||||
error?.response?.status === 401 ||
|
}
|
||||||
error?.response?.status === 403
|
|
||||||
) {
|
switch (status) {
|
||||||
|
case 429:
|
||||||
|
return "TooManyRequests";
|
||||||
|
case 401:
|
||||||
|
case 403:
|
||||||
return "Unauthorized";
|
return "Unauthorized";
|
||||||
} else if (error && !!error.isAxiosError && !error.response) {
|
default:
|
||||||
|
return "UnknownError";
|
||||||
|
}
|
||||||
|
} else if (error.request) {
|
||||||
return "NetworkError";
|
return "NetworkError";
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return "UnknownError";
|
return "UnknownError";
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getChannelName(
|
export function getChannelName(
|
||||||
|
|
1
src/globals.d.ts
vendored
1
src/globals.d.ts
vendored
|
@ -4,6 +4,7 @@ type NativeConfig = {
|
||||||
frame: boolean;
|
frame: boolean;
|
||||||
build: Build;
|
build: Build;
|
||||||
discordRPC: boolean;
|
discordRPC: boolean;
|
||||||
|
minimiseToTray: boolean;
|
||||||
hardwareAcceleration: boolean;
|
hardwareAcceleration: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,18 +11,10 @@ import {
|
||||||
Trash,
|
Trash,
|
||||||
} from "@styled-icons/boxicons-regular";
|
} from "@styled-icons/boxicons-regular";
|
||||||
import { Cog, UserVoice } from "@styled-icons/boxicons-solid";
|
import { Cog, UserVoice } from "@styled-icons/boxicons-solid";
|
||||||
|
import { isFirefox } from "react-device-detect";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { Attachment } from "revolt-api/types/Autumn";
|
import { Channel, Message, Server, User, API } from "revolt.js";
|
||||||
import { Presence, RelationshipStatus } from "revolt-api/types/Users";
|
import { Permission, UserPermission } from "revolt.js";
|
||||||
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 {
|
import {
|
||||||
ContextMenuWithData,
|
ContextMenuWithData,
|
||||||
|
@ -60,7 +52,7 @@ interface ContextMenuData {
|
||||||
server_list?: string;
|
server_list?: string;
|
||||||
channel?: string;
|
channel?: string;
|
||||||
message?: Message;
|
message?: Message;
|
||||||
attachment?: Attachment;
|
attachment?: API.File;
|
||||||
|
|
||||||
unread?: boolean;
|
unread?: boolean;
|
||||||
queued?: QueuedMessage;
|
queued?: QueuedMessage;
|
||||||
|
@ -82,9 +74,9 @@ type Action =
|
||||||
| { action: "quote_message"; content: string }
|
| { action: "quote_message"; content: string }
|
||||||
| { action: "edit_message"; id: string }
|
| { action: "edit_message"; id: string }
|
||||||
| { action: "delete_message"; target: Message }
|
| { action: "delete_message"; target: Message }
|
||||||
| { action: "open_file"; attachment: Attachment }
|
| { action: "open_file"; attachment: API.File }
|
||||||
| { action: "save_file"; attachment: Attachment }
|
| { action: "save_file"; attachment: API.File }
|
||||||
| { action: "copy_file_link"; attachment: Attachment }
|
| { action: "copy_file_link"; attachment: API.File }
|
||||||
| { action: "open_link"; link: string }
|
| { action: "open_link"; link: string }
|
||||||
| { action: "copy_link"; link: string }
|
| { action: "copy_link"; link: string }
|
||||||
| { action: "remove_member"; channel: Channel; user: User }
|
| { action: "remove_member"; channel: Channel; user: User }
|
||||||
|
@ -97,7 +89,7 @@ type Action =
|
||||||
| { action: "add_friend"; user: User }
|
| { action: "add_friend"; user: User }
|
||||||
| { action: "remove_friend"; user: User }
|
| { action: "remove_friend"; user: User }
|
||||||
| { action: "cancel_friend"; user: User }
|
| { action: "cancel_friend"; user: User }
|
||||||
| { action: "set_presence"; presence: Presence }
|
| { action: "set_presence"; presence: API.Presence }
|
||||||
| { action: "set_status" }
|
| { action: "set_status" }
|
||||||
| { action: "clear_status" }
|
| { action: "clear_status" }
|
||||||
| { action: "create_channel"; target: Server }
|
| { action: "create_channel"; target: Server }
|
||||||
|
@ -295,7 +287,7 @@ export default function ContextMenus() {
|
||||||
"attachments",
|
"attachments",
|
||||||
"attachments/download",
|
"attachments/download",
|
||||||
),
|
),
|
||||||
"_blank",
|
isFirefox || window.native ? "_blank" : "_self",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -505,9 +497,8 @@ export default function ContextMenus() {
|
||||||
|
|
||||||
if (server_list) {
|
if (server_list) {
|
||||||
const server = client.servers.get(server_list)!;
|
const server = client.servers.get(server_list)!;
|
||||||
const permissions = server.permission;
|
|
||||||
if (server) {
|
if (server) {
|
||||||
if (permissions & ServerPermission.ManageChannels) {
|
if (server.havePermission("ManageChannel")) {
|
||||||
generateAction({
|
generateAction({
|
||||||
action: "create_category",
|
action: "create_category",
|
||||||
target: server,
|
target: server,
|
||||||
|
@ -517,7 +508,8 @@ export default function ContextMenus() {
|
||||||
target: server,
|
target: server,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (permissions & ServerPermission.ManageServer)
|
|
||||||
|
if (server.havePermission("ManageServer"))
|
||||||
generateAction({
|
generateAction({
|
||||||
action: "open_server_settings",
|
action: "open_server_settings",
|
||||||
id: server_list,
|
id: server_list,
|
||||||
|
@ -589,40 +581,45 @@ export default function ContextMenus() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
if (!user.bot) {
|
let actions: (Action["action"] | boolean)[];
|
||||||
let actions: Action["action"][];
|
|
||||||
switch (user.relationship) {
|
switch (user.relationship) {
|
||||||
case RelationshipStatus.User:
|
case "User":
|
||||||
actions = [];
|
actions = [];
|
||||||
break;
|
break;
|
||||||
case RelationshipStatus.Friend:
|
case "Friend":
|
||||||
actions = ["remove_friend", "block_user"];
|
actions = [
|
||||||
|
!user.bot && "remove_friend",
|
||||||
|
"block_user",
|
||||||
|
];
|
||||||
break;
|
break;
|
||||||
case RelationshipStatus.Incoming:
|
case "Incoming":
|
||||||
actions = [
|
actions = [
|
||||||
"add_friend",
|
"add_friend",
|
||||||
"cancel_friend",
|
"cancel_friend",
|
||||||
"block_user",
|
"block_user",
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
case RelationshipStatus.Outgoing:
|
case "Outgoing":
|
||||||
actions = ["cancel_friend", "block_user"];
|
actions = [
|
||||||
|
!user.bot && "cancel_friend",
|
||||||
|
"block_user",
|
||||||
|
];
|
||||||
break;
|
break;
|
||||||
case RelationshipStatus.Blocked:
|
case "Blocked":
|
||||||
actions = ["unblock_user"];
|
actions = ["unblock_user"];
|
||||||
break;
|
break;
|
||||||
case RelationshipStatus.BlockedOther:
|
case "BlockedOther":
|
||||||
actions = ["block_user"];
|
actions = ["block_user"];
|
||||||
break;
|
break;
|
||||||
case RelationshipStatus.None:
|
case "None":
|
||||||
default:
|
default:
|
||||||
if (
|
if ((user.flags && 2) || (user.flags && 4)) {
|
||||||
(user.flags && 2) ||
|
|
||||||
(user.flags && 4)
|
|
||||||
) {
|
|
||||||
actions = ["block_user"];
|
actions = ["block_user"];
|
||||||
} else {
|
} else {
|
||||||
actions = ["add_friend", "block_user"];
|
actions = [
|
||||||
|
!user.bot && "add_friend",
|
||||||
|
"block_user",
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -644,9 +641,10 @@ export default function ContextMenus() {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < actions.length; i++) {
|
for (let i = 0; i < actions.length; i++) {
|
||||||
// Typescript can't determine that user the actions are linked together correctly
|
let action = actions[i];
|
||||||
|
if (action) {
|
||||||
generateAction({
|
generateAction({
|
||||||
action: actions[i],
|
action,
|
||||||
user,
|
user,
|
||||||
} as unknown as Action);
|
} as unknown as Action);
|
||||||
}
|
}
|
||||||
|
@ -673,9 +671,7 @@ export default function ContextMenus() {
|
||||||
userId !== uid &&
|
userId !== uid &&
|
||||||
uid !== server.owner
|
uid !== server.owner
|
||||||
) {
|
) {
|
||||||
if (
|
if (serverPermissions & Permission.KickMembers)
|
||||||
serverPermissions & ServerPermission.KickMembers
|
|
||||||
)
|
|
||||||
generateAction(
|
generateAction(
|
||||||
{
|
{
|
||||||
action: "kick_member",
|
action: "kick_member",
|
||||||
|
@ -688,7 +684,7 @@ export default function ContextMenus() {
|
||||||
"var(--error)", // the only relevant part really
|
"var(--error)", // the only relevant part really
|
||||||
);
|
);
|
||||||
|
|
||||||
if (serverPermissions & ServerPermission.BanMembers)
|
if (serverPermissions & Permission.BanMembers)
|
||||||
generateAction(
|
generateAction(
|
||||||
{
|
{
|
||||||
action: "ban_member",
|
action: "ban_member",
|
||||||
|
@ -718,8 +714,7 @@ export default function ContextMenus() {
|
||||||
if (message && !queued) {
|
if (message && !queued) {
|
||||||
const sendPermission =
|
const sendPermission =
|
||||||
message.channel &&
|
message.channel &&
|
||||||
message.channel.permission &
|
message.channel.permission & Permission.SendMessage;
|
||||||
ChannelPermission.SendMessage;
|
|
||||||
|
|
||||||
if (sendPermission) {
|
if (sendPermission) {
|
||||||
generateAction({
|
generateAction({
|
||||||
|
@ -759,8 +754,7 @@ export default function ContextMenus() {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
message.author_id === userId ||
|
message.author_id === userId ||
|
||||||
channelPermissions &
|
channelPermissions & Permission.ManageMessages
|
||||||
ChannelPermission.ManageMessages
|
|
||||||
) {
|
) {
|
||||||
generateAction({
|
generateAction({
|
||||||
action: "delete_message",
|
action: "delete_message",
|
||||||
|
@ -903,7 +897,7 @@ export default function ContextMenus() {
|
||||||
case "VoiceChannel":
|
case "VoiceChannel":
|
||||||
if (
|
if (
|
||||||
channelPermissions &
|
channelPermissions &
|
||||||
ChannelPermission.InviteOthers
|
Permission.InviteOthers
|
||||||
) {
|
) {
|
||||||
generateAction({
|
generateAction({
|
||||||
action: "create_invite",
|
action: "create_invite",
|
||||||
|
@ -913,7 +907,7 @@ export default function ContextMenus() {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
serverPermissions &
|
serverPermissions &
|
||||||
ServerPermission.ManageServer
|
Permission.ManageServer
|
||||||
)
|
)
|
||||||
generateAction(
|
generateAction(
|
||||||
{
|
{
|
||||||
|
@ -926,7 +920,7 @@ export default function ContextMenus() {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
serverPermissions &
|
serverPermissions &
|
||||||
ServerPermission.ManageChannels
|
Permission.ManageChannel
|
||||||
)
|
)
|
||||||
generateAction({
|
generateAction({
|
||||||
action: "delete_channel",
|
action: "delete_channel",
|
||||||
|
@ -958,20 +952,15 @@ export default function ContextMenus() {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
serverPermissions &
|
serverPermissions & Permission.ChangeNickname ||
|
||||||
ServerPermission.ChangeNickname ||
|
serverPermissions & Permission.ChangeAvatar
|
||||||
serverPermissions &
|
|
||||||
ServerPermission.ChangeAvatar
|
|
||||||
)
|
)
|
||||||
generateAction(
|
generateAction(
|
||||||
{ action: "edit_identity", target: server },
|
{ action: "edit_identity", target: server },
|
||||||
"edit_identity",
|
"edit_identity",
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (serverPermissions & Permission.ManageServer)
|
||||||
serverPermissions &
|
|
||||||
ServerPermission.ManageServer
|
|
||||||
)
|
|
||||||
generateAction(
|
generateAction(
|
||||||
{
|
{
|
||||||
action: "open_server_settings",
|
action: "open_server_settings",
|
||||||
|
@ -1060,7 +1049,7 @@ export default function ContextMenus() {
|
||||||
<MenuItem
|
<MenuItem
|
||||||
data={{
|
data={{
|
||||||
action: "set_presence",
|
action: "set_presence",
|
||||||
presence: Presence.Online,
|
presence: "Online",
|
||||||
}}
|
}}
|
||||||
disabled={!isOnline}>
|
disabled={!isOnline}>
|
||||||
<div className="indicator online" />
|
<div className="indicator online" />
|
||||||
|
@ -1069,7 +1058,7 @@ export default function ContextMenus() {
|
||||||
<MenuItem
|
<MenuItem
|
||||||
data={{
|
data={{
|
||||||
action: "set_presence",
|
action: "set_presence",
|
||||||
presence: Presence.Idle,
|
presence: "Idle",
|
||||||
}}
|
}}
|
||||||
disabled={!isOnline}>
|
disabled={!isOnline}>
|
||||||
<div className="indicator idle" />
|
<div className="indicator idle" />
|
||||||
|
@ -1078,7 +1067,7 @@ export default function ContextMenus() {
|
||||||
<MenuItem
|
<MenuItem
|
||||||
data={{
|
data={{
|
||||||
action: "set_presence",
|
action: "set_presence",
|
||||||
presence: Presence.Busy,
|
presence: "Busy",
|
||||||
}}
|
}}
|
||||||
disabled={!isOnline}>
|
disabled={!isOnline}>
|
||||||
<div className="indicator busy" />
|
<div className="indicator busy" />
|
||||||
|
@ -1087,7 +1076,7 @@ export default function ContextMenus() {
|
||||||
<MenuItem
|
<MenuItem
|
||||||
data={{
|
data={{
|
||||||
action: "set_presence",
|
action: "set_presence",
|
||||||
presence: Presence.Invisible,
|
presence: "Invisible",
|
||||||
}}
|
}}
|
||||||
disabled={!isOnline}>
|
disabled={!isOnline}>
|
||||||
<div className="indicator invisible" />
|
<div className="indicator invisible" />
|
||||||
|
|
|
@ -9,8 +9,8 @@ import {
|
||||||
LeftArrowAlt,
|
LeftArrowAlt,
|
||||||
} from "@styled-icons/boxicons-regular";
|
} from "@styled-icons/boxicons-regular";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
import { Channel } from "revolt.js";
|
||||||
import { Server } from "revolt.js/dist/maps/Servers";
|
import { Server } from "revolt.js";
|
||||||
|
|
||||||
import { ContextMenuWithData, MenuItem } from "preact-context-menu";
|
import { ContextMenuWithData, MenuItem } from "preact-context-menu";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/* eslint-disable react-hooks/rules-of-hooks */
|
/* eslint-disable react-hooks/rules-of-hooks */
|
||||||
import { action, makeAutoObservable } from "mobx";
|
import { action, makeAutoObservable } from "mobx";
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
import { Channel } from "revolt.js";
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "revolt.js";
|
||||||
import { Nullable } from "revolt.js/dist/util/null";
|
import { Nullable } from "revolt.js";
|
||||||
|
|
||||||
import { SimpleRenderer } from "./simple/SimpleRenderer";
|
import { SimpleRenderer } from "./simple/SimpleRenderer";
|
||||||
import { RendererRoutines, ScrollState } from "./types";
|
import { RendererRoutines, ScrollState } from "./types";
|
||||||
|
@ -222,3 +222,7 @@ export function getRenderer(channel: Channel) {
|
||||||
|
|
||||||
return renderer;
|
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";
|
import { ChannelRenderer } from "./Singleton";
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ import { types } from "mediasoup-client";
|
||||||
|
|
||||||
import { Device, Producer, Transport } from "mediasoup-client/lib/types";
|
import { Device, Producer, Transport } from "mediasoup-client/lib/types";
|
||||||
|
|
||||||
|
import { useApplicationState } from "../../mobx/State";
|
||||||
|
|
||||||
import Signaling from "./Signaling";
|
import Signaling from "./Signaling";
|
||||||
import {
|
import {
|
||||||
ProduceType,
|
ProduceType,
|
||||||
|
@ -58,6 +60,8 @@ export default class VoiceClient extends EventEmitter<VoiceEvents> {
|
||||||
|
|
||||||
this.isDeaf = false;
|
this.isDeaf = false;
|
||||||
|
|
||||||
|
const state = useApplicationState();
|
||||||
|
|
||||||
this.signaling.on(
|
this.signaling.on(
|
||||||
"data",
|
"data",
|
||||||
(json) => {
|
(json) => {
|
||||||
|
@ -65,11 +69,13 @@ export default class VoiceClient extends EventEmitter<VoiceEvents> {
|
||||||
switch (json.type) {
|
switch (json.type) {
|
||||||
case WSEventType.UserJoined: {
|
case WSEventType.UserJoined: {
|
||||||
this.participants.set(data.id, {});
|
this.participants.set(data.id, {});
|
||||||
|
state.settings.sounds.playSound("call_join");
|
||||||
this.emit("userJoined", data.id);
|
this.emit("userJoined", data.id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WSEventType.UserLeft: {
|
case WSEventType.UserLeft: {
|
||||||
this.participants.delete(data.id);
|
this.participants.delete(data.id);
|
||||||
|
state.settings.sounds.playSound("call_leave");
|
||||||
this.emit("userLeft", data.id);
|
this.emit("userLeft", data.id);
|
||||||
|
|
||||||
if (this.recvTransport) this.stopConsume(data.id);
|
if (this.recvTransport) this.stopConsume(data.id);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { action, makeAutoObservable, runInAction } from "mobx";
|
import { action, makeAutoObservable, runInAction } from "mobx";
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
import { Channel } from "revolt.js";
|
||||||
import { Nullable, toNullable } from "revolt.js/dist/util/null";
|
import { Nullable, toNullable } from "revolt.js";
|
||||||
|
|
||||||
import type { ProduceType, VoiceUser } from "./Types";
|
import type { ProduceType, VoiceUser } from "./Types";
|
||||||
import type VoiceClient from "./VoiceClient";
|
import type VoiceClient from "./VoiceClient";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// @ts-expect-error No typings.
|
// @ts-expect-error No typings.
|
||||||
import stringify from "json-stringify-deterministic";
|
import stringify from "json-stringify-deterministic";
|
||||||
import localforage from "localforage";
|
import localforage from "localforage";
|
||||||
import { makeAutoObservable, reaction } from "mobx";
|
import { makeAutoObservable, reaction, runInAction } from "mobx";
|
||||||
import { Client } from "revolt.js";
|
import { Client } from "revolt.js";
|
||||||
|
|
||||||
import { reportError } from "../lib/ErrorBoundary";
|
import { reportError } from "../lib/ErrorBoundary";
|
||||||
|
@ -184,11 +184,13 @@ export default class State {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.keys(obj).length > 0) {
|
if (Object.keys(obj).length > 0) {
|
||||||
|
if (client.websocket.connected) {
|
||||||
client.syncSetSettings(
|
client.syncSetSettings(
|
||||||
obj as any,
|
obj as any,
|
||||||
revision,
|
revision,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
@ -198,6 +200,7 @@ export default class State {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sync.setRevision(id, revision);
|
this.sync.setRevision(id, revision);
|
||||||
|
if (client.websocket.connected) {
|
||||||
client.syncSetSettings(
|
client.syncSetSettings(
|
||||||
(
|
(
|
||||||
store as unknown as Syncable
|
store as unknown as Syncable
|
||||||
|
@ -207,6 +210,7 @@ export default class State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to serialise!");
|
console.error("Failed to serialise!");
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
@ -263,6 +267,26 @@ export default class State {
|
||||||
// Post-hydration, init plugins.
|
// Post-hydration, init plugins.
|
||||||
this.plugins.init();
|
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;
|
var state: State;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue