mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-09-29 03:28:11 +00:00
Merge branch 'master' into production
This commit is contained in:
@@ -5,3 +5,6 @@ dist_injected
|
|||||||
node_modules
|
node_modules
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
|
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Security
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
If you would like to report a security vulnerability,
|
|
||||||
please email **[security@revolt.chat](mailto:security@revolt.chat)**,
|
|
||||||
this will open a new ticket in ticket system, you should receive a response
|
|
||||||
within the next couple of days, potentially within a few minutes if someone
|
|
||||||
is currently active.
|
|
||||||
|
|
||||||
To help us best triage the issue, please provide:
|
|
||||||
|
|
||||||
- The type of issue at hand
|
|
||||||
- The name of the relevant project affected
|
|
||||||
- Reproduction steps
|
|
||||||
- Reference to any relevant source file(s) that you may suspect are causing the issue
|
|
||||||
- Any extra information about your configuration.
|
|
||||||
- Description of potential ways this can be exploited, if you can list any
|
|
||||||
|
|
||||||
For revoltchat/revite in particular:
|
|
||||||
|
|
||||||
- Please include the commit hash of the client, it is visible in settings under the log out button.
|
|
||||||
|
|
||||||
Thank you for helping Revolt.
|
|
||||||
@@ -7,6 +7,9 @@ dist-ssr
|
|||||||
*.log
|
*.log
|
||||||
/.idea
|
/.idea
|
||||||
|
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/install-state.gz
|
||||||
|
|
||||||
public/assets
|
public/assets
|
||||||
public/assets_*
|
public/assets_*
|
||||||
!public/assets_default
|
!public/assets_default
|
||||||
|
|||||||
Vendored
+1
-2
@@ -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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+785
File diff suppressed because one or more lines are too long
+11
@@ -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
|
||||||
+6
-9
@@ -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 .
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
Submodule external/lang updated: 6a72c5c952...bac88cffd1
+13
-16
@@ -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",
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
"@fontsource/noto-sans": "^4.4.5",
|
"@fontsource/noto-sans": "^4.4.5",
|
||||||
"@fontsource/open-sans": "^4.5.2",
|
"@fontsource/open-sans": "^4.5.2",
|
||||||
"@fontsource/opendyslexic": "^4.5.2",
|
"@fontsource/opendyslexic": "^4.5.2",
|
||||||
"@fontsource/poppins": "^4.4.5",
|
"@fontsource/poppins": "^4.4.5",
|
||||||
"@fontsource/raleway": "^4.4.5",
|
"@fontsource/raleway": "^4.4.5",
|
||||||
"@fontsource/roboto": "^4.4.5",
|
"@fontsource/roboto": "^4.4.5",
|
||||||
"@fontsource/roboto-mono": "^4.4.5",
|
"@fontsource/roboto-mono": "^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"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="64.00001046823172" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-0.000008942940667111543 -0.0000033745862566547657 64.00001046823172 64.00000545874563" height="64.00000545874563" style="-webkit-print-color-adjust: exact;"><g id="shape-d9b11490-3403-11ec-bc16-7b519797d558"><rect rx="0" ry="0" x="0" y="0" transform="matrix(1.0000000000000007,-8.726646259971662e-8,-1.5707963280665485e-7,1.0000000000000124,0.000005026548230091521,0.0000027925264056705146)" width="64" height="64" style="fill: rgb(255, 255, 255); fill-opacity: 1;"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 626 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="64.00001046823172" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-0.000008942940667111543 -0.0000033745862566547657 64.00001046823172 64.00000545874563" height="64.00000545874563" style="-webkit-print-color-adjust: exact;"><g id="shape-d9b11490-3403-11ec-bc16-7b519797d558"><rect rx="0" ry="0" x="0" y="0" transform="matrix(1.0000000000000007,-8.726646259971662e-8,-1.5707963280665485e-7,1.0000000000000124,0.000005026548230091521,0.0000027925264056705146)" width="64" height="64" style="fill: rgb(255, 255, 255); fill-opacity: 1;"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 626 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="64.00001046823172" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-0.000008942940667111543 -0.0000033745862566547657 64.00001046823172 64.00000545874563" height="64.00000545874563" style="-webkit-print-color-adjust: exact;"><g id="shape-d9b11490-3403-11ec-bc16-7b519797d558"><rect rx="0" ry="0" x="0" y="0" transform="matrix(1.0000000000000007,-8.726646259971662e-8,-1.5707963280665485e-7,1.0000000000000124,0.000005026548230091521,0.0000027925264056705146)" width="64" height="64" style="fill: rgb(255, 255, 255); fill-opacity: 1;"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 626 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="64.00001046823172" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-0.000008942940667111543 -0.0000033745862566547657 64.00001046823172 64.00000545874563" height="64.00000545874563" style="-webkit-print-color-adjust: exact;"><g id="shape-d9b11490-3403-11ec-bc16-7b519797d558"><rect rx="0" ry="0" x="0" y="0" transform="matrix(1.0000000000000007,-8.726646259971662e-8,-1.5707963280665485e-7,1.0000000000000124,0.000005026548230091521,0.0000027925264056705146)" width="64" height="64" style="fill: rgb(255, 255, 255); fill-opacity: 1;"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 626 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="64.00001046823172" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-0.000008942940667111543 -0.0000033745862566547657 64.00001046823172 64.00000545874563" height="64.00000545874563" style="-webkit-print-color-adjust: exact;"><g id="shape-d9b11490-3403-11ec-bc16-7b519797d558"><rect rx="0" ry="0" x="0" y="0" transform="matrix(1.0000000000000007,-8.726646259971662e-8,-1.5707963280665485e-7,1.0000000000000124,0.000005026548230091521,0.0000027925264056705146)" width="64" height="64" style="fill: rgb(255, 255, 255); fill-opacity: 1;"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 626 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="64.00001046823172" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-0.000008942940667111543 -0.0000033745862566547657 64.00001046823172 64.00000545874563" height="64.00000545874563" style="-webkit-print-color-adjust: exact;"><g id="shape-d9b11490-3403-11ec-bc16-7b519797d558"><rect rx="0" ry="0" x="0" y="0" transform="matrix(1.0000000000000007,-8.726646259971662e-8,-1.5707963280665485e-7,1.0000000000000124,0.000005026548230091521,0.0000027925264056705146)" width="64" height="64" style="fill: rgb(255, 255, 255); fill-opacity: 1;"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 626 B |
@@ -1,6 +1,6 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { 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 (
|
||||||
{...props}
|
<img
|
||||||
src={url}
|
{...props}
|
||||||
alt={attachment.filename}
|
src={url}
|
||||||
loading="lazy"
|
alt={attachment.filename}
|
||||||
className={classNames(styles.image, {
|
loading="lazy"
|
||||||
[styles.loading]: loading !== ImageLoadingState.Loaded
|
className={classNames(styles.image, {
|
||||||
})}
|
[styles.loading]: loading !== ImageLoadingState.Loaded,
|
||||||
onClick={() =>
|
})}
|
||||||
openScreen({ id: "image_viewer", attachment })
|
onClick={() => openScreen({ id: "image_viewer", attachment })}
|
||||||
}
|
onMouseDown={(ev) => ev.button === 1 && window.open(url, "_blank")}
|
||||||
onMouseDown={(ev) =>
|
onLoad={() => setLoading(ImageLoadingState.Loaded)}
|
||||||
ev.button === 1 && window.open(url, "_blank")
|
onError={() => setLoading(ImageLoadingState.Error)}
|
||||||
}
|
/>
|
||||||
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 () => {
|
||||||
|
setProcessing(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setProcessing(true);
|
await client.joinInvite(invite);
|
||||||
|
|
||||||
if (invite.type === "Server") {
|
history.push(
|
||||||
if (client.servers.get(invite.server_id)) {
|
`/server/${invite.server_id}/channel/${invite.channel_id}`,
|
||||||
history.push(
|
);
|
||||||
`/server/${invite.server_id}/channel/${invite.channel_id}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dispose = reaction(
|
|
||||||
() =>
|
|
||||||
client.servers.get(
|
|
||||||
invite.server_id,
|
|
||||||
),
|
|
||||||
(server) => {
|
|
||||||
if (server) {
|
|
||||||
client.unreads!.markMultipleRead(
|
|
||||||
server.channel_ids,
|
|
||||||
);
|
|
||||||
|
|
||||||
history.push(
|
|
||||||
`/server/${server._id}/channel/${invite.channel_id}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
dispose();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.joinInvite(code);
|
|
||||||
} 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>
|
||||||
<Text id="app.main.channel.bot" />
|
{masquerade ? (
|
||||||
|
<Text id="app.main.channel.bridge" />
|
||||||
|
) : (
|
||||||
|
<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";
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { API, Channel, Member, Server } from "revolt.js";
|
||||||
|
import { Permission } from "revolt.js";
|
||||||
|
|
||||||
|
import { PermissionSelect } from "./PermissionSelect";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
value: API.OverrideField | number;
|
||||||
|
onChange: (v: API.OverrideField | number) => void;
|
||||||
|
|
||||||
|
target?: Channel | Server;
|
||||||
|
filter?: (keyof typeof Permission)[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PermissionList({ value, onChange, filter, target }: Props) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{(Object.keys(Permission) as (keyof typeof Permission)[])
|
||||||
|
.filter(
|
||||||
|
(key) =>
|
||||||
|
![
|
||||||
|
"GrantAllSafe",
|
||||||
|
"TimeoutMembers",
|
||||||
|
"ReadMessageHistory",
|
||||||
|
"Speak",
|
||||||
|
"Video",
|
||||||
|
"MuteMembers",
|
||||||
|
"DeafenMembers",
|
||||||
|
"MoveMembers",
|
||||||
|
"ManageWebhooks",
|
||||||
|
].includes(key) &&
|
||||||
|
(!filter || filter.includes(key)),
|
||||||
|
)
|
||||||
|
.map((x) => (
|
||||||
|
<PermissionSelect
|
||||||
|
id={x}
|
||||||
|
key={x}
|
||||||
|
permission={Permission[x]}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
target={target}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
import { Lock } from "@styled-icons/boxicons-solid";
|
||||||
|
import Long from "long";
|
||||||
|
import { API, Channel, Member, Server } from "revolt.js";
|
||||||
|
import { Permission } from "revolt.js";
|
||||||
|
import styled, { css } from "styled-components";
|
||||||
|
|
||||||
|
import { Text } from "preact-i18n";
|
||||||
|
import { useMemo } from "preact/hooks";
|
||||||
|
|
||||||
|
import Checkbox from "../../ui/Checkbox";
|
||||||
|
import { OverrideSwitch } from "@revoltchat/ui";
|
||||||
|
|
||||||
|
interface PermissionSelectProps {
|
||||||
|
id: keyof typeof Permission;
|
||||||
|
target?: Channel | Server;
|
||||||
|
permission: number;
|
||||||
|
value: API.OverrideField | number;
|
||||||
|
onChange: (value: API.OverrideField | number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type State = "Allow" | "Neutral" | "Deny";
|
||||||
|
|
||||||
|
const PermissionEntry = styled.label<{ disabled?: boolean }>`
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 8px 0;
|
||||||
|
display: flex;
|
||||||
|
font-size: 1.1em;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lock {
|
||||||
|
margin-inline-start: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: var(--secondary-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
${(props) =>
|
||||||
|
props.disabled &&
|
||||||
|
css`
|
||||||
|
color: var(--tertiary-foreground);
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export function PermissionSelect({
|
||||||
|
id,
|
||||||
|
permission,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
target,
|
||||||
|
}: PermissionSelectProps) {
|
||||||
|
const state: State = useMemo(() => {
|
||||||
|
if (typeof value === "object") {
|
||||||
|
if (Long.fromNumber(value.d).and(permission).eq(permission)) {
|
||||||
|
return "Deny";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Long.fromNumber(value.a).and(permission).eq(permission)) {
|
||||||
|
return "Allow";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Neutral";
|
||||||
|
} else {
|
||||||
|
if (Long.fromNumber(value).and(permission).eq(permission)) {
|
||||||
|
return "Allow";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Neutral";
|
||||||
|
}
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
function onSwitch(state: State) {
|
||||||
|
if (typeof value !== "object") throw "!";
|
||||||
|
|
||||||
|
// Convert to Long so we can do bitwise ops.
|
||||||
|
let allow = Long.fromNumber(value.a);
|
||||||
|
let deny = Long.fromNumber(value.d);
|
||||||
|
|
||||||
|
// Clear the current permission value.
|
||||||
|
if (allow.and(permission).eq(permission)) {
|
||||||
|
allow = allow.xor(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deny.and(permission).eq(permission)) {
|
||||||
|
deny = deny.xor(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the current permission state.
|
||||||
|
if (state === "Allow") {
|
||||||
|
allow = allow.or(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state === "Deny") {
|
||||||
|
deny = deny.or(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoke state change.
|
||||||
|
onChange({
|
||||||
|
a: allow.toNumber(),
|
||||||
|
d: deny.toNumber(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const member =
|
||||||
|
target &&
|
||||||
|
(target instanceof Server ? target.member : target.server?.member);
|
||||||
|
|
||||||
|
const disabled = member && !member.hasPermission(target!, id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PermissionEntry disabled={disabled}>
|
||||||
|
<span class="title">
|
||||||
|
<span>
|
||||||
|
<Text id={`permissions.${id}.t`}>{id}</Text>
|
||||||
|
{disabled && <Lock className="lock" size={14} />}
|
||||||
|
</span>
|
||||||
|
<span class="description">
|
||||||
|
<Text id={`permissions.${id}.d`} />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
{typeof value === "object" ? (
|
||||||
|
<OverrideSwitch
|
||||||
|
disabled={disabled}
|
||||||
|
state={state}
|
||||||
|
onChange={onSwitch}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Checkbox
|
||||||
|
disabled={disabled}
|
||||||
|
checked={state === "Allow"}
|
||||||
|
onChange={() =>
|
||||||
|
onChange(
|
||||||
|
Long.fromNumber(value, false)
|
||||||
|
.xor(permission)
|
||||||
|
.toNumber(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</PermissionEntry>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { API } from "revolt.js";
|
||||||
|
|
||||||
|
export type RoleOrDefault = (
|
||||||
|
| API.Role
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
permissions: number;
|
||||||
|
colour?: string;
|
||||||
|
hoist?: boolean;
|
||||||
|
rank?: number;
|
||||||
|
}
|
||||||
|
) & { id: string };
|
||||||
@@ -89,7 +89,7 @@ export interface CheckboxProps {
|
|||||||
disabled?: boolean;
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-1
@@ -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}`,
|
||||||
|
|||||||
@@ -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,28 +216,26 @@ export const UserProfile = observer(
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Localizer>
|
</Localizer>
|
||||||
)}
|
)}
|
||||||
{user.relationship === RelationshipStatus.User &&
|
{user.relationship === "User" && !dummy && (
|
||||||
!dummy && (
|
<IconButton
|
||||||
<IconButton
|
onClick={() => {
|
||||||
onClick={() => {
|
onClose?.();
|
||||||
onClose?.();
|
history.push(`/settings/profile`);
|
||||||
history.push(`/settings/profile`);
|
}}>
|
||||||
}}>
|
<Edit size={28} />
|
||||||
<Edit size={28} />
|
</IconButton>
|
||||||
</IconButton>
|
)}
|
||||||
)}
|
|
||||||
{!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>
|
||||||
)}
|
)}
|
||||||
<Markdown content={profile?.content} />
|
<div className={styles.markdown}>
|
||||||
|
<Markdown content={profile?.content} />
|
||||||
|
</div>
|
||||||
{/*<div className={styles.category}><Text id="app.special.popovers.user_profile.sub.connections" /></div>*/}
|
{/*<div 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
|
|
||||||
) {
|
|
||||||
return "Unauthorized";
|
|
||||||
} else if (error && !!error.isAxiosError && !error.response) {
|
|
||||||
return "NetworkError";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(error);
|
switch (status) {
|
||||||
return "UnknownError";
|
case 429:
|
||||||
|
return "TooManyRequests";
|
||||||
|
case 401:
|
||||||
|
case 403:
|
||||||
|
return "Unauthorized";
|
||||||
|
default:
|
||||||
|
return "UnknownError";
|
||||||
|
}
|
||||||
|
} else if (error.request) {
|
||||||
|
return "NetworkError";
|
||||||
}
|
}
|
||||||
|
|
||||||
return id;
|
console.error(error);
|
||||||
|
return "UnknownError";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getChannelName(
|
export function getChannelName(
|
||||||
|
|||||||
Vendored
+1
@@ -4,6 +4,7 @@ type NativeConfig = {
|
|||||||
frame: boolean;
|
frame: boolean;
|
||||||
build: Build;
|
build: Build;
|
||||||
discordRPC: boolean;
|
discordRPC: boolean;
|
||||||
|
minimiseToTray: boolean;
|
||||||
hardwareAcceleration: boolean;
|
hardwareAcceleration: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+83
-94
@@ -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,64 +581,70 @@ 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 "User":
|
||||||
case RelationshipStatus.User:
|
actions = [];
|
||||||
actions = [];
|
break;
|
||||||
break;
|
case "Friend":
|
||||||
case RelationshipStatus.Friend:
|
actions = [
|
||||||
actions = ["remove_friend", "block_user"];
|
!user.bot && "remove_friend",
|
||||||
break;
|
"block_user",
|
||||||
case RelationshipStatus.Incoming:
|
];
|
||||||
|
break;
|
||||||
|
case "Incoming":
|
||||||
|
actions = [
|
||||||
|
"add_friend",
|
||||||
|
"cancel_friend",
|
||||||
|
"block_user",
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
case "Outgoing":
|
||||||
|
actions = [
|
||||||
|
!user.bot && "cancel_friend",
|
||||||
|
"block_user",
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
case "Blocked":
|
||||||
|
actions = ["unblock_user"];
|
||||||
|
break;
|
||||||
|
case "BlockedOther":
|
||||||
|
actions = ["block_user"];
|
||||||
|
break;
|
||||||
|
case "None":
|
||||||
|
default:
|
||||||
|
if ((user.flags && 2) || (user.flags && 4)) {
|
||||||
|
actions = ["block_user"];
|
||||||
|
} else {
|
||||||
actions = [
|
actions = [
|
||||||
"add_friend",
|
!user.bot && "add_friend",
|
||||||
"cancel_friend",
|
|
||||||
"block_user",
|
"block_user",
|
||||||
];
|
];
|
||||||
break;
|
}
|
||||||
case RelationshipStatus.Outgoing:
|
}
|
||||||
actions = ["cancel_friend", "block_user"];
|
|
||||||
break;
|
|
||||||
case RelationshipStatus.Blocked:
|
|
||||||
actions = ["unblock_user"];
|
|
||||||
break;
|
|
||||||
case RelationshipStatus.BlockedOther:
|
|
||||||
actions = ["block_user"];
|
|
||||||
break;
|
|
||||||
case RelationshipStatus.None:
|
|
||||||
default:
|
|
||||||
if (
|
|
||||||
(user.flags && 2) ||
|
|
||||||
(user.flags && 4)
|
|
||||||
) {
|
|
||||||
actions = ["block_user"];
|
|
||||||
} else {
|
|
||||||
actions = ["add_friend", "block_user"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userPermissions & UserPermission.ViewProfile) {
|
if (userPermissions & UserPermission.ViewProfile) {
|
||||||
generateAction({
|
generateAction({
|
||||||
action: "view_profile",
|
action: "view_profile",
|
||||||
user,
|
user,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
user._id !== userId &&
|
user._id !== userId &&
|
||||||
userPermissions & UserPermission.SendMessage
|
userPermissions & UserPermission.SendMessage
|
||||||
) {
|
) {
|
||||||
generateAction({
|
generateAction({
|
||||||
action: "message_user",
|
action: "message_user",
|
||||||
user,
|
user,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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";
|
||||||
|
|||||||
+35
-11
@@ -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,10 +184,12 @@ export default class State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Object.keys(obj).length > 0) {
|
if (Object.keys(obj).length > 0) {
|
||||||
client.syncSetSettings(
|
if (client.websocket.connected) {
|
||||||
obj as any,
|
client.syncSetSettings(
|
||||||
revision,
|
obj as any,
|
||||||
);
|
revision,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -198,12 +200,14 @@ export default class State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.sync.setRevision(id, revision);
|
this.sync.setRevision(id, revision);
|
||||||
client.syncSetSettings(
|
if (client.websocket.connected) {
|
||||||
(
|
client.syncSetSettings(
|
||||||
store as unknown as Syncable
|
(
|
||||||
).toSyncable(),
|
store as unknown as Syncable
|
||||||
revision,
|
).toSyncable(),
|
||||||
);
|
revision,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
Reference in New Issue
Block a user