chore: update prettier setting
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
|||||||
module.exports = {extends: ['@commitlint/config-conventional']};
|
module.exports = { extends: ["@commitlint/config-conventional"] };
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"singleQuote": false,
|
"singleQuote": false,
|
||||||
"bracketSpacing": true,
|
"bracketSpacing": true,
|
||||||
"jsxBracketSameLine": false,
|
"jsxBracketSameLine": false,
|
||||||
"tabWidth": 1,
|
"tabWidth": 2,
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"trailingComma": "none"
|
"trailingComma": "none"
|
||||||
}
|
}
|
||||||
Vendored
+8
-5
@@ -5,7 +5,7 @@
|
|||||||
"comments": false,
|
"comments": false,
|
||||||
"strings": true
|
"strings": true
|
||||||
},
|
},
|
||||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"eslint.alwaysShowStatus": true,
|
"eslint.alwaysShowStatus": true,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
@@ -13,14 +13,17 @@
|
|||||||
},
|
},
|
||||||
"eslint.validate": ["javascript"],
|
"eslint.validate": ["javascript"],
|
||||||
"[json]": {
|
"[json]": {
|
||||||
"editor.defaultFormatter": "svipas.prettier-plus",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true
|
"editor.formatOnSave": true
|
||||||
},
|
},
|
||||||
"[jsonc]": {
|
"[jsonc]": {
|
||||||
"editor.defaultFormatter": "svipas.prettier-plus"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"[javascriptreact]": {
|
"[javascriptreact]": {
|
||||||
"editor.defaultFormatter": "svipas.prettier-plus"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"emmet.triggerExpansionOnTab": true
|
"emmet.triggerExpansionOnTab": true,
|
||||||
|
"[javascript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,19 @@
|
|||||||
|
## Refs
|
||||||
|
|
||||||
- preview demo: https://privoce.rustchat.com/
|
- preview demo: https://privoce.rustchat.com/
|
||||||
- design: https://www.figma.com/file/EHnNr53kNmDWgUT86It6CH/UI
|
- design: https://www.figma.com/file/EHnNr53kNmDWgUT86It6CH/UI
|
||||||
- backend APIs: https://dev.rustchat.com/api/swagger
|
- backend APIs: https://dev.rustchat.com/api/swagger
|
||||||
- text editor: https://plate.udecode.io/docs/installation
|
- text editor: https://plate.udecode.io/docs/installation
|
||||||
- markdown editor: https://nhn.github.io/tui.editor/latest/
|
- markdown editor: https://nhn.github.io/tui.editor/latest/
|
||||||
- redux: [@reduxjs/toolkit](https://redux-toolkit.js.org/introduction/getting-started)
|
- redux: [@reduxjs/toolkit](https://redux-toolkit.js.org/introduction/getting-started)
|
||||||
- indexDB wrapper: https://github.com/localForage/localForage
|
- indexDB wrapper: https://github.com/localForage/localForage
|
||||||
|
|
||||||
|
## Local Development
|
||||||
|
|
||||||
|
- [VS Code](https://code.visualstudio.com/) Editor Recommended
|
||||||
|
- VS Code plugins:
|
||||||
|
- dbaeumer.vscode-eslint: ESLint
|
||||||
|
- esbenp.prettier-vscode: Prettier
|
||||||
|
- components.vscode-styled-components: Syntax highlighting for styled-components
|
||||||
|
- jonkwheeler.styled-components-snippets: Styled-Components Snippets for VSCode
|
||||||
|
- dsznajder.es7-react-js-snippets:Extensions for React, React-Native and Redux in JS/TS with ES7+ syntax
|
||||||
|
|||||||
+5
-7
@@ -9,9 +9,7 @@ delete require.cache[require.resolve("./paths")];
|
|||||||
|
|
||||||
const NODE_ENV = process.env.NODE_ENV;
|
const NODE_ENV = process.env.NODE_ENV;
|
||||||
if (!NODE_ENV) {
|
if (!NODE_ENV) {
|
||||||
throw new Error(
|
throw new Error("The NODE_ENV environment variable is required but was not specified.");
|
||||||
"The NODE_ENV environment variable is required but was not specified."
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
|
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
|
||||||
@@ -22,7 +20,7 @@ const dotenvFiles = [
|
|||||||
// results for everyone
|
// results for everyone
|
||||||
NODE_ENV !== "test" && `${paths.dotenv}.local`,
|
NODE_ENV !== "test" && `${paths.dotenv}.local`,
|
||||||
`${paths.dotenv}.${NODE_ENV}`,
|
`${paths.dotenv}.${NODE_ENV}`,
|
||||||
paths.dotenv,
|
paths.dotenv
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
// Load environment variables from .env* files. Suppress warnings using silent
|
// Load environment variables from .env* files. Suppress warnings using silent
|
||||||
@@ -34,7 +32,7 @@ dotenvFiles.forEach((dotenvFile) => {
|
|||||||
if (fs.existsSync(dotenvFile)) {
|
if (fs.existsSync(dotenvFile)) {
|
||||||
require("dotenv-expand")(
|
require("dotenv-expand")(
|
||||||
require("dotenv").config({
|
require("dotenv").config({
|
||||||
path: dotenvFile,
|
path: dotenvFile
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -88,7 +86,7 @@ function getClientEnvironment(publicUrl) {
|
|||||||
WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
|
WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
|
||||||
// Whether or not react-refresh is enabled.
|
// Whether or not react-refresh is enabled.
|
||||||
// It is defined here so it is available in the webpackHotDevClient.
|
// It is defined here so it is available in the webpackHotDevClient.
|
||||||
FAST_REFRESH: process.env.FAST_REFRESH !== "false",
|
FAST_REFRESH: process.env.FAST_REFRESH !== "false"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// Stringify all values so we can feed into webpack DefinePlugin
|
// Stringify all values so we can feed into webpack DefinePlugin
|
||||||
@@ -96,7 +94,7 @@ function getClientEnvironment(publicUrl) {
|
|||||||
"process.env": Object.keys(raw).reduce((env, key) => {
|
"process.env": Object.keys(raw).reduce((env, key) => {
|
||||||
env[key] = JSON.stringify(raw[key]);
|
env[key] = JSON.stringify(raw[key]);
|
||||||
return env;
|
return env;
|
||||||
}, {}),
|
}, {})
|
||||||
};
|
};
|
||||||
|
|
||||||
return { raw, stringified };
|
return { raw, stringified };
|
||||||
|
|||||||
+15
-21
@@ -1,10 +1,10 @@
|
|||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require("fs");
|
||||||
const path = require('path');
|
const path = require("path");
|
||||||
const crypto = require('crypto');
|
const crypto = require("crypto");
|
||||||
const chalk = require('react-dev-utils/chalk');
|
const chalk = require("react-dev-utils/chalk");
|
||||||
const paths = require('./paths');
|
const paths = require("./paths");
|
||||||
|
|
||||||
// Ensure the certificate and key provided are valid and if not
|
// Ensure the certificate and key provided are valid and if not
|
||||||
// throw an easy to debug error
|
// throw an easy to debug error
|
||||||
@@ -12,22 +12,16 @@ function validateKeyAndCerts({ cert, key, keyFile, crtFile }) {
|
|||||||
let encrypted;
|
let encrypted;
|
||||||
try {
|
try {
|
||||||
// publicEncrypt will throw an error with an invalid cert
|
// publicEncrypt will throw an error with an invalid cert
|
||||||
encrypted = crypto.publicEncrypt(cert, Buffer.from('test'));
|
encrypted = crypto.publicEncrypt(cert, Buffer.from("test"));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(
|
throw new Error(`The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}`);
|
||||||
`The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// privateDecrypt will throw an error with an invalid key
|
// privateDecrypt will throw an error with an invalid key
|
||||||
crypto.privateDecrypt(key, encrypted);
|
crypto.privateDecrypt(key, encrypted);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(
|
throw new Error(`The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${err.message}`);
|
||||||
`The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${
|
|
||||||
err.message
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,9 +29,9 @@ function validateKeyAndCerts({ cert, key, keyFile, crtFile }) {
|
|||||||
function readEnvFile(file, type) {
|
function readEnvFile(file, type) {
|
||||||
if (!fs.existsSync(file)) {
|
if (!fs.existsSync(file)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`You specified ${chalk.cyan(
|
`You specified ${chalk.cyan(type)} in your env, but the file "${chalk.yellow(
|
||||||
type
|
file
|
||||||
)} in your env, but the file "${chalk.yellow(file)}" can't be found.`
|
)}" can't be found.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return fs.readFileSync(file);
|
return fs.readFileSync(file);
|
||||||
@@ -47,14 +41,14 @@ function readEnvFile(file, type) {
|
|||||||
// Return cert files if provided in env, otherwise just true or false
|
// Return cert files if provided in env, otherwise just true or false
|
||||||
function getHttpsConfig() {
|
function getHttpsConfig() {
|
||||||
const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env;
|
const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env;
|
||||||
const isHttps = HTTPS === 'true';
|
const isHttps = HTTPS === "true";
|
||||||
|
|
||||||
if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) {
|
if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) {
|
||||||
const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE);
|
const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE);
|
||||||
const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE);
|
const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE);
|
||||||
const config = {
|
const config = {
|
||||||
cert: readEnvFile(crtFile, 'SSL_CRT_FILE'),
|
cert: readEnvFile(crtFile, "SSL_CRT_FILE"),
|
||||||
key: readEnvFile(keyFile, 'SSL_KEY_FILE'),
|
key: readEnvFile(keyFile, "SSL_KEY_FILE")
|
||||||
};
|
};
|
||||||
|
|
||||||
validateKeyAndCerts({ ...config, keyFile, crtFile });
|
validateKeyAndCerts({ ...config, keyFile, crtFile });
|
||||||
|
|||||||
+17
-18
@@ -1,10 +1,10 @@
|
|||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require("fs");
|
||||||
const path = require('path');
|
const path = require("path");
|
||||||
const paths = require('./paths');
|
const paths = require("./paths");
|
||||||
const chalk = require('react-dev-utils/chalk');
|
const chalk = require("react-dev-utils/chalk");
|
||||||
const resolve = require('resolve');
|
const resolve = require("resolve");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get additional module paths based on the baseUrl of a compilerOptions object.
|
* Get additional module paths based on the baseUrl of a compilerOptions object.
|
||||||
@@ -15,19 +15,19 @@ function getAdditionalModulePaths(options = {}) {
|
|||||||
const baseUrl = options.baseUrl;
|
const baseUrl = options.baseUrl;
|
||||||
|
|
||||||
if (!baseUrl) {
|
if (!baseUrl) {
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||||
|
|
||||||
// We don't need to do anything if `baseUrl` is set to `node_modules`. This is
|
// We don't need to do anything if `baseUrl` is set to `node_modules`. This is
|
||||||
// the default behavior.
|
// the default behavior.
|
||||||
if (path.relative(paths.appNodeModules, baseUrlResolved) === '') {
|
if (path.relative(paths.appNodeModules, baseUrlResolved) === "") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow the user set the `baseUrl` to `appSrc`.
|
// Allow the user set the `baseUrl` to `appSrc`.
|
||||||
if (path.relative(paths.appSrc, baseUrlResolved) === '') {
|
if (path.relative(paths.appSrc, baseUrlResolved) === "") {
|
||||||
return [paths.appSrc];
|
return [paths.appSrc];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ function getAdditionalModulePaths(options = {}) {
|
|||||||
// not transpiled outside of `src`. We do allow importing them with the
|
// not transpiled outside of `src`. We do allow importing them with the
|
||||||
// absolute path (e.g. `src/Components/Button.js`) but we set that up with
|
// absolute path (e.g. `src/Components/Button.js`) but we set that up with
|
||||||
// an alias.
|
// an alias.
|
||||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
if (path.relative(paths.appPath, baseUrlResolved) === "") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ function getAdditionalModulePaths(options = {}) {
|
|||||||
throw new Error(
|
throw new Error(
|
||||||
chalk.red.bold(
|
chalk.red.bold(
|
||||||
"Your project's `baseUrl` can only be set to `src` or `node_modules`." +
|
"Your project's `baseUrl` can only be set to `src` or `node_modules`." +
|
||||||
' Create React App does not support other values at this time.'
|
" Create React App does not support other values at this time."
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -63,14 +63,13 @@ function getWebpackAliases(options = {}) {
|
|||||||
|
|
||||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||||
|
|
||||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
if (path.relative(paths.appPath, baseUrlResolved) === "") {
|
||||||
return {
|
return {
|
||||||
src: paths.appSrc,
|
src: paths.appSrc
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getModules() {
|
function getModules() {
|
||||||
// Check if TypeScript is setup
|
// Check if TypeScript is setup
|
||||||
const hasTsConfig = fs.existsSync(paths.appTsConfig);
|
const hasTsConfig = fs.existsSync(paths.appTsConfig);
|
||||||
@@ -78,7 +77,7 @@ function getModules() {
|
|||||||
|
|
||||||
if (hasTsConfig && hasJsConfig) {
|
if (hasTsConfig && hasJsConfig) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.'
|
"You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,8 +87,8 @@ function getModules() {
|
|||||||
// TypeScript project and set up the config
|
// TypeScript project and set up the config
|
||||||
// based on tsconfig.json
|
// based on tsconfig.json
|
||||||
if (hasTsConfig) {
|
if (hasTsConfig) {
|
||||||
const ts = require(resolve.sync('typescript', {
|
const ts = require(resolve.sync("typescript", {
|
||||||
basedir: paths.appNodeModules,
|
basedir: paths.appNodeModules
|
||||||
}));
|
}));
|
||||||
config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config;
|
config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config;
|
||||||
// Otherwise we'll check if there is jsconfig.json
|
// Otherwise we'll check if there is jsconfig.json
|
||||||
@@ -106,7 +105,7 @@ function getModules() {
|
|||||||
return {
|
return {
|
||||||
additionalModulePaths: additionalModulePaths,
|
additionalModulePaths: additionalModulePaths,
|
||||||
webpackAliases: getWebpackAliases(options),
|
webpackAliases: getWebpackAliases(options),
|
||||||
hasTsConfig,
|
hasTsConfig
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+37
-39
@@ -1,13 +1,13 @@
|
|||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
const path = require('path');
|
const path = require("path");
|
||||||
const fs = require('fs');
|
const fs = require("fs");
|
||||||
const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');
|
const getPublicUrlOrPath = require("react-dev-utils/getPublicUrlOrPath");
|
||||||
|
|
||||||
// Make sure any symlinks in the project folder are resolved:
|
// Make sure any symlinks in the project folder are resolved:
|
||||||
// https://github.com/facebook/create-react-app/issues/637
|
// https://github.com/facebook/create-react-app/issues/637
|
||||||
const appDirectory = fs.realpathSync(process.cwd());
|
const appDirectory = fs.realpathSync(process.cwd());
|
||||||
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
|
const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);
|
||||||
|
|
||||||
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
|
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
|
||||||
// "public path" at which the app is served.
|
// "public path" at which the app is served.
|
||||||
@@ -16,30 +16,30 @@ const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
|
|||||||
// We can't use a relative path in HTML because we don't want to load something
|
// We can't use a relative path in HTML because we don't want to load something
|
||||||
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
|
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
|
||||||
const publicUrlOrPath = getPublicUrlOrPath(
|
const publicUrlOrPath = getPublicUrlOrPath(
|
||||||
process.env.NODE_ENV === 'development',
|
process.env.NODE_ENV === "development",
|
||||||
require(resolveApp('package.json')).homepage,
|
require(resolveApp("package.json")).homepage,
|
||||||
process.env.PUBLIC_URL
|
process.env.PUBLIC_URL
|
||||||
);
|
);
|
||||||
|
|
||||||
const buildPath = process.env.BUILD_PATH || 'build';
|
const buildPath = process.env.BUILD_PATH || "build";
|
||||||
|
|
||||||
const moduleFileExtensions = [
|
const moduleFileExtensions = [
|
||||||
'web.mjs',
|
"web.mjs",
|
||||||
'mjs',
|
"mjs",
|
||||||
'web.js',
|
"web.js",
|
||||||
'js',
|
"js",
|
||||||
'web.ts',
|
"web.ts",
|
||||||
'ts',
|
"ts",
|
||||||
'web.tsx',
|
"web.tsx",
|
||||||
'tsx',
|
"tsx",
|
||||||
'json',
|
"json",
|
||||||
'web.jsx',
|
"web.jsx",
|
||||||
'jsx',
|
"jsx"
|
||||||
];
|
];
|
||||||
|
|
||||||
// Resolve file paths in the same order as webpack
|
// Resolve file paths in the same order as webpack
|
||||||
const resolveModule = (resolveFn, filePath) => {
|
const resolveModule = (resolveFn, filePath) => {
|
||||||
const extension = moduleFileExtensions.find(extension =>
|
const extension = moduleFileExtensions.find((extension) =>
|
||||||
fs.existsSync(resolveFn(`${filePath}.${extension}`))
|
fs.existsSync(resolveFn(`${filePath}.${extension}`))
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -52,26 +52,24 @@ const resolveModule = (resolveFn, filePath) => {
|
|||||||
|
|
||||||
// config after eject: we're in ./config/
|
// config after eject: we're in ./config/
|
||||||
module.exports = {
|
module.exports = {
|
||||||
dotenv: resolveApp('.env'),
|
dotenv: resolveApp(".env"),
|
||||||
appPath: resolveApp('.'),
|
appPath: resolveApp("."),
|
||||||
appBuild: resolveApp(buildPath),
|
appBuild: resolveApp(buildPath),
|
||||||
appPublic: resolveApp('public'),
|
appPublic: resolveApp("public"),
|
||||||
appHtml: resolveApp('public/index.html'),
|
appHtml: resolveApp("public/index.html"),
|
||||||
appIndexJs: resolveModule(resolveApp, 'src/index'),
|
appIndexJs: resolveModule(resolveApp, "src/index"),
|
||||||
appPackageJson: resolveApp('package.json'),
|
appPackageJson: resolveApp("package.json"),
|
||||||
appSrc: resolveApp('src'),
|
appSrc: resolveApp("src"),
|
||||||
appTsConfig: resolveApp('tsconfig.json'),
|
appTsConfig: resolveApp("tsconfig.json"),
|
||||||
appJsConfig: resolveApp('jsconfig.json'),
|
appJsConfig: resolveApp("jsconfig.json"),
|
||||||
yarnLockFile: resolveApp('yarn.lock'),
|
yarnLockFile: resolveApp("yarn.lock"),
|
||||||
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
|
testsSetup: resolveModule(resolveApp, "src/setupTests"),
|
||||||
proxySetup: resolveApp('src/setupProxy.js'),
|
proxySetup: resolveApp("src/setupProxy.js"),
|
||||||
appNodeModules: resolveApp('node_modules'),
|
appNodeModules: resolveApp("node_modules"),
|
||||||
appWebpackCache: resolveApp('node_modules/.cache'),
|
appWebpackCache: resolveApp("node_modules/.cache"),
|
||||||
appTsBuildInfoFile: resolveApp('node_modules/.cache/tsconfig.tsbuildinfo'),
|
appTsBuildInfoFile: resolveApp("node_modules/.cache/tsconfig.tsbuildinfo"),
|
||||||
swSrc: resolveModule(resolveApp, 'src/service-worker'),
|
swSrc: resolveModule(resolveApp, "src/service-worker"),
|
||||||
publicUrlOrPath,
|
publicUrlOrPath
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports.moduleFileExtensions = moduleFileExtensions;
|
module.exports.moduleFileExtensions = moduleFileExtensions;
|
||||||
|
|||||||
+68
-90
@@ -33,16 +33,14 @@ const babelRuntimeEntryHelpers = require.resolve(
|
|||||||
{ paths: [babelRuntimeEntry] }
|
{ paths: [babelRuntimeEntry] }
|
||||||
);
|
);
|
||||||
const babelRuntimeRegenerator = require.resolve("@babel/runtime/regenerator", {
|
const babelRuntimeRegenerator = require.resolve("@babel/runtime/regenerator", {
|
||||||
paths: [babelRuntimeEntry],
|
paths: [babelRuntimeEntry]
|
||||||
});
|
});
|
||||||
|
|
||||||
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
|
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
|
||||||
// makes for a smoother build process.
|
// makes for a smoother build process.
|
||||||
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== "false";
|
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== "false";
|
||||||
|
|
||||||
const imageInlineSizeLimit = parseInt(
|
const imageInlineSizeLimit = parseInt(process.env.IMAGE_INLINE_SIZE_LIMIT || "10000");
|
||||||
process.env.IMAGE_INLINE_SIZE_LIMIT || "10000"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check if TypeScript is setup
|
// Check if TypeScript is setup
|
||||||
const useTypeScript = fs.existsSync(paths.appTsConfig);
|
const useTypeScript = fs.existsSync(paths.appTsConfig);
|
||||||
@@ -75,8 +73,7 @@ module.exports = function (webpackEnv) {
|
|||||||
|
|
||||||
// Variable used for enabling profiling in Production
|
// Variable used for enabling profiling in Production
|
||||||
// passed into alias object. Uses a flag if passed into the build command
|
// passed into alias object. Uses a flag if passed into the build command
|
||||||
const isEnvProductionProfile =
|
const isEnvProductionProfile = isEnvProduction && process.argv.includes("--profile");
|
||||||
isEnvProduction && process.argv.includes("--profile");
|
|
||||||
|
|
||||||
// We will provide `paths.publicUrlOrPath` to our app
|
// We will provide `paths.publicUrlOrPath` to our app
|
||||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||||
@@ -94,14 +91,12 @@ module.exports = function (webpackEnv) {
|
|||||||
loader: MiniCssExtractPlugin.loader,
|
loader: MiniCssExtractPlugin.loader,
|
||||||
// css is located in `static/css`, use '../../' to locate index.html folder
|
// css is located in `static/css`, use '../../' to locate index.html folder
|
||||||
// in production `paths.publicUrlOrPath` can be a relative path
|
// in production `paths.publicUrlOrPath` can be a relative path
|
||||||
options: paths.publicUrlOrPath.startsWith(".")
|
options: paths.publicUrlOrPath.startsWith(".") ? { publicPath: "../../" } : {}
|
||||||
? { publicPath: "../../" }
|
|
||||||
: {},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
loader: require.resolve("css-loader"),
|
loader: require.resolve("css-loader"),
|
||||||
options: cssOptions,
|
options: cssOptions
|
||||||
},
|
}
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
return loaders;
|
return loaders;
|
||||||
};
|
};
|
||||||
@@ -140,13 +135,9 @@ module.exports = function (webpackEnv) {
|
|||||||
publicPath: paths.publicUrlOrPath,
|
publicPath: paths.publicUrlOrPath,
|
||||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||||
devtoolModuleFilenameTemplate: isEnvProduction
|
devtoolModuleFilenameTemplate: isEnvProduction
|
||||||
? (info) =>
|
? (info) => path.relative(paths.appSrc, info.absoluteResourcePath).replace(/\\/g, "/")
|
||||||
path
|
|
||||||
.relative(paths.appSrc, info.absoluteResourcePath)
|
|
||||||
.replace(/\\/g, "/")
|
|
||||||
: isEnvDevelopment &&
|
: isEnvDevelopment &&
|
||||||
((info) =>
|
((info) => path.resolve(info.absoluteResourcePath).replace(/\\/g, "/"))
|
||||||
path.resolve(info.absoluteResourcePath).replace(/\\/g, "/")),
|
|
||||||
},
|
},
|
||||||
cache: {
|
cache: {
|
||||||
type: "filesystem",
|
type: "filesystem",
|
||||||
@@ -156,13 +147,11 @@ module.exports = function (webpackEnv) {
|
|||||||
buildDependencies: {
|
buildDependencies: {
|
||||||
defaultWebpack: ["webpack/lib/"],
|
defaultWebpack: ["webpack/lib/"],
|
||||||
config: [__filename],
|
config: [__filename],
|
||||||
tsconfig: [paths.appTsConfig, paths.appJsConfig].filter((f) =>
|
tsconfig: [paths.appTsConfig, paths.appJsConfig].filter((f) => fs.existsSync(f))
|
||||||
fs.existsSync(f)
|
}
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
infrastructureLogging: {
|
infrastructureLogging: {
|
||||||
level: "none",
|
level: "none"
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
minimize: isEnvProduction,
|
minimize: isEnvProduction,
|
||||||
@@ -176,7 +165,7 @@ module.exports = function (webpackEnv) {
|
|||||||
// into invalid ecma 5 code. This is why the 'compress' and 'output'
|
// into invalid ecma 5 code. This is why the 'compress' and 'output'
|
||||||
// sections only apply transformations that are ecma 5 safe
|
// sections only apply transformations that are ecma 5 safe
|
||||||
// https://github.com/facebook/create-react-app/pull/4234
|
// https://github.com/facebook/create-react-app/pull/4234
|
||||||
ecma: 8,
|
ecma: 8
|
||||||
},
|
},
|
||||||
compress: {
|
compress: {
|
||||||
drop_console: true,
|
drop_console: true,
|
||||||
@@ -191,10 +180,10 @@ module.exports = function (webpackEnv) {
|
|||||||
// https://github.com/facebook/create-react-app/issues/5250
|
// https://github.com/facebook/create-react-app/issues/5250
|
||||||
// Pending further investigation:
|
// Pending further investigation:
|
||||||
// https://github.com/terser-js/terser/issues/120
|
// https://github.com/terser-js/terser/issues/120
|
||||||
inline: 2,
|
inline: 2
|
||||||
},
|
},
|
||||||
mangle: {
|
mangle: {
|
||||||
safari10: true,
|
safari10: true
|
||||||
},
|
},
|
||||||
// Added for profiling in devtools
|
// Added for profiling in devtools
|
||||||
keep_classnames: isEnvProductionProfile,
|
keep_classnames: isEnvProductionProfile,
|
||||||
@@ -204,22 +193,20 @@ module.exports = function (webpackEnv) {
|
|||||||
comments: false,
|
comments: false,
|
||||||
// Turned on because emoji and regex is not minified properly using default
|
// Turned on because emoji and regex is not minified properly using default
|
||||||
// https://github.com/facebook/create-react-app/issues/2488
|
// https://github.com/facebook/create-react-app/issues/2488
|
||||||
ascii_only: true,
|
ascii_only: true
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
// This is only used in production mode
|
// This is only used in production mode
|
||||||
new CssMinimizerPlugin(),
|
new CssMinimizerPlugin()
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
// This allows you to set a fallback for where webpack should look for modules.
|
// This allows you to set a fallback for where webpack should look for modules.
|
||||||
// We placed these paths second because we want `node_modules` to "win"
|
// We placed these paths second because we want `node_modules` to "win"
|
||||||
// if there are any conflicts. This matches Node resolution mechanism.
|
// if there are any conflicts. This matches Node resolution mechanism.
|
||||||
// https://github.com/facebook/create-react-app/issues/253
|
// https://github.com/facebook/create-react-app/issues/253
|
||||||
modules: ["node_modules", paths.appNodeModules].concat(
|
modules: ["node_modules", paths.appNodeModules].concat(modules.additionalModulePaths || []),
|
||||||
modules.additionalModulePaths || []
|
|
||||||
),
|
|
||||||
// These are the reasonable defaults supported by the Node ecosystem.
|
// These are the reasonable defaults supported by the Node ecosystem.
|
||||||
// We also include JSX as a common component filename extension to support
|
// We also include JSX as a common component filename extension to support
|
||||||
// some tools, although we do not recommend using it, see:
|
// some tools, although we do not recommend using it, see:
|
||||||
@@ -233,9 +220,9 @@ module.exports = function (webpackEnv) {
|
|||||||
// Allows for better profiling with ReactDevTools
|
// Allows for better profiling with ReactDevTools
|
||||||
...(isEnvProductionProfile && {
|
...(isEnvProductionProfile && {
|
||||||
"react-dom$": "react-dom/profiling",
|
"react-dom$": "react-dom/profiling",
|
||||||
"scheduler/tracing": "scheduler/tracing-profiling",
|
"scheduler/tracing": "scheduler/tracing-profiling"
|
||||||
}),
|
}),
|
||||||
...(modules.webpackAliases || {}),
|
...(modules.webpackAliases || {})
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||||
@@ -249,9 +236,9 @@ module.exports = function (webpackEnv) {
|
|||||||
reactRefreshWebpackPluginRuntimeEntry,
|
reactRefreshWebpackPluginRuntimeEntry,
|
||||||
babelRuntimeEntry,
|
babelRuntimeEntry,
|
||||||
babelRuntimeEntryHelpers,
|
babelRuntimeEntryHelpers,
|
||||||
babelRuntimeRegenerator,
|
babelRuntimeRegenerator
|
||||||
]),
|
])
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
strictExportPresence: true,
|
strictExportPresence: true,
|
||||||
@@ -261,7 +248,7 @@ module.exports = function (webpackEnv) {
|
|||||||
enforce: "pre",
|
enforce: "pre",
|
||||||
exclude: /@babel(?:\/|\\{1,2})runtime/,
|
exclude: /@babel(?:\/|\\{1,2})runtime/,
|
||||||
test: /\.(js|mjs|jsx|ts|tsx|css)$/,
|
test: /\.(js|mjs|jsx|ts|tsx|css)$/,
|
||||||
loader: require.resolve("source-map-loader"),
|
loader: require.resolve("source-map-loader")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// "oneOf" will traverse all following loaders until one will
|
// "oneOf" will traverse all following loaders until one will
|
||||||
@@ -276,9 +263,9 @@ module.exports = function (webpackEnv) {
|
|||||||
mimetype: "image/avif",
|
mimetype: "image/avif",
|
||||||
parser: {
|
parser: {
|
||||||
dataUrlCondition: {
|
dataUrlCondition: {
|
||||||
maxSize: imageInlineSizeLimit,
|
maxSize: imageInlineSizeLimit
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
// "url" loader works like "file" loader except that it embeds assets
|
// "url" loader works like "file" loader except that it embeds assets
|
||||||
// smaller than specified limit in bytes as data URLs to avoid requests.
|
// smaller than specified limit in bytes as data URLs to avoid requests.
|
||||||
@@ -288,9 +275,9 @@ module.exports = function (webpackEnv) {
|
|||||||
type: "asset",
|
type: "asset",
|
||||||
parser: {
|
parser: {
|
||||||
dataUrlCondition: {
|
dataUrlCondition: {
|
||||||
maxSize: imageInlineSizeLimit,
|
maxSize: imageInlineSizeLimit
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.svg$/,
|
test: /\.svg$/,
|
||||||
@@ -301,17 +288,17 @@ module.exports = function (webpackEnv) {
|
|||||||
prettier: false,
|
prettier: false,
|
||||||
svgo: false,
|
svgo: false,
|
||||||
svgoConfig: {
|
svgoConfig: {
|
||||||
plugins: [{ removeViewBox: false }],
|
plugins: [{ removeViewBox: false }]
|
||||||
},
|
},
|
||||||
titleProp: true,
|
titleProp: true,
|
||||||
ref: true,
|
ref: true
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
],
|
],
|
||||||
issuer: {
|
issuer: {
|
||||||
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
|
and: [/\.(ts|tsx|js|jsx|md|mdx)$/]
|
||||||
},
|
},
|
||||||
resourceQuery: { not: [/url/] }, // exclude react component if *.svg?url
|
resourceQuery: { not: [/url/] } // exclude react component if *.svg?url
|
||||||
},
|
},
|
||||||
// Process application JS with Babel.
|
// Process application JS with Babel.
|
||||||
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
|
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
|
||||||
@@ -320,22 +307,20 @@ module.exports = function (webpackEnv) {
|
|||||||
include: paths.appSrc,
|
include: paths.appSrc,
|
||||||
loader: require.resolve("babel-loader"),
|
loader: require.resolve("babel-loader"),
|
||||||
options: {
|
options: {
|
||||||
customize: require.resolve(
|
customize: require.resolve("babel-preset-react-app/webpack-overrides"),
|
||||||
"babel-preset-react-app/webpack-overrides"
|
|
||||||
),
|
|
||||||
presets: [
|
presets: [
|
||||||
[
|
[
|
||||||
require.resolve("babel-preset-react-app"),
|
require.resolve("babel-preset-react-app"),
|
||||||
{
|
{
|
||||||
runtime: hasJsxRuntime ? "automatic" : "classic",
|
runtime: hasJsxRuntime ? "automatic" : "classic"
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
isEnvDevelopment &&
|
isEnvDevelopment &&
|
||||||
shouldUseReactRefresh &&
|
shouldUseReactRefresh &&
|
||||||
require.resolve("react-refresh/babel"),
|
require.resolve("react-refresh/babel")
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
// This is a feature of `babel-loader` for webpack (not Babel itself).
|
// This is a feature of `babel-loader` for webpack (not Babel itself).
|
||||||
// It enables caching results in ./node_modules/.cache/babel-loader/
|
// It enables caching results in ./node_modules/.cache/babel-loader/
|
||||||
@@ -343,8 +328,8 @@ module.exports = function (webpackEnv) {
|
|||||||
cacheDirectory: true,
|
cacheDirectory: true,
|
||||||
// See #6846 for context on why cacheCompression is disabled
|
// See #6846 for context on why cacheCompression is disabled
|
||||||
cacheCompression: false,
|
cacheCompression: false,
|
||||||
compact: isEnvProduction,
|
compact: isEnvProduction
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
// Process any JS outside of the app with Babel.
|
// Process any JS outside of the app with Babel.
|
||||||
// Unlike the application JS, we only compile the standard ES features.
|
// Unlike the application JS, we only compile the standard ES features.
|
||||||
@@ -357,10 +342,7 @@ module.exports = function (webpackEnv) {
|
|||||||
configFile: false,
|
configFile: false,
|
||||||
compact: false,
|
compact: false,
|
||||||
presets: [
|
presets: [
|
||||||
[
|
[require.resolve("babel-preset-react-app/dependencies"), { helpers: true }]
|
||||||
require.resolve("babel-preset-react-app/dependencies"),
|
|
||||||
{ helpers: true },
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
cacheDirectory: true,
|
cacheDirectory: true,
|
||||||
// See #6846 for context on why cacheCompression is disabled
|
// See #6846 for context on why cacheCompression is disabled
|
||||||
@@ -370,8 +352,8 @@ module.exports = function (webpackEnv) {
|
|||||||
// code. Without the options below, debuggers like VSCode
|
// code. Without the options below, debuggers like VSCode
|
||||||
// show incorrect code and set breakpoints on the wrong lines.
|
// show incorrect code and set breakpoints on the wrong lines.
|
||||||
sourceMaps: shouldUseSourceMap,
|
sourceMaps: shouldUseSourceMap,
|
||||||
inputSourceMap: shouldUseSourceMap,
|
inputSourceMap: shouldUseSourceMap
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||||
// "style" loader turns CSS into JS modules that inject <style> tags.
|
// "style" loader turns CSS into JS modules that inject <style> tags.
|
||||||
@@ -384,18 +366,16 @@ module.exports = function (webpackEnv) {
|
|||||||
exclude: cssModuleRegex,
|
exclude: cssModuleRegex,
|
||||||
use: getStyleLoaders({
|
use: getStyleLoaders({
|
||||||
importLoaders: 1,
|
importLoaders: 1,
|
||||||
sourceMap: isEnvProduction
|
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
|
||||||
? shouldUseSourceMap
|
|
||||||
: isEnvDevelopment,
|
|
||||||
modules: {
|
modules: {
|
||||||
mode: "icss",
|
mode: "icss"
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
// Don't consider CSS imports dead code even if the
|
// Don't consider CSS imports dead code even if the
|
||||||
// containing package claims to have no side effects.
|
// containing package claims to have no side effects.
|
||||||
// Remove this when webpack adds a warning or an error for this.
|
// Remove this when webpack adds a warning or an error for this.
|
||||||
// See https://github.com/webpack/webpack/issues/6571
|
// See https://github.com/webpack/webpack/issues/6571
|
||||||
sideEffects: true,
|
sideEffects: true
|
||||||
},
|
},
|
||||||
// "file" loader makes sure those assets get served by WebpackDevServer.
|
// "file" loader makes sure those assets get served by WebpackDevServer.
|
||||||
// When you `import` an asset, you get its (virtual) filename.
|
// When you `import` an asset, you get its (virtual) filename.
|
||||||
@@ -408,13 +388,13 @@ module.exports = function (webpackEnv) {
|
|||||||
// Also exclude `html` and `json` extensions so they get processed
|
// Also exclude `html` and `json` extensions so they get processed
|
||||||
// by webpacks internal loaders.
|
// by webpacks internal loaders.
|
||||||
exclude: [/^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
|
exclude: [/^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
|
||||||
type: "asset/resource",
|
type: "asset/resource"
|
||||||
},
|
}
|
||||||
// ** STOP ** Are you adding a new loader?
|
// ** STOP ** Are you adding a new loader?
|
||||||
// Make sure to add the new loader(s) before the "file" loader.
|
// Make sure to add the new loader(s) before the "file" loader.
|
||||||
],
|
]
|
||||||
},
|
}
|
||||||
].filter(Boolean),
|
].filter(Boolean)
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
// Generates an `index.html` file with the <script> injected.
|
// Generates an `index.html` file with the <script> injected.
|
||||||
@@ -423,7 +403,7 @@ module.exports = function (webpackEnv) {
|
|||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
inject: true,
|
inject: true,
|
||||||
template: paths.appHtml,
|
template: paths.appHtml
|
||||||
},
|
},
|
||||||
isEnvProduction
|
isEnvProduction
|
||||||
? {
|
? {
|
||||||
@@ -437,8 +417,8 @@ module.exports = function (webpackEnv) {
|
|||||||
keepClosingSlash: true,
|
keepClosingSlash: true,
|
||||||
minifyJS: true,
|
minifyJS: true,
|
||||||
minifyCSS: true,
|
minifyCSS: true,
|
||||||
minifyURLs: true,
|
minifyURLs: true
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
)
|
)
|
||||||
@@ -469,7 +449,7 @@ module.exports = function (webpackEnv) {
|
|||||||
isEnvDevelopment &&
|
isEnvDevelopment &&
|
||||||
shouldUseReactRefresh &&
|
shouldUseReactRefresh &&
|
||||||
new ReactRefreshWebpackPlugin({
|
new ReactRefreshWebpackPlugin({
|
||||||
overlay: false,
|
overlay: false
|
||||||
}),
|
}),
|
||||||
// Watcher doesn't work well if you mistype casing in a path so we use
|
// Watcher doesn't work well if you mistype casing in a path so we use
|
||||||
// a plugin that prints an error when you attempt to do this.
|
// a plugin that prints an error when you attempt to do this.
|
||||||
@@ -480,7 +460,7 @@ module.exports = function (webpackEnv) {
|
|||||||
// Options similar to the same options in webpackOptions.output
|
// Options similar to the same options in webpackOptions.output
|
||||||
// both options are optional
|
// both options are optional
|
||||||
filename: "static/css/[name].[contenthash:8].css",
|
filename: "static/css/[name].[contenthash:8].css",
|
||||||
chunkFilename: "static/css/[name].[contenthash:8].chunk.css",
|
chunkFilename: "static/css/[name].[contenthash:8].chunk.css"
|
||||||
}),
|
}),
|
||||||
// Generate an asset manifest file with the following content:
|
// Generate an asset manifest file with the following content:
|
||||||
// - "files" key: Mapping of all asset filenames to their corresponding
|
// - "files" key: Mapping of all asset filenames to their corresponding
|
||||||
@@ -496,15 +476,13 @@ module.exports = function (webpackEnv) {
|
|||||||
manifest[file.name] = file.path;
|
manifest[file.name] = file.path;
|
||||||
return manifest;
|
return manifest;
|
||||||
}, seed);
|
}, seed);
|
||||||
const entrypointFiles = entrypoints.main.filter(
|
const entrypointFiles = entrypoints.main.filter((fileName) => !fileName.endsWith(".map"));
|
||||||
(fileName) => !fileName.endsWith(".map")
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
files: manifestFiles,
|
files: manifestFiles,
|
||||||
entrypoints: entrypointFiles,
|
entrypoints: entrypointFiles
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
// Generate a service worker script that will precache, and keep up to date,
|
// Generate a service worker script that will precache, and keep up to date,
|
||||||
// the HTML & assets that are part of the webpack build.
|
// the HTML & assets that are part of the webpack build.
|
||||||
@@ -517,11 +495,11 @@ module.exports = function (webpackEnv) {
|
|||||||
// Bump up the default maximum size (2mb) that's precached,
|
// Bump up the default maximum size (2mb) that's precached,
|
||||||
// to make lazy-loading failure scenarios less likely.
|
// to make lazy-loading failure scenarios less likely.
|
||||||
// See https://github.com/cra-template/pwa/issues/13#issuecomment-722667270
|
// See https://github.com/cra-template/pwa/issues/13#issuecomment-722667270
|
||||||
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
|
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024
|
||||||
}),
|
})
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
// Turn off performance processing because we utilize
|
// Turn off performance processing because we utilize
|
||||||
// our own hints via the FileSizeReporter
|
// our own hints via the FileSizeReporter
|
||||||
performance: false,
|
performance: false
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
'use strict';
|
"use strict";
|
||||||
const { createHash } = require('crypto');
|
const { createHash } = require("crypto");
|
||||||
|
|
||||||
module.exports = env => {
|
module.exports = (env) => {
|
||||||
const hash = createHash('md5');
|
const hash = createHash("md5");
|
||||||
hash.update(JSON.stringify(env));
|
hash.update(JSON.stringify(env));
|
||||||
|
return hash.digest("hex");
|
||||||
return hash.digest('hex');
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require("fs");
|
||||||
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
|
const evalSourceMapMiddleware = require("react-dev-utils/evalSourceMapMiddleware");
|
||||||
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
|
const noopServiceWorkerMiddleware = require("react-dev-utils/noopServiceWorkerMiddleware");
|
||||||
const ignoredFiles = require('react-dev-utils/ignoredFiles');
|
const ignoredFiles = require("react-dev-utils/ignoredFiles");
|
||||||
const redirectServedPath = require('react-dev-utils/redirectServedPathMiddleware');
|
const redirectServedPath = require("react-dev-utils/redirectServedPathMiddleware");
|
||||||
const paths = require('./paths');
|
const paths = require("./paths");
|
||||||
const getHttpsConfig = require('./getHttpsConfig');
|
const getHttpsConfig = require("./getHttpsConfig");
|
||||||
|
|
||||||
const host = process.env.HOST || '0.0.0.0';
|
const host = process.env.HOST || "0.0.0.0";
|
||||||
const sockHost = process.env.WDS_SOCKET_HOST;
|
const sockHost = process.env.WDS_SOCKET_HOST;
|
||||||
const sockPath = process.env.WDS_SOCKET_PATH; // default: '/ws'
|
const sockPath = process.env.WDS_SOCKET_PATH; // default: '/ws'
|
||||||
const sockPort = process.env.WDS_SOCKET_PORT;
|
const sockPort = process.env.WDS_SOCKET_PORT;
|
||||||
|
|
||||||
module.exports = function (proxy, allowedHost) {
|
module.exports = function (proxy, allowedHost) {
|
||||||
const disableFirewall =
|
const disableFirewall = !proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === "true";
|
||||||
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true';
|
|
||||||
return {
|
return {
|
||||||
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
|
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
|
||||||
// websites from potentially accessing local content through DNS rebinding:
|
// websites from potentially accessing local content through DNS rebinding:
|
||||||
@@ -35,11 +34,11 @@ module.exports = function (proxy, allowedHost) {
|
|||||||
// really know what you're doing with a special environment variable.
|
// really know what you're doing with a special environment variable.
|
||||||
// Note: ["localhost", ".localhost"] will support subdomains - but we might
|
// Note: ["localhost", ".localhost"] will support subdomains - but we might
|
||||||
// want to allow setting the allowedHosts manually for more complex setups
|
// want to allow setting the allowedHosts manually for more complex setups
|
||||||
allowedHosts: disableFirewall ? 'all' : [allowedHost],
|
allowedHosts: disableFirewall ? "all" : [allowedHost],
|
||||||
headers: {
|
headers: {
|
||||||
'Access-Control-Allow-Origin': '*',
|
"Access-Control-Allow-Origin": "*",
|
||||||
'Access-Control-Allow-Methods': '*',
|
"Access-Control-Allow-Methods": "*",
|
||||||
'Access-Control-Allow-Headers': '*',
|
"Access-Control-Allow-Headers": "*"
|
||||||
},
|
},
|
||||||
// Enable gzip compression of generated files.
|
// Enable gzip compression of generated files.
|
||||||
compress: true,
|
compress: true,
|
||||||
@@ -66,8 +65,8 @@ module.exports = function (proxy, allowedHost) {
|
|||||||
// https://github.com/facebook/create-react-app/issues/293
|
// https://github.com/facebook/create-react-app/issues/293
|
||||||
// src/node_modules is not ignored to support absolute imports
|
// src/node_modules is not ignored to support absolute imports
|
||||||
// https://github.com/facebook/create-react-app/issues/1065
|
// https://github.com/facebook/create-react-app/issues/1065
|
||||||
ignored: ignoredFiles(paths.appSrc),
|
ignored: ignoredFiles(paths.appSrc)
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
client: {
|
client: {
|
||||||
webSocketURL: {
|
webSocketURL: {
|
||||||
@@ -76,19 +75,19 @@ module.exports = function (proxy, allowedHost) {
|
|||||||
// to hot reloading server.
|
// to hot reloading server.
|
||||||
hostname: sockHost,
|
hostname: sockHost,
|
||||||
pathname: sockPath,
|
pathname: sockPath,
|
||||||
port: sockPort,
|
port: sockPort
|
||||||
},
|
},
|
||||||
overlay: {
|
overlay: {
|
||||||
errors: true,
|
errors: true,
|
||||||
warnings: false,
|
warnings: false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
devMiddleware: {
|
devMiddleware: {
|
||||||
// It is important to tell WebpackDevServer to use the same "publicPath" path as
|
// It is important to tell WebpackDevServer to use the same "publicPath" path as
|
||||||
// we specified in the webpack config. When homepage is '.', default to serving
|
// we specified in the webpack config. When homepage is '.', default to serving
|
||||||
// from the root.
|
// from the root.
|
||||||
// remove last slash so user can land on `/test` instead of `/test/`
|
// remove last slash so user can land on `/test` instead of `/test/`
|
||||||
publicPath: paths.publicUrlOrPath.slice(0, -1),
|
publicPath: paths.publicUrlOrPath.slice(0, -1)
|
||||||
},
|
},
|
||||||
|
|
||||||
https: getHttpsConfig(),
|
https: getHttpsConfig(),
|
||||||
@@ -97,7 +96,7 @@ module.exports = function (proxy, allowedHost) {
|
|||||||
// Paths with dots should still use the history fallback.
|
// Paths with dots should still use the history fallback.
|
||||||
// See https://github.com/facebook/create-react-app/issues/387.
|
// See https://github.com/facebook/create-react-app/issues/387.
|
||||||
disableDotRule: true,
|
disableDotRule: true,
|
||||||
index: paths.publicUrlOrPath,
|
index: paths.publicUrlOrPath
|
||||||
},
|
},
|
||||||
// `proxy` is run between `before` and `after` `webpack-dev-server` hooks
|
// `proxy` is run between `before` and `after` `webpack-dev-server` hooks
|
||||||
proxy,
|
proxy,
|
||||||
@@ -122,6 +121,6 @@ module.exports = function (proxy, allowedHost) {
|
|||||||
// it used the same host and port.
|
// it used the same host and port.
|
||||||
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
|
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
|
||||||
devServer.app.use(noopServiceWorkerMiddleware(paths.publicUrlOrPath));
|
devServer.app.use(noopServiceWorkerMiddleware(paths.publicUrlOrPath));
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ self.addEventListener("notificationclick", function (event) {
|
|||||||
event.waitUntil(
|
event.waitUntil(
|
||||||
(async function () {
|
(async function () {
|
||||||
const allClients = await clients.matchAll({
|
const allClients = await clients.matchAll({
|
||||||
includeUncontrolled: true,
|
includeUncontrolled: true
|
||||||
});
|
});
|
||||||
const [firstClient] = allClients;
|
const [firstClient] = allClients;
|
||||||
// 没有数据
|
// 没有数据
|
||||||
@@ -15,12 +15,7 @@ self.addEventListener("notificationclick", function (event) {
|
|||||||
firstClient.focus();
|
firstClient.focus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {
|
const { rustchat_from_uid, rustchat_to_uid, rustchat_to_gid } = customData;
|
||||||
rustchat_from_uid,
|
|
||||||
rustchat_to_uid,
|
|
||||||
rustchat_to_gid,
|
|
||||||
} = customData;
|
|
||||||
|
|
||||||
let chatClient;
|
let chatClient;
|
||||||
let redirectPath = rustchat_to_uid
|
let redirectPath = rustchat_to_uid
|
||||||
? `/chat/dm/${rustchat_from_uid}`
|
? `/chat/dm/${rustchat_from_uid}`
|
||||||
@@ -47,9 +42,7 @@ self.addEventListener("notificationclick", function (event) {
|
|||||||
// importScripts(
|
// importScripts(
|
||||||
// "https://www.gstatic.com/firebasejs/9.8.1/firebase-messaging-compat.js"
|
// "https://www.gstatic.com/firebasejs/9.8.1/firebase-messaging-compat.js"
|
||||||
// );
|
// );
|
||||||
importScripts(
|
importScripts("https://cdnjs.cloudflare.com/ajax/libs/firebase/9.8.1/firebase-app-compat.min.js");
|
||||||
"https://cdnjs.cloudflare.com/ajax/libs/firebase/9.8.1/firebase-app-compat.min.js"
|
|
||||||
);
|
|
||||||
importScripts(
|
importScripts(
|
||||||
"https://cdnjs.cloudflare.com/ajax/libs/firebase/9.8.1/firebase-messaging-compat.min.js"
|
"https://cdnjs.cloudflare.com/ajax/libs/firebase/9.8.1/firebase-messaging-compat.min.js"
|
||||||
);
|
);
|
||||||
@@ -62,7 +55,7 @@ const firebaseConfig = {
|
|||||||
storageBucket: "rustchat-develop.appspot.com",
|
storageBucket: "rustchat-develop.appspot.com",
|
||||||
messagingSenderId: "418687074928",
|
messagingSenderId: "418687074928",
|
||||||
appId: "1:418687074928:web:753286adbf239f5af9eab5",
|
appId: "1:418687074928:web:753286adbf239f5af9eab5",
|
||||||
measurementId: "G-XV476KEC8P",
|
measurementId: "G-XV476KEC8P"
|
||||||
};
|
};
|
||||||
|
|
||||||
firebase.initializeApp(firebaseConfig);
|
firebase.initializeApp(firebaseConfig);
|
||||||
|
|||||||
+8
-21
@@ -27,8 +27,7 @@ const printHostingInstructions = require("react-dev-utils/printHostingInstructio
|
|||||||
const FileSizeReporter = require("react-dev-utils/FileSizeReporter");
|
const FileSizeReporter = require("react-dev-utils/FileSizeReporter");
|
||||||
const printBuildError = require("react-dev-utils/printBuildError");
|
const printBuildError = require("react-dev-utils/printBuildError");
|
||||||
|
|
||||||
const measureFileSizesBeforeBuild =
|
const measureFileSizesBeforeBuild = FileSizeReporter.measureFileSizesBeforeBuild;
|
||||||
FileSizeReporter.measureFileSizesBeforeBuild;
|
|
||||||
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
|
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
|
||||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
const useYarn = fs.existsSync(paths.yarnLockFile);
|
||||||
|
|
||||||
@@ -78,9 +77,7 @@ checkBrowsers(paths.appPath, isInteractive)
|
|||||||
" to learn more about each warning."
|
" to learn more about each warning."
|
||||||
);
|
);
|
||||||
console.log(
|
console.log(
|
||||||
"To ignore, add " +
|
"To ignore, add " + chalk.cyan("// eslint-disable-next-line") + " to the line before.\n"
|
||||||
chalk.cyan("// eslint-disable-next-line") +
|
|
||||||
" to the line before.\n"
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
console.log(chalk.green("Compiled successfully.\n"));
|
console.log(chalk.green("Compiled successfully.\n"));
|
||||||
@@ -98,18 +95,9 @@ checkBrowsers(paths.appPath, isInteractive)
|
|||||||
const publicUrl = paths.publicUrlOrPath;
|
const publicUrl = paths.publicUrlOrPath;
|
||||||
const publicPath = config.output.publicPath;
|
const publicPath = config.output.publicPath;
|
||||||
const buildFolder = path.relative(process.cwd(), paths.appBuild);
|
const buildFolder = path.relative(process.cwd(), paths.appBuild);
|
||||||
printHostingInstructions(
|
printHostingInstructions(appPackage, publicUrl, publicPath, buildFolder, useYarn);
|
||||||
appPackage,
|
|
||||||
publicUrl,
|
|
||||||
publicPath,
|
|
||||||
buildFolder,
|
|
||||||
useYarn
|
|
||||||
);
|
|
||||||
// version and md5 files
|
// version and md5 files
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(`${buildFolder}/VERSION`, require("../package.json").version);
|
||||||
`${buildFolder}/VERSION`,
|
|
||||||
require("../package.json").version
|
|
||||||
);
|
|
||||||
const hash = md5File.sync(`${buildFolder}/VERSION`);
|
const hash = md5File.sync(`${buildFolder}/VERSION`);
|
||||||
fs.writeFileSync(`${buildFolder}/web.rustchat.md5`, hash);
|
fs.writeFileSync(`${buildFolder}/web.rustchat.md5`, hash);
|
||||||
},
|
},
|
||||||
@@ -153,7 +141,7 @@ function build(previousFileSizes) {
|
|||||||
|
|
||||||
messages = formatWebpackMessages({
|
messages = formatWebpackMessages({
|
||||||
errors: [errMessage],
|
errors: [errMessage],
|
||||||
warnings: [],
|
warnings: []
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
messages = formatWebpackMessages(
|
messages = formatWebpackMessages(
|
||||||
@@ -170,8 +158,7 @@ function build(previousFileSizes) {
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
process.env.CI &&
|
process.env.CI &&
|
||||||
(typeof process.env.CI !== "string" ||
|
(typeof process.env.CI !== "string" || process.env.CI.toLowerCase() !== "false") &&
|
||||||
process.env.CI.toLowerCase() !== "false") &&
|
|
||||||
messages.warnings.length
|
messages.warnings.length
|
||||||
) {
|
) {
|
||||||
// Ignore sourcemap warnings in CI builds. See #8227 for more info.
|
// Ignore sourcemap warnings in CI builds. See #8227 for more info.
|
||||||
@@ -192,7 +179,7 @@ function build(previousFileSizes) {
|
|||||||
const resolveArgs = {
|
const resolveArgs = {
|
||||||
stats,
|
stats,
|
||||||
previousFileSizes,
|
previousFileSizes,
|
||||||
warnings: messages.warnings,
|
warnings: messages.warnings
|
||||||
};
|
};
|
||||||
|
|
||||||
if (writeStatsJson) {
|
if (writeStatsJson) {
|
||||||
@@ -210,6 +197,6 @@ function build(previousFileSizes) {
|
|||||||
function copyPublicFolder() {
|
function copyPublicFolder() {
|
||||||
fs.copySync(paths.appPublic, paths.appBuild, {
|
fs.copySync(paths.appPublic, paths.appBuild, {
|
||||||
dereference: true,
|
dereference: true,
|
||||||
filter: (file) => file !== paths.appHtml,
|
filter: (file) => file !== paths.appHtml
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+109
-108
@@ -1,38 +1,37 @@
|
|||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
// Do this as the first thing so that any code reading it knows the right env.
|
// Do this as the first thing so that any code reading it knows the right env.
|
||||||
process.env.BABEL_ENV = 'development';
|
process.env.BABEL_ENV = "development";
|
||||||
process.env.NODE_ENV = 'development';
|
process.env.NODE_ENV = "development";
|
||||||
|
|
||||||
// Makes the script crash on unhandled rejections instead of silently
|
// Makes the script crash on unhandled rejections instead of silently
|
||||||
// ignoring them. In the future, promise rejections that are not handled will
|
// ignoring them. In the future, promise rejections that are not handled will
|
||||||
// terminate the Node.js process with a non-zero exit code.
|
// terminate the Node.js process with a non-zero exit code.
|
||||||
process.on('unhandledRejection', (err) => {
|
process.on("unhandledRejection", (err) => {
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ensure environment variables are read.
|
// Ensure environment variables are read.
|
||||||
require('../config/env');
|
require("../config/env");
|
||||||
|
const fs = require("fs");
|
||||||
const fs = require('fs');
|
const chalk = require("react-dev-utils/chalk");
|
||||||
const chalk = require('react-dev-utils/chalk');
|
const webpack = require("webpack");
|
||||||
const webpack = require('webpack');
|
const WebpackDevServer = require("webpack-dev-server");
|
||||||
const WebpackDevServer = require('webpack-dev-server');
|
const clearConsole = require("react-dev-utils/clearConsole");
|
||||||
const clearConsole = require('react-dev-utils/clearConsole');
|
const checkRequiredFiles = require("react-dev-utils/checkRequiredFiles");
|
||||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
|
||||||
const {
|
const {
|
||||||
choosePort,
|
choosePort,
|
||||||
createCompiler,
|
createCompiler,
|
||||||
prepareProxy,
|
prepareProxy,
|
||||||
prepareUrls
|
prepareUrls
|
||||||
} = require('react-dev-utils/WebpackDevServerUtils');
|
} = require("react-dev-utils/WebpackDevServerUtils");
|
||||||
const openBrowser = require('react-dev-utils/openBrowser');
|
const openBrowser = require("react-dev-utils/openBrowser");
|
||||||
const semver = require('semver');
|
const semver = require("semver");
|
||||||
const paths = require('../config/paths');
|
const paths = require("../config/paths");
|
||||||
const configFactory = require('../config/webpack.config');
|
const configFactory = require("../config/webpack.config");
|
||||||
const createDevServerConfig = require('../config/webpackDevServer.config');
|
const createDevServerConfig = require("../config/webpackDevServer.config");
|
||||||
const getClientEnvironment = require('../config/env');
|
const getClientEnvironment = require("../config/env");
|
||||||
const react = require(require.resolve('react', { paths: [paths.appPath] }));
|
const react = require(require.resolve("react", { paths: [paths.appPath] }));
|
||||||
|
|
||||||
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
|
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
|
||||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
const useYarn = fs.existsSync(paths.yarnLockFile);
|
||||||
@@ -40,100 +39,102 @@ const isInteractive = process.stdout.isTTY;
|
|||||||
|
|
||||||
// Warn and crash if required files are missing
|
// Warn and crash if required files are missing
|
||||||
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
|
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tools like Cloud9 rely on this.
|
// Tools like Cloud9 rely on this.
|
||||||
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3009;
|
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3009;
|
||||||
const HOST = process.env.HOST || '0.0.0.0';
|
const HOST = process.env.HOST || "0.0.0.0";
|
||||||
|
|
||||||
if (process.env.HOST) {
|
if (process.env.HOST) {
|
||||||
console.log(
|
console.log(
|
||||||
chalk.cyan(
|
chalk.cyan(
|
||||||
`Attempting to bind to HOST environment variable: ${chalk.yellow(chalk.bold(process.env.HOST))}`
|
`Attempting to bind to HOST environment variable: ${chalk.yellow(
|
||||||
)
|
chalk.bold(process.env.HOST)
|
||||||
);
|
)}`
|
||||||
console.log(`If this was unintentional, check that you haven't mistakenly set it in your shell.`);
|
)
|
||||||
console.log(`Learn more here: ${chalk.yellow('https://cra.link/advanced-config')}`);
|
);
|
||||||
console.log();
|
console.log(`If this was unintentional, check that you haven't mistakenly set it in your shell.`);
|
||||||
|
console.log(`Learn more here: ${chalk.yellow("https://cra.link/advanced-config")}`);
|
||||||
|
console.log();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We require that you explicitly set browsers and do not fall back to
|
// We require that you explicitly set browsers and do not fall back to
|
||||||
// browserslist defaults.
|
// browserslist defaults.
|
||||||
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
|
const { checkBrowsers } = require("react-dev-utils/browsersHelper");
|
||||||
checkBrowsers(paths.appPath, isInteractive)
|
checkBrowsers(paths.appPath, isInteractive)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// We attempt to use the default port but if it is busy, we offer the user to
|
// We attempt to use the default port but if it is busy, we offer the user to
|
||||||
// run on a different port. `choosePort()` Promise resolves to the next free port.
|
// run on a different port. `choosePort()` Promise resolves to the next free port.
|
||||||
return choosePort(HOST, DEFAULT_PORT);
|
return choosePort(HOST, DEFAULT_PORT);
|
||||||
})
|
})
|
||||||
.then((port) => {
|
.then((port) => {
|
||||||
if (port == null) {
|
if (port == null) {
|
||||||
// We have not found a port.
|
// We have not found a port.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = configFactory('development');
|
const config = configFactory("development");
|
||||||
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
|
const protocol = process.env.HTTPS === "true" ? "https" : "http";
|
||||||
const appName = require(paths.appPackageJson).name;
|
const appName = require(paths.appPackageJson).name;
|
||||||
|
|
||||||
const useTypeScript = fs.existsSync(paths.appTsConfig);
|
const useTypeScript = fs.existsSync(paths.appTsConfig);
|
||||||
const urls = prepareUrls(protocol, HOST, port, paths.publicUrlOrPath.slice(0, -1));
|
const urls = prepareUrls(protocol, HOST, port, paths.publicUrlOrPath.slice(0, -1));
|
||||||
// Create a webpack compiler that is configured with custom messages.
|
// Create a webpack compiler that is configured with custom messages.
|
||||||
const compiler = createCompiler({
|
const compiler = createCompiler({
|
||||||
appName,
|
appName,
|
||||||
config,
|
config,
|
||||||
urls,
|
urls,
|
||||||
useYarn,
|
useYarn,
|
||||||
useTypeScript,
|
useTypeScript,
|
||||||
webpack
|
webpack
|
||||||
|
});
|
||||||
|
// Load proxy config
|
||||||
|
const proxySetting = require(paths.appPackageJson).proxy;
|
||||||
|
const proxyConfig = prepareProxy(proxySetting, paths.appPublic, paths.publicUrlOrPath);
|
||||||
|
// Serve webpack assets generated by the compiler over a web server.
|
||||||
|
const serverConfig = {
|
||||||
|
...createDevServerConfig(proxyConfig, urls.lanUrlForConfig),
|
||||||
|
host: HOST,
|
||||||
|
port
|
||||||
|
};
|
||||||
|
const devServer = new WebpackDevServer(serverConfig, compiler);
|
||||||
|
// Launch WebpackDevServer.
|
||||||
|
devServer.startCallback(() => {
|
||||||
|
if (isInteractive) {
|
||||||
|
clearConsole();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (env.raw.FAST_REFRESH && semver.lt(react.version, "16.10.0")) {
|
||||||
|
console.log(
|
||||||
|
chalk.yellow(
|
||||||
|
`Fast Refresh requires React 16.10 or higher. You are using React ${react.version}.`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(chalk.cyan("Starting the development server...\n"));
|
||||||
|
openBrowser(urls.localUrlForBrowser);
|
||||||
|
});
|
||||||
|
|
||||||
|
["SIGINT", "SIGTERM"].forEach(function (sig) {
|
||||||
|
process.on(sig, function () {
|
||||||
|
devServer.close();
|
||||||
|
process.exit();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (process.env.CI !== "true") {
|
||||||
|
// Gracefully exit when stdin ends
|
||||||
|
process.stdin.on("end", function () {
|
||||||
|
devServer.close();
|
||||||
|
process.exit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if (err && err.message) {
|
||||||
|
console.log(err.message);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
});
|
});
|
||||||
// Load proxy config
|
|
||||||
const proxySetting = require(paths.appPackageJson).proxy;
|
|
||||||
const proxyConfig = prepareProxy(proxySetting, paths.appPublic, paths.publicUrlOrPath);
|
|
||||||
// Serve webpack assets generated by the compiler over a web server.
|
|
||||||
const serverConfig = {
|
|
||||||
...createDevServerConfig(proxyConfig, urls.lanUrlForConfig),
|
|
||||||
host: HOST,
|
|
||||||
port
|
|
||||||
};
|
|
||||||
const devServer = new WebpackDevServer(serverConfig, compiler);
|
|
||||||
// Launch WebpackDevServer.
|
|
||||||
devServer.startCallback(() => {
|
|
||||||
if (isInteractive) {
|
|
||||||
clearConsole();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.raw.FAST_REFRESH && semver.lt(react.version, '16.10.0')) {
|
|
||||||
console.log(
|
|
||||||
chalk.yellow(
|
|
||||||
`Fast Refresh requires React 16.10 or higher. You are using React ${react.version}.`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(chalk.cyan('Starting the development server...\n'));
|
|
||||||
openBrowser(urls.localUrlForBrowser);
|
|
||||||
});
|
|
||||||
|
|
||||||
['SIGINT', 'SIGTERM'].forEach(function (sig) {
|
|
||||||
process.on(sig, function () {
|
|
||||||
devServer.close();
|
|
||||||
process.exit();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (process.env.CI !== 'true') {
|
|
||||||
// Gracefully exit when stdin ends
|
|
||||||
process.stdin.on('end', function () {
|
|
||||||
devServer.close();
|
|
||||||
process.exit();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
if (err && err.message) {
|
|
||||||
console.log(err.message);
|
|
||||||
}
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
|
|||||||
Vendored
+12
-12
@@ -4,44 +4,44 @@ import { KEY_UID, CACHE_VERSION } from "../config";
|
|||||||
const tables = [
|
const tables = [
|
||||||
{
|
{
|
||||||
storeName: "channels",
|
storeName: "channels",
|
||||||
description: "store channel list",
|
description: "store channel list"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
storeName: "contacts",
|
storeName: "contacts",
|
||||||
description: "store contact list",
|
description: "store contact list"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
storeName: "messageDM",
|
storeName: "messageDM",
|
||||||
description: "store DM message with IDs",
|
description: "store DM message with IDs"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
storeName: "messageChannel",
|
storeName: "messageChannel",
|
||||||
description: "store channel message with IDs",
|
description: "store channel message with IDs"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
storeName: "message",
|
storeName: "message",
|
||||||
description: "store message with key-val full data",
|
description: "store message with key-val full data"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
storeName: "messageFile",
|
storeName: "messageFile",
|
||||||
description: "store file message list refs",
|
description: "store file message list refs"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
storeName: "messageReaction",
|
storeName: "messageReaction",
|
||||||
description: "store message reaction with key-val full data",
|
description: "store message reaction with key-val full data"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
storeName: "footprint",
|
storeName: "footprint",
|
||||||
description: "store user visit data",
|
description: "store user visit data"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
storeName: "server",
|
storeName: "server",
|
||||||
description: "store server data",
|
description: "store server data"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
storeName: "ui",
|
storeName: "ui",
|
||||||
description: "store UI state",
|
description: "store UI state"
|
||||||
},
|
}
|
||||||
// {
|
// {
|
||||||
// storeName: "message",
|
// storeName: "message",
|
||||||
// description: "store message with key-val full data",
|
// description: "store message with key-val full data",
|
||||||
@@ -55,7 +55,7 @@ const initCache = () => {
|
|||||||
window.CACHE[storeName] = localforage.createInstance({
|
window.CACHE[storeName] = localforage.createInstance({
|
||||||
name,
|
name,
|
||||||
storeName,
|
storeName,
|
||||||
description,
|
description
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Vendored
+1
-1
@@ -25,7 +25,7 @@ const useRehydrate = () => {
|
|||||||
message: { replying: {} },
|
message: { replying: {} },
|
||||||
footprint: {},
|
footprint: {},
|
||||||
ui: {},
|
ui: {},
|
||||||
server: {},
|
server: {}
|
||||||
};
|
};
|
||||||
const tables = Object.keys(window.CACHE);
|
const tables = Object.keys(window.CACHE);
|
||||||
const results = await Promise.all(
|
const results = await Promise.all(
|
||||||
|
|||||||
+4
-4
@@ -7,7 +7,7 @@ export const ContentTypes = {
|
|||||||
file: "rustchat/file",
|
file: "rustchat/file",
|
||||||
formData: "multipart/form-data",
|
formData: "multipart/form-data",
|
||||||
json: "application/json",
|
json: "application/json",
|
||||||
archive: "rustchat/archive",
|
archive: "rustchat/archive"
|
||||||
};
|
};
|
||||||
export const firebaseConfig = {
|
export const firebaseConfig = {
|
||||||
apiKey: "AIzaSyDyJ6B1Ouenoha_gdGkBwIkBNStlwhlbO0",
|
apiKey: "AIzaSyDyJ6B1Ouenoha_gdGkBwIkBNStlwhlbO0",
|
||||||
@@ -16,11 +16,11 @@ export const firebaseConfig = {
|
|||||||
storageBucket: "rustchat-develop.appspot.com",
|
storageBucket: "rustchat-develop.appspot.com",
|
||||||
messagingSenderId: "418687074928",
|
messagingSenderId: "418687074928",
|
||||||
appId: "1:418687074928:web:753286adbf239f5af9eab5",
|
appId: "1:418687074928:web:753286adbf239f5af9eab5",
|
||||||
measurementId: "G-XV476KEC8P",
|
measurementId: "G-XV476KEC8P"
|
||||||
};
|
};
|
||||||
export const ChatPrefixs = {
|
export const ChatPrefixs = {
|
||||||
channel: "#",
|
channel: "#",
|
||||||
user: "@",
|
user: "@"
|
||||||
};
|
};
|
||||||
export const vapidKey = `BGXCn-5YRXSFw38Q9lUKJ5bibL212-yIQn1pCvthGhp6_KwA29FO1Ax_d_7if1vfC2a5wTSVO8AcZrc-Hm1aS0Y`;
|
export const vapidKey = `BGXCn-5YRXSFw38Q9lUKJ5bibL212-yIQn1pCvthGhp6_KwA29FO1Ax_d_7if1vfC2a5wTSVO8AcZrc-Hm1aS0Y`;
|
||||||
// "840319286941-6ds7lbvk55eq8mjortf68cb2ll65lprt.apps.googleusercontent.com";
|
// "840319286941-6ds7lbvk55eq8mjortf68cb2ll65lprt.apps.googleusercontent.com";
|
||||||
@@ -38,6 +38,6 @@ export const KEY_PWA_INSTALLED = "RUSTCHAT_PWA_INSTALLED";
|
|||||||
export const Emojis = ["👍", "❤️", "😄", "👀", "👎", "🎉", "🙁", "🚀"];
|
export const Emojis = ["👍", "❤️", "😄", "👀", "👎", "🎉", "🙁", "🚀"];
|
||||||
export const Views = {
|
export const Views = {
|
||||||
item: "item",
|
item: "item",
|
||||||
grid: "grid",
|
grid: "grid"
|
||||||
};
|
};
|
||||||
export default BASE_URL;
|
export default BASE_URL;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const operations = [
|
|||||||
"message",
|
"message",
|
||||||
"ui",
|
"ui",
|
||||||
"footprint",
|
"footprint",
|
||||||
"server",
|
"server"
|
||||||
];
|
];
|
||||||
|
|
||||||
// Create the middleware instance and methods
|
// Create the middleware instance and methods
|
||||||
@@ -51,7 +51,7 @@ listenerMiddleware.startListening({
|
|||||||
rtkqHandler({
|
rtkqHandler({
|
||||||
operation,
|
operation,
|
||||||
payload,
|
payload,
|
||||||
dispatch: listenerApi.dispatch,
|
dispatch: listenerApi.dispatch
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -60,7 +60,7 @@ listenerMiddleware.startListening({
|
|||||||
await channelsHandler({
|
await channelsHandler({
|
||||||
operation,
|
operation,
|
||||||
payload,
|
payload,
|
||||||
data: state,
|
data: state
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -69,7 +69,7 @@ listenerMiddleware.startListening({
|
|||||||
await contactsHandler({
|
await contactsHandler({
|
||||||
operation,
|
operation,
|
||||||
payload,
|
payload,
|
||||||
data: state,
|
data: state
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -78,7 +78,7 @@ listenerMiddleware.startListening({
|
|||||||
await channelMsgHandler({
|
await channelMsgHandler({
|
||||||
operation,
|
operation,
|
||||||
payload,
|
payload,
|
||||||
data: state,
|
data: state
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -87,7 +87,7 @@ listenerMiddleware.startListening({
|
|||||||
await dmMsgHandler({
|
await dmMsgHandler({
|
||||||
operation,
|
operation,
|
||||||
payload,
|
payload,
|
||||||
data: state,
|
data: state
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -96,7 +96,7 @@ listenerMiddleware.startListening({
|
|||||||
await fileMessageHandler({
|
await fileMessageHandler({
|
||||||
operation,
|
operation,
|
||||||
// payload,
|
// payload,
|
||||||
data: state,
|
data: state
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -105,7 +105,7 @@ listenerMiddleware.startListening({
|
|||||||
await messageHandler({
|
await messageHandler({
|
||||||
operation,
|
operation,
|
||||||
payload,
|
payload,
|
||||||
data: state,
|
data: state
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -114,7 +114,7 @@ listenerMiddleware.startListening({
|
|||||||
await reactionHandler({
|
await reactionHandler({
|
||||||
operation,
|
operation,
|
||||||
payload,
|
payload,
|
||||||
data: state,
|
data: state
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -123,7 +123,7 @@ listenerMiddleware.startListening({
|
|||||||
await footprintHandler({
|
await footprintHandler({
|
||||||
operation,
|
operation,
|
||||||
payload,
|
payload,
|
||||||
data: state,
|
data: state
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -132,7 +132,7 @@ listenerMiddleware.startListening({
|
|||||||
await UIHandler({
|
await UIHandler({
|
||||||
operation,
|
operation,
|
||||||
payload,
|
payload,
|
||||||
data: state,
|
data: state
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -141,7 +141,7 @@ listenerMiddleware.startListening({
|
|||||||
await serverHandler({
|
await serverHandler({
|
||||||
operation,
|
operation,
|
||||||
payload,
|
payload,
|
||||||
data: state,
|
data: state
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -149,7 +149,7 @@ listenerMiddleware.startListening({
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default listenerMiddleware;
|
export default listenerMiddleware;
|
||||||
|
|||||||
+146
-146
@@ -4,157 +4,157 @@ import baseQuery from "./base.query";
|
|||||||
import { setAuthData, updateToken, resetAuthData, updateInitialized } from "../slices/auth.data";
|
import { setAuthData, updateToken, resetAuthData, updateInitialized } from "../slices/auth.data";
|
||||||
import BASE_URL, { KEY_DEVICE_KEY } from "../config";
|
import BASE_URL, { KEY_DEVICE_KEY } from "../config";
|
||||||
const getDeviceId = () => {
|
const getDeviceId = () => {
|
||||||
let d = localStorage.getItem(KEY_DEVICE_KEY);
|
let d = localStorage.getItem(KEY_DEVICE_KEY);
|
||||||
if (!d) {
|
if (!d) {
|
||||||
d = `web:${nanoid()}`;
|
d = `web:${nanoid()}`;
|
||||||
localStorage.setItem(KEY_DEVICE_KEY, d);
|
localStorage.setItem(KEY_DEVICE_KEY, d);
|
||||||
}
|
}
|
||||||
return d;
|
return d;
|
||||||
};
|
};
|
||||||
export const authApi = createApi({
|
export const authApi = createApi({
|
||||||
reducerPath: "authApi",
|
reducerPath: "authApi",
|
||||||
baseQuery,
|
baseQuery,
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
login: builder.mutation({
|
login: builder.mutation({
|
||||||
query: (credentials) => ({
|
query: (credentials) => ({
|
||||||
url: "token/login",
|
url: "token/login",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: {
|
body: {
|
||||||
credential: credentials,
|
credential: credentials,
|
||||||
device: getDeviceId(),
|
device: getDeviceId(),
|
||||||
device_token: "test"
|
device_token: "test"
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
transformResponse: (data) => {
|
transformResponse: (data) => {
|
||||||
const { avatar_updated_at } = data.user;
|
const { avatar_updated_at } = data.user;
|
||||||
data.user.avatar =
|
data.user.avatar =
|
||||||
avatar_updated_at == 0
|
avatar_updated_at == 0
|
||||||
? ""
|
? ""
|
||||||
: `${BASE_URL}/resource/avatar?uid=${data.user.uid}&t=${avatar_updated_at}`;
|
: `${BASE_URL}/resource/avatar?uid=${data.user.uid}&t=${avatar_updated_at}`;
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
||||||
try {
|
try {
|
||||||
const { data } = await queryFulfilled;
|
const { data } = await queryFulfilled;
|
||||||
if (data) {
|
if (data) {
|
||||||
console.log("login resp", data);
|
console.log("login resp", data);
|
||||||
dispatch(setAuthData(data));
|
dispatch(setAuthData(data));
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
console.log("login error");
|
console.log("login error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
// 更新token
|
// 更新token
|
||||||
renew: builder.mutation({
|
renew: builder.mutation({
|
||||||
query: ({ token, refreshToken }) => ({
|
query: ({ token, refreshToken }) => ({
|
||||||
url: "/token/renew",
|
url: "/token/renew",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: {
|
body: {
|
||||||
token,
|
token,
|
||||||
refresh_token: refreshToken
|
refresh_token: refreshToken
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
||||||
try {
|
try {
|
||||||
const { data } = await queryFulfilled;
|
const { data } = await queryFulfilled;
|
||||||
dispatch(updateToken(data));
|
dispatch(updateToken(data));
|
||||||
} catch {
|
} catch {
|
||||||
dispatch(resetAuthData());
|
dispatch(resetAuthData());
|
||||||
console.log("renew token error");
|
console.log("renew token error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
// 更新 device token
|
// 更新 device token
|
||||||
updateDeviceToken: builder.mutation({
|
updateDeviceToken: builder.mutation({
|
||||||
query: (device_token) => ({
|
query: (device_token) => ({
|
||||||
url: "/token/device_token",
|
url: "/token/device_token",
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: {
|
body: {
|
||||||
device_token
|
device_token
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
// 获取openid
|
// 获取openid
|
||||||
getOpenid: builder.mutation({
|
getOpenid: builder.mutation({
|
||||||
query: ({ issuer, redirect_uri }) => ({
|
query: ({ issuer, redirect_uri }) => ({
|
||||||
url: "/token/openid/authorize",
|
url: "/token/openid/authorize",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: {
|
body: {
|
||||||
issuer,
|
issuer,
|
||||||
redirect_uri
|
redirect_uri
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
|
||||||
checkInviteTokenValid: builder.mutation({
|
checkInviteTokenValid: builder.mutation({
|
||||||
query: (token) => ({
|
query: (token) => ({
|
||||||
url: "user/check_invite_magic_token",
|
url: "user/check_invite_magic_token",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: { magic_token: token }
|
body: { magic_token: token }
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
updatePassword: builder.mutation({
|
updatePassword: builder.mutation({
|
||||||
query: ({ old_password, new_password }) => ({
|
query: ({ old_password, new_password }) => ({
|
||||||
url: "user/change_password",
|
url: "user/change_password",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: { old_password, new_password }
|
body: { old_password, new_password }
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
sendMagicLink: builder.mutation({
|
sendMagicLink: builder.mutation({
|
||||||
query: (email) => ({
|
query: (email) => ({
|
||||||
url: "token/send_magic_link",
|
url: "token/send_magic_link",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: { email }
|
body: { email }
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
getMetamaskNonce: builder.query({
|
getMetamaskNonce: builder.query({
|
||||||
query: (address) => ({
|
query: (address) => ({
|
||||||
url: `/token/metamask/nonce?public_address=${address}`
|
url: `/token/metamask/nonce?public_address=${address}`
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
getCredentials: builder.query({
|
getCredentials: builder.query({
|
||||||
query: () => ({
|
query: () => ({
|
||||||
url: `/token/credentials`
|
url: `/token/credentials`
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
logout: builder.query({
|
logout: builder.query({
|
||||||
query: () => ({ url: `token/logout` }),
|
query: () => ({ url: `token/logout` }),
|
||||||
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
||||||
try {
|
try {
|
||||||
await queryFulfilled;
|
await queryFulfilled;
|
||||||
dispatch(resetAuthData());
|
dispatch(resetAuthData());
|
||||||
} catch {
|
} catch {
|
||||||
console.log("logout error");
|
console.log("logout error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
getInitialized: builder.query({
|
getInitialized: builder.query({
|
||||||
query: () => ({
|
query: () => ({
|
||||||
url: `/admin/system/initialized`
|
url: `/admin/system/initialized`
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
||||||
try {
|
try {
|
||||||
const { data: isInitialized } = await queryFulfilled;
|
const { data: isInitialized } = await queryFulfilled;
|
||||||
dispatch(updateInitialized(isInitialized));
|
dispatch(updateInitialized(isInitialized));
|
||||||
} catch {
|
} catch {
|
||||||
console.log("api initialized error");
|
console.log("api initialized error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
useGetInitializedQuery,
|
useGetInitializedQuery,
|
||||||
useSendMagicLinkMutation,
|
useSendMagicLinkMutation,
|
||||||
useGetCredentialsQuery,
|
useGetCredentialsQuery,
|
||||||
useUpdateDeviceTokenMutation,
|
useUpdateDeviceTokenMutation,
|
||||||
useGetOpenidMutation,
|
useGetOpenidMutation,
|
||||||
useRenewMutation,
|
useRenewMutation,
|
||||||
useLazyGetMetamaskNonceQuery,
|
useLazyGetMetamaskNonceQuery,
|
||||||
useLoginMutation,
|
useLoginMutation,
|
||||||
useLazyLogoutQuery,
|
useLazyLogoutQuery,
|
||||||
useCheckInviteTokenValidMutation,
|
useCheckInviteTokenValidMutation,
|
||||||
useUpdatePasswordMutation
|
useUpdatePasswordMutation
|
||||||
} = authApi;
|
} = authApi;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const whiteList = [
|
|||||||
"getMetamaskNonce",
|
"getMetamaskNonce",
|
||||||
"renew",
|
"renew",
|
||||||
"getInitialized",
|
"getInitialized",
|
||||||
"createAdmin",
|
"createAdmin"
|
||||||
];
|
];
|
||||||
const baseQuery = fetchBaseQuery({
|
const baseQuery = fetchBaseQuery({
|
||||||
baseUrl: BASE_URL,
|
baseUrl: BASE_URL,
|
||||||
@@ -29,7 +29,7 @@ const baseQuery = fetchBaseQuery({
|
|||||||
headers.set(tokenHeader, token);
|
headers.set(tokenHeader, token);
|
||||||
}
|
}
|
||||||
return headers;
|
return headers;
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
let waitingForRenew = null;
|
let waitingForRenew = null;
|
||||||
const baseQueryWithTokenCheck = async (args, api, extraOptions) => {
|
const baseQueryWithTokenCheck = async (args, api, extraOptions) => {
|
||||||
@@ -37,16 +37,9 @@ const baseQueryWithTokenCheck = async (args, api, extraOptions) => {
|
|||||||
await waitingForRenew;
|
await waitingForRenew;
|
||||||
}
|
}
|
||||||
// 先检查token是否过期,过期则renew
|
// 先检查token是否过期,过期则renew
|
||||||
const {
|
const { token, refreshToken, expireTime = +new Date() } = api.getState().authData;
|
||||||
token,
|
|
||||||
refreshToken,
|
|
||||||
expireTime = +new Date(),
|
|
||||||
} = api.getState().authData;
|
|
||||||
let result = null;
|
let result = null;
|
||||||
if (
|
if (!whiteList.includes(api.endpoint) && dayjs().isAfter(new Date(expireTime - 20 * 1000))) {
|
||||||
!whiteList.includes(api.endpoint) &&
|
|
||||||
dayjs().isAfter(new Date(expireTime - 20 * 1000))
|
|
||||||
) {
|
|
||||||
// 快过期了,renew
|
// 快过期了,renew
|
||||||
waitingForRenew = baseQuery(
|
waitingForRenew = baseQuery(
|
||||||
{
|
{
|
||||||
@@ -54,8 +47,8 @@ const baseQueryWithTokenCheck = async (args, api, extraOptions) => {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
body: {
|
body: {
|
||||||
token,
|
token,
|
||||||
refresh_token: refreshToken,
|
refresh_token: refreshToken
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
api,
|
api,
|
||||||
extraOptions
|
extraOptions
|
||||||
|
|||||||
+31
-34
@@ -15,10 +15,10 @@ export const channelApi = createApi({
|
|||||||
refetchOnFocus: true,
|
refetchOnFocus: true,
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
getChannels: builder.query({
|
getChannels: builder.query({
|
||||||
query: () => ({ url: `group` }),
|
query: () => ({ url: `group` })
|
||||||
}),
|
}),
|
||||||
getChannel: builder.query({
|
getChannel: builder.query({
|
||||||
query: (id) => ({ url: `group/${id}` }),
|
query: (id) => ({ url: `group/${id}` })
|
||||||
}),
|
}),
|
||||||
leaveChannel: builder.query({
|
leaveChannel: builder.query({
|
||||||
query: (id) => ({ url: `group/${id}/leave` }),
|
query: (id) => ({ url: `group/${id}/leave` }),
|
||||||
@@ -29,25 +29,22 @@ export const channelApi = createApi({
|
|||||||
} catch {
|
} catch {
|
||||||
console.log("channel update failed");
|
console.log("channel update failed");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
createChannel: builder.mutation({
|
createChannel: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: "group",
|
url: "group",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
updateChannel: builder.mutation({
|
updateChannel: builder.mutation({
|
||||||
query: ({ id, ...data }) => ({
|
query: ({ id, ...data }) => ({
|
||||||
url: `group/${id}`,
|
url: `group/${id}`,
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(
|
async onQueryStarted({ id, name, description }, { dispatch, queryFulfilled }) {
|
||||||
{ id, name, description },
|
|
||||||
{ dispatch, queryFulfilled }
|
|
||||||
) {
|
|
||||||
// id: who send to ,from_uid: who sent
|
// id: who send to ,from_uid: who sent
|
||||||
const patchResult = dispatch(updateChannel({ id, name, description }));
|
const patchResult = dispatch(updateChannel({ id, name, description }));
|
||||||
try {
|
try {
|
||||||
@@ -56,13 +53,13 @@ export const channelApi = createApi({
|
|||||||
console.log("channel update failed");
|
console.log("channel update failed");
|
||||||
patchResult.undo();
|
patchResult.undo();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
getHistoryMessages: builder.query({
|
getHistoryMessages: builder.query({
|
||||||
query: ({ id, mid = null, limit = 100 }) => ({
|
query: ({ id, mid = null, limit = 100 }) => ({
|
||||||
url: mid
|
url: mid
|
||||||
? `/group/${id}/history?before=${mid}&limit=${limit}`
|
? `/group/${id}/history?before=${mid}&limit=${limit}`
|
||||||
: `/group/${id}/history?limit=${limit}`,
|
: `/group/${id}/history?limit=${limit}`
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(params, { dispatch, getState, queryFulfilled }) {
|
async onQueryStarted(params, { dispatch, getState, queryFulfilled }) {
|
||||||
const { data: messages } = await queryFulfilled;
|
const { data: messages } = await queryFulfilled;
|
||||||
@@ -71,34 +68,34 @@ export const channelApi = createApi({
|
|||||||
handleChatMessage(msg, dispatch, getState());
|
handleChatMessage(msg, dispatch, getState());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
createInviteLink: builder.query({
|
createInviteLink: builder.query({
|
||||||
query: (gid) => ({
|
query: (gid) => ({
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "text/plain",
|
"content-type": "text/plain",
|
||||||
accept: "text/plain",
|
accept: "text/plain"
|
||||||
},
|
},
|
||||||
url: `/group/${gid}/create_invite_link`,
|
url: `/group/${gid}/create_invite_link`,
|
||||||
responseHandler: (response) => response.text(),
|
responseHandler: (response) => response.text()
|
||||||
}),
|
}),
|
||||||
transformResponse: (link) => {
|
transformResponse: (link) => {
|
||||||
// 替换掉域名
|
// 替换掉域名
|
||||||
const invite = new URL(link);
|
const invite = new URL(link);
|
||||||
return `${location.origin}${invite.pathname}${invite.search}${invite.hash}`;
|
return `${location.origin}${invite.pathname}${invite.search}${invite.hash}`;
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
removeChannel: builder.query({
|
removeChannel: builder.query({
|
||||||
query: (id) => ({
|
query: (id) => ({
|
||||||
url: `group/${id}`,
|
url: `group/${id}`,
|
||||||
method: "DELETE",
|
method: "DELETE"
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(id, { dispatch, getState, queryFulfilled }) {
|
async onQueryStarted(id, { dispatch, getState, queryFulfilled }) {
|
||||||
const {
|
const {
|
||||||
channelMessage,
|
channelMessage,
|
||||||
ui: {
|
ui: {
|
||||||
remeberedNavs: { chat: remeberedPath },
|
remeberedNavs: { chat: remeberedPath }
|
||||||
},
|
}
|
||||||
} = getState();
|
} = getState();
|
||||||
try {
|
try {
|
||||||
await queryFulfilled;
|
await queryFulfilled;
|
||||||
@@ -115,7 +112,7 @@ export const channelApi = createApi({
|
|||||||
} catch {
|
} catch {
|
||||||
console.log("remove channel error");
|
console.log("remove channel error");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
sendChannelMsg: builder.mutation({
|
sendChannelMsg: builder.mutation({
|
||||||
query: ({ id, content, type = "text", properties = "" }) => ({
|
query: ({ id, content, type = "text", properties = "" }) => ({
|
||||||
@@ -123,38 +120,38 @@ export const channelApi = createApi({
|
|||||||
"content-type": ContentTypes[type],
|
"content-type": ContentTypes[type],
|
||||||
"X-Properties": properties
|
"X-Properties": properties
|
||||||
? btoa(unescape(encodeURIComponent(JSON.stringify(properties))))
|
? btoa(unescape(encodeURIComponent(JSON.stringify(properties))))
|
||||||
: "",
|
: ""
|
||||||
},
|
},
|
||||||
url: `group/${id}/send`,
|
url: `group/${id}/send`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: type == "file" ? JSON.stringify(content) : content,
|
body: type == "file" ? JSON.stringify(content) : content
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(param1, param2) {
|
async onQueryStarted(param1, param2) {
|
||||||
await onMessageSendStarted.call(this, param1, param2, "channel");
|
await onMessageSendStarted.call(this, param1, param2, "channel");
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
addMembers: builder.mutation({
|
addMembers: builder.mutation({
|
||||||
query: ({ id, members }) => ({
|
query: ({ id, members }) => ({
|
||||||
url: `group/${id}/members/add`,
|
url: `group/${id}/members/add`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: members,
|
body: members
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
removeMembers: builder.mutation({
|
removeMembers: builder.mutation({
|
||||||
query: ({ id, members }) => ({
|
query: ({ id, members }) => ({
|
||||||
url: `group/${id}/members/remove`,
|
url: `group/${id}/members/remove`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: members,
|
body: members
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
updateIcon: builder.mutation({
|
updateIcon: builder.mutation({
|
||||||
query: ({ gid, image }) => ({
|
query: ({ gid, image }) => ({
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "image/png",
|
"content-type": "image/png"
|
||||||
},
|
},
|
||||||
url: `/group/${gid}/avatar`,
|
url: `/group/${gid}/avatar`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: image,
|
body: image
|
||||||
}),
|
}),
|
||||||
async onQueryStarted({ gid }, { dispatch, queryFulfilled }) {
|
async onQueryStarted({ gid }, { dispatch, queryFulfilled }) {
|
||||||
try {
|
try {
|
||||||
@@ -162,15 +159,15 @@ export const channelApi = createApi({
|
|||||||
dispatch(
|
dispatch(
|
||||||
updateChannel({
|
updateChannel({
|
||||||
id: gid,
|
id: gid,
|
||||||
icon: `${BASE_URL}/resource/group_avatar?gid=${gid}&t=${+new Date()}`,
|
icon: `${BASE_URL}/resource/group_avatar?gid=${gid}&t=${+new Date()}`
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("err", error);
|
console.log("err", error);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}),
|
})
|
||||||
}),
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
@@ -186,5 +183,5 @@ export const {
|
|||||||
useSendChannelMsgMutation,
|
useSendChannelMsgMutation,
|
||||||
useAddMembersMutation,
|
useAddMembersMutation,
|
||||||
useRemoveMembersMutation,
|
useRemoveMembersMutation,
|
||||||
useUpdateIconMutation,
|
useUpdateIconMutation
|
||||||
} = channelApi;
|
} = channelApi;
|
||||||
|
|||||||
+21
-21
@@ -45,23 +45,23 @@ export const contactApi = createApi({
|
|||||||
} catch {
|
} catch {
|
||||||
console.log("get contact list error");
|
console.log("get contact list error");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
deleteContact: builder.query({
|
deleteContact: builder.query({
|
||||||
query: (uid) => ({ url: `/admin/user/${uid}`, method: "DELETE" }),
|
query: (uid) => ({ url: `/admin/user/${uid}`, method: "DELETE" })
|
||||||
}),
|
}),
|
||||||
updateContact: builder.mutation({
|
updateContact: builder.mutation({
|
||||||
query: ({ id, ...rest }) => ({
|
query: ({ id, ...rest }) => ({
|
||||||
url: `/admin/user/${id}`,
|
url: `/admin/user/${id}`,
|
||||||
body: rest,
|
body: rest,
|
||||||
method: "PUT",
|
method: "PUT"
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
updateMuteSetting: builder.mutation({
|
updateMuteSetting: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: `/user/mute`,
|
url: `/user/mute`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(data, { dispatch, queryFulfilled }) {
|
async onQueryStarted(data, { dispatch, queryFulfilled }) {
|
||||||
try {
|
try {
|
||||||
@@ -70,31 +70,31 @@ export const contactApi = createApi({
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("update mute failed", error);
|
console.log("update mute failed", error);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
updateAvatar: builder.mutation({
|
updateAvatar: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "image/png",
|
"content-type": "image/png"
|
||||||
},
|
},
|
||||||
url: `user/avatar`,
|
url: `user/avatar`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
updateInfo: builder.mutation({
|
updateInfo: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: `user`,
|
url: `user`,
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
register: builder.mutation({
|
register: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: `user/register`,
|
url: `user/register`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
sendMsg: builder.mutation({
|
sendMsg: builder.mutation({
|
||||||
query: ({ id, content, type = "text", properties = "" }) => ({
|
query: ({ id, content, type = "text", properties = "" }) => ({
|
||||||
@@ -102,21 +102,21 @@ export const contactApi = createApi({
|
|||||||
"content-type": ContentTypes[type],
|
"content-type": ContentTypes[type],
|
||||||
"X-Properties": properties
|
"X-Properties": properties
|
||||||
? btoa(unescape(encodeURIComponent(JSON.stringify(properties))))
|
? btoa(unescape(encodeURIComponent(JSON.stringify(properties))))
|
||||||
: "",
|
: ""
|
||||||
},
|
},
|
||||||
url: `user/${id}/send`,
|
url: `user/${id}/send`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: type == "file" ? JSON.stringify(content) : content,
|
body: type == "file" ? JSON.stringify(content) : content
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(param1, param2) {
|
async onQueryStarted(param1, param2) {
|
||||||
await onMessageSendStarted.call(this, param1, param2, "user");
|
await onMessageSendStarted.call(this, param1, param2, "user");
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
getHistoryMessages: builder.query({
|
getHistoryMessages: builder.query({
|
||||||
query: ({ id, mid = null, limit = 100 }) => ({
|
query: ({ id, mid = null, limit = 100 }) => ({
|
||||||
url: mid
|
url: mid
|
||||||
? `/user/${id}/history?before=${mid}&limit=${limit}`
|
? `/user/${id}/history?before=${mid}&limit=${limit}`
|
||||||
: `/user/${id}/history?limit=${limit}`,
|
: `/user/${id}/history?limit=${limit}`
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(params, { dispatch, getState, queryFulfilled }) {
|
async onQueryStarted(params, { dispatch, getState, queryFulfilled }) {
|
||||||
const { data: messages } = await queryFulfilled;
|
const { data: messages } = await queryFulfilled;
|
||||||
@@ -125,9 +125,9 @@ export const contactApi = createApi({
|
|||||||
handleChatMessage(msg, dispatch, getState());
|
handleChatMessage(msg, dispatch, getState());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}),
|
})
|
||||||
}),
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
@@ -140,5 +140,5 @@ export const {
|
|||||||
useGetContactsQuery,
|
useGetContactsQuery,
|
||||||
useLazyGetContactsQuery,
|
useLazyGetContactsQuery,
|
||||||
useSendMsgMutation,
|
useSendMsgMutation,
|
||||||
useRegisterMutation,
|
useRegisterMutation
|
||||||
} = contactApi;
|
} = contactApi;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export const onMessageSendStarted = async (
|
|||||||
type = "text",
|
type = "text",
|
||||||
from_uid,
|
from_uid,
|
||||||
reply_mid = null,
|
reply_mid = null,
|
||||||
properties = { local_id: +new Date() },
|
properties = { local_id: +new Date() }
|
||||||
},
|
},
|
||||||
{ dispatch, queryFulfilled },
|
{ dispatch, queryFulfilled },
|
||||||
from = "channel"
|
from = "channel"
|
||||||
@@ -42,11 +42,10 @@ export const onMessageSendStarted = async (
|
|||||||
properties,
|
properties,
|
||||||
from_uid,
|
from_uid,
|
||||||
reply_mid,
|
reply_mid,
|
||||||
sending: true,
|
sending: true
|
||||||
};
|
};
|
||||||
const addContextMessage = from == "channel" ? addChannelMsg : addUserMsg;
|
const addContextMessage = from == "channel" ? addChannelMsg : addUserMsg;
|
||||||
const removeContextMessage =
|
const removeContextMessage = from == "channel" ? removeChannelMsg : removeUserMsg;
|
||||||
from == "channel" ? removeChannelMsg : removeUserMsg;
|
|
||||||
if (!ignoreLocal) {
|
if (!ignoreLocal) {
|
||||||
batch(() => {
|
batch(() => {
|
||||||
dispatch(addMessage({ mid: ts, ...tmpMsg }));
|
dispatch(addMessage({ mid: ts, ...tmpMsg }));
|
||||||
|
|||||||
+205
-205
@@ -1,214 +1,214 @@
|
|||||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
import { createApi } from "@reduxjs/toolkit/query/react";
|
||||||
import { ContentTypes } from '../config';
|
import { ContentTypes } from "../config";
|
||||||
import { updateReadChannels, updateReadUsers } from '../slices/footprint';
|
import { updateReadChannels, updateReadUsers } from "../slices/footprint";
|
||||||
import {
|
import {
|
||||||
fullfillFavorites,
|
fullfillFavorites,
|
||||||
populateFavorite,
|
populateFavorite,
|
||||||
addFavorite,
|
addFavorite,
|
||||||
deleteFavorite
|
deleteFavorite
|
||||||
} from '../slices/favorites';
|
} from "../slices/favorites";
|
||||||
import { onMessageSendStarted } from './handlers';
|
import { onMessageSendStarted } from "./handlers";
|
||||||
import { normalizeArchiveData } from '../../common/utils';
|
import { normalizeArchiveData } from "../../common/utils";
|
||||||
// import { updateMessage } from "../slices/message";
|
// import { updateMessage } from "../slices/message";
|
||||||
import baseQuery from './base.query';
|
import baseQuery from "./base.query";
|
||||||
|
|
||||||
export const messageApi = createApi({
|
export const messageApi = createApi({
|
||||||
reducerPath: 'messageApi',
|
reducerPath: "messageApi",
|
||||||
baseQuery,
|
baseQuery,
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
editMessage: builder.mutation({
|
editMessage: builder.mutation({
|
||||||
query: ({ mid, content, type = 'text' }) => ({
|
query: ({ mid, content, type = "text" }) => ({
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': ContentTypes[type]
|
"content-type": ContentTypes[type]
|
||||||
},
|
},
|
||||||
url: `/message/${mid}/edit`,
|
url: `/message/${mid}/edit`,
|
||||||
method: 'PUT',
|
method: "PUT",
|
||||||
body: content
|
body: content
|
||||||
})
|
})
|
||||||
// async onQueryStarted({mid,content},{dispatch}){
|
// async onQueryStarted({mid,content},{dispatch}){
|
||||||
// dispatch()
|
// dispatch()
|
||||||
// }
|
// }
|
||||||
}),
|
}),
|
||||||
reactMessage: builder.mutation({
|
reactMessage: builder.mutation({
|
||||||
query: ({ mid, action }) => ({
|
query: ({ mid, action }) => ({
|
||||||
url: `/message/${mid}/like`,
|
url: `/message/${mid}/like`,
|
||||||
method: 'PUT',
|
method: "PUT",
|
||||||
body: { action }
|
body: { action }
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
deleteMessage: builder.query({
|
deleteMessage: builder.query({
|
||||||
query: (mid) => ({
|
query: (mid) => ({
|
||||||
url: `/message/${mid}`,
|
url: `/message/${mid}`,
|
||||||
method: 'DELETE'
|
method: "DELETE"
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
prepareUploadFile: builder.mutation({
|
prepareUploadFile: builder.mutation({
|
||||||
query: (meta = {}) => ({
|
query: (meta = {}) => ({
|
||||||
url: `/resource/file/prepare`,
|
url: `/resource/file/prepare`,
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
body: meta
|
body: meta
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
createArchive: builder.mutation({
|
createArchive: builder.mutation({
|
||||||
query: (mids = []) => ({
|
query: (mids = []) => ({
|
||||||
url: `/resource/archive`,
|
url: `/resource/archive`,
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
body: { mid_list: mids }
|
body: { mid_list: mids }
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
uploadFile: builder.mutation({
|
uploadFile: builder.mutation({
|
||||||
query: (formData) => ({
|
query: (formData) => ({
|
||||||
// headers: {
|
// headers: {
|
||||||
// "content-type": ContentTypes.formData,
|
// "content-type": ContentTypes.formData,
|
||||||
// },
|
// },
|
||||||
url: `/resource/file/upload`,
|
url: `/resource/file/upload`,
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
body: formData
|
body: formData
|
||||||
}),
|
}),
|
||||||
transformResponse: (data) => {
|
transformResponse: (data) => {
|
||||||
console.log('upload file response', data);
|
console.log("upload file response", data);
|
||||||
return data ? data : {};
|
return data ? data : {};
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
getOGInfo: builder.query({
|
getOGInfo: builder.query({
|
||||||
query: (url) => ({
|
query: (url) => ({
|
||||||
url: `/resource/open_graphic_parse?url=${encodeURIComponent(url)}`
|
url: `/resource/open_graphic_parse?url=${encodeURIComponent(url)}`
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
getArchiveMessage: builder.query({
|
getArchiveMessage: builder.query({
|
||||||
query: (file_path) => ({
|
query: (file_path) => ({
|
||||||
url: `/resource/archive?file_path=${encodeURIComponent(file_path)}`
|
url: `/resource/archive?file_path=${encodeURIComponent(file_path)}`
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
pinMessage: builder.mutation({
|
pinMessage: builder.mutation({
|
||||||
query: ({ gid, mid }) => ({
|
query: ({ gid, mid }) => ({
|
||||||
url: `/group/${gid}/pin`,
|
url: `/group/${gid}/pin`,
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
body: { mid }
|
body: { mid }
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
unpinMessage: builder.mutation({
|
unpinMessage: builder.mutation({
|
||||||
query: ({ gid, mid }) => ({
|
query: ({ gid, mid }) => ({
|
||||||
url: `/group/${gid}/unpin`,
|
url: `/group/${gid}/unpin`,
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
body: { mid }
|
body: { mid }
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
favoriteMessage: builder.mutation({
|
favoriteMessage: builder.mutation({
|
||||||
query: (mids) => ({
|
query: (mids) => ({
|
||||||
url: `/favorite`,
|
url: `/favorite`,
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
body: { mid_list: mids }
|
body: { mid_list: mids }
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(mids, { dispatch, queryFulfilled }) {
|
async onQueryStarted(mids, { dispatch, queryFulfilled }) {
|
||||||
try {
|
try {
|
||||||
const { data } = await queryFulfilled;
|
const { data } = await queryFulfilled;
|
||||||
const { created_at, id } = data;
|
const { created_at, id } = data;
|
||||||
dispatch(addFavorite({ id, created_at }));
|
dispatch(addFavorite({ id, created_at }));
|
||||||
dispatch(messageApi.endpoints.getFavoriteDetails.initiate(id));
|
dispatch(messageApi.endpoints.getFavoriteDetails.initiate(id));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('get favorite list error', err);
|
console.log("get favorite list error", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
removeFavorite: builder.query({
|
removeFavorite: builder.query({
|
||||||
query: (id) => ({
|
query: (id) => ({
|
||||||
url: `/favorite/${id}`,
|
url: `/favorite/${id}`,
|
||||||
method: 'DELETE'
|
method: "DELETE"
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(id, { dispatch, queryFulfilled }) {
|
async onQueryStarted(id, { dispatch, queryFulfilled }) {
|
||||||
try {
|
try {
|
||||||
await queryFulfilled;
|
await queryFulfilled;
|
||||||
dispatch(deleteFavorite(id));
|
dispatch(deleteFavorite(id));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('get favorite list error', err);
|
console.log("get favorite list error", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
getFavoriteDetails: builder.query({
|
getFavoriteDetails: builder.query({
|
||||||
query: (id) => ({
|
query: (id) => ({
|
||||||
url: `/favorite/${id}`
|
url: `/favorite/${id}`
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(id, { dispatch, queryFulfilled, getState }) {
|
async onQueryStarted(id, { dispatch, queryFulfilled, getState }) {
|
||||||
try {
|
try {
|
||||||
const { data } = await queryFulfilled;
|
const { data } = await queryFulfilled;
|
||||||
const loginUid = getState().authData.uid;
|
const loginUid = getState().authData.uid;
|
||||||
const messages = normalizeArchiveData(data, id, loginUid);
|
const messages = normalizeArchiveData(data, id, loginUid);
|
||||||
dispatch(populateFavorite({ id, messages }));
|
dispatch(populateFavorite({ id, messages }));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('get favorite list error', err);
|
console.log("get favorite list error", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
getFavorites: builder.query({
|
getFavorites: builder.query({
|
||||||
query: () => ({
|
query: () => ({
|
||||||
url: `/favorite`
|
url: `/favorite`
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(data, { dispatch, queryFulfilled }) {
|
async onQueryStarted(data, { dispatch, queryFulfilled }) {
|
||||||
try {
|
try {
|
||||||
const { data: favorites } = await queryFulfilled;
|
const { data: favorites } = await queryFulfilled;
|
||||||
dispatch(fullfillFavorites(favorites));
|
dispatch(fullfillFavorites(favorites));
|
||||||
for (const fav of favorites) {
|
for (const fav of favorites) {
|
||||||
const { id } = fav;
|
const { id } = fav;
|
||||||
dispatch(messageApi.endpoints.getFavoriteDetails.initiate(id));
|
dispatch(messageApi.endpoints.getFavoriteDetails.initiate(id));
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('get favorite list error', err);
|
console.log("get favorite list error", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
replyMessage: builder.mutation({
|
replyMessage: builder.mutation({
|
||||||
query: ({ reply_mid, content, type = 'text' }) => ({
|
query: ({ reply_mid, content, type = "text" }) => ({
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': ContentTypes[type]
|
"content-type": ContentTypes[type]
|
||||||
},
|
},
|
||||||
url: `/message/${reply_mid}/reply`,
|
url: `/message/${reply_mid}/reply`,
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
body: content
|
body: content
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(param1, param2) {
|
async onQueryStarted(param1, param2) {
|
||||||
await onMessageSendStarted.call(this, param1, param2, param1.context);
|
await onMessageSendStarted.call(this, param1, param2, param1.context);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
readMessage: builder.mutation({
|
readMessage: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: `/user/read-index`,
|
url: `/user/read-index`,
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
body: data
|
body: data
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(data, { dispatch, queryFulfilled }) {
|
async onQueryStarted(data, { dispatch, queryFulfilled }) {
|
||||||
const { users = null, groups = null } = data;
|
const { users = null, groups = null } = data;
|
||||||
if (users) {
|
if (users) {
|
||||||
dispatch(updateReadUsers(users));
|
dispatch(updateReadUsers(users));
|
||||||
}
|
}
|
||||||
if (groups) {
|
if (groups) {
|
||||||
dispatch(updateReadChannels(groups));
|
dispatch(updateReadChannels(groups));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await queryFulfilled;
|
await queryFulfilled;
|
||||||
} catch {
|
} catch {
|
||||||
// todo
|
// todo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
useLazyRemoveFavoriteQuery,
|
useLazyRemoveFavoriteQuery,
|
||||||
useUnpinMessageMutation,
|
useUnpinMessageMutation,
|
||||||
useLazyGetFavoritesQuery,
|
useLazyGetFavoritesQuery,
|
||||||
useFavoriteMessageMutation,
|
useFavoriteMessageMutation,
|
||||||
usePinMessageMutation,
|
usePinMessageMutation,
|
||||||
useLazyGetArchiveMessageQuery,
|
useLazyGetArchiveMessageQuery,
|
||||||
useGetArchiveMessageQuery,
|
useGetArchiveMessageQuery,
|
||||||
useLazyGetOGInfoQuery,
|
useLazyGetOGInfoQuery,
|
||||||
usePrepareUploadFileMutation,
|
usePrepareUploadFileMutation,
|
||||||
useUploadFileMutation,
|
useUploadFileMutation,
|
||||||
useEditMessageMutation,
|
useEditMessageMutation,
|
||||||
useReactMessageMutation,
|
useReactMessageMutation,
|
||||||
useReplyMessageMutation,
|
useReplyMessageMutation,
|
||||||
useLazyDeleteMessageQuery,
|
useLazyDeleteMessageQuery,
|
||||||
useReadMessageMutation,
|
useReadMessageMutation,
|
||||||
useCreateArchiveMutation
|
useCreateArchiveMutation
|
||||||
} = messageApi;
|
} = messageApi;
|
||||||
|
|||||||
+46
-46
@@ -20,7 +20,7 @@ export const serverApi = createApi({
|
|||||||
} catch {
|
} catch {
|
||||||
console.log("get server info error");
|
console.log("get server info error");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
getThirdPartySecret: builder.query({
|
getThirdPartySecret: builder.query({
|
||||||
query: () => ({
|
query: () => ({
|
||||||
@@ -29,142 +29,142 @@ export const serverApi = createApi({
|
|||||||
// accept: "text/plain",
|
// accept: "text/plain",
|
||||||
// },
|
// },
|
||||||
url: `/admin/system/third_party_secret`,
|
url: `/admin/system/third_party_secret`,
|
||||||
responseHandler: (response) => response.text(),
|
responseHandler: (response) => response.text()
|
||||||
}),
|
}),
|
||||||
keepUnusedDataFor: 0,
|
keepUnusedDataFor: 0
|
||||||
}),
|
}),
|
||||||
updateThirdPartySecret: builder.mutation({
|
updateThirdPartySecret: builder.mutation({
|
||||||
query: () => ({
|
query: () => ({
|
||||||
url: `/admin/system/third_party_secret`,
|
url: `/admin/system/third_party_secret`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
responseHandler: (response) => response.text(),
|
responseHandler: (response) => response.text()
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
getMetrics: builder.query({
|
getMetrics: builder.query({
|
||||||
query: () => ({ url: `/admin/system/metrics` }),
|
query: () => ({ url: `/admin/system/metrics` })
|
||||||
}),
|
}),
|
||||||
getServerVersion: builder.query({
|
getServerVersion: builder.query({
|
||||||
query: () => ({
|
query: () => ({
|
||||||
headers: {
|
headers: {
|
||||||
// "content-type": "text/plain",
|
// "content-type": "text/plain",
|
||||||
accept: "text/plain",
|
accept: "text/plain"
|
||||||
},
|
},
|
||||||
url: `/admin/system/version`,
|
url: `/admin/system/version`,
|
||||||
responseHandler: (response) => response.text(),
|
responseHandler: (response) => response.text()
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
getFirebaseConfig: builder.query({
|
getFirebaseConfig: builder.query({
|
||||||
query: () => ({ url: `admin/fcm/config` }),
|
query: () => ({ url: `admin/fcm/config` })
|
||||||
}),
|
}),
|
||||||
getGoogleAuthConfig: builder.query({
|
getGoogleAuthConfig: builder.query({
|
||||||
query: () => ({ url: `admin/google_auth/config` }),
|
query: () => ({ url: `admin/google_auth/config` })
|
||||||
}),
|
}),
|
||||||
updateGoogleAuthConfig: builder.mutation({
|
updateGoogleAuthConfig: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: `admin/google_auth/config`,
|
url: `admin/google_auth/config`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
getGithubAuthConfig: builder.query({
|
getGithubAuthConfig: builder.query({
|
||||||
query: () => ({ url: `admin/github_auth/config` }),
|
query: () => ({ url: `admin/github_auth/config` })
|
||||||
}),
|
}),
|
||||||
updateGithubAuthConfig: builder.mutation({
|
updateGithubAuthConfig: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: `admin/github_auth/config`,
|
url: `admin/github_auth/config`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
sendTestEmail: builder.mutation({
|
sendTestEmail: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: `/admin/system/send_mail`,
|
url: `/admin/system/send_mail`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
updateFirebaseConfig: builder.mutation({
|
updateFirebaseConfig: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: `admin/fcm/config`,
|
url: `admin/fcm/config`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
getAgoraConfig: builder.query({
|
getAgoraConfig: builder.query({
|
||||||
query: () => ({ url: `admin/agora/config` }),
|
query: () => ({ url: `admin/agora/config` })
|
||||||
}),
|
}),
|
||||||
updateAgoraConfig: builder.mutation({
|
updateAgoraConfig: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: `admin/agora/config`,
|
url: `admin/agora/config`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
getSMTPConfig: builder.query({
|
getSMTPConfig: builder.query({
|
||||||
query: () => ({ url: `admin/smtp/config` }),
|
query: () => ({ url: `admin/smtp/config` })
|
||||||
}),
|
}),
|
||||||
getSMTPStatus: builder.query({
|
getSMTPStatus: builder.query({
|
||||||
query: () => ({ url: `/admin/smtp/enabled` }),
|
query: () => ({ url: `/admin/smtp/enabled` })
|
||||||
}),
|
}),
|
||||||
updateSMTPConfig: builder.mutation({
|
updateSMTPConfig: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: `admin/smtp/config`,
|
url: `admin/smtp/config`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
getLoginConfig: builder.query({
|
getLoginConfig: builder.query({
|
||||||
query: () => ({ url: `admin/login/config` }),
|
query: () => ({ url: `admin/login/config` })
|
||||||
}),
|
}),
|
||||||
updateLoginConfig: builder.mutation({
|
updateLoginConfig: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: `admin/login/config`,
|
url: `admin/login/config`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
updateLogo: builder.mutation({
|
updateLogo: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "image/png",
|
"content-type": "image/png"
|
||||||
},
|
},
|
||||||
url: `admin/system/organization/logo`,
|
url: `admin/system/organization/logo`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(data, { dispatch, queryFulfilled }) {
|
async onQueryStarted(data, { dispatch, queryFulfilled }) {
|
||||||
try {
|
try {
|
||||||
await queryFulfilled;
|
await queryFulfilled;
|
||||||
dispatch(
|
dispatch(
|
||||||
updateInfo({
|
updateInfo({
|
||||||
logo: `${BASE_URL}/resource/organization/logo?t=${+new Date()}`,
|
logo: `${BASE_URL}/resource/organization/logo?t=${+new Date()}`
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
console.log("update server logo error");
|
console.log("update server logo error");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
createInviteLink: builder.query({
|
createInviteLink: builder.query({
|
||||||
query: (expired_in = defaultExpireDuration) => ({
|
query: (expired_in = defaultExpireDuration) => ({
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "text/plain",
|
"content-type": "text/plain",
|
||||||
accept: "text/plain",
|
accept: "text/plain"
|
||||||
},
|
},
|
||||||
url: `/admin/system/create_invite_link?expired_in=${expired_in}`,
|
url: `/admin/system/create_invite_link?expired_in=${expired_in}`,
|
||||||
responseHandler: (response) => response.text(),
|
responseHandler: (response) => response.text()
|
||||||
}),
|
}),
|
||||||
transformResponse: (link) => {
|
transformResponse: (link) => {
|
||||||
// 替换掉域名
|
// 替换掉域名
|
||||||
const invite = new URL(link);
|
const invite = new URL(link);
|
||||||
return `${location.origin}${invite.pathname}${invite.search}${invite.hash}`;
|
return `${location.origin}${invite.pathname}${invite.search}${invite.hash}`;
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
updateServer: builder.mutation({
|
updateServer: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: `admin/system/organization`,
|
url: `admin/system/organization`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
}),
|
||||||
async onQueryStarted(data, { dispatch, queryFulfilled, getState }) {
|
async onQueryStarted(data, { dispatch, queryFulfilled, getState }) {
|
||||||
const { name: prevName, description: prevDesc } = getState().server;
|
const { name: prevName, description: prevDesc } = getState().server;
|
||||||
@@ -174,21 +174,21 @@ export const serverApi = createApi({
|
|||||||
} catch {
|
} catch {
|
||||||
dispatch(updateInfo({ name: prevName, description: prevDesc }));
|
dispatch(updateInfo({ name: prevName, description: prevDesc }));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
createAdmin: builder.mutation({
|
createAdmin: builder.mutation({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: `/admin/system/create_admin`,
|
url: `/admin/system/create_admin`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: data,
|
body: data
|
||||||
}),
|
})
|
||||||
}),
|
}),
|
||||||
getInitialized: builder.query({
|
getInitialized: builder.query({
|
||||||
query: () => ({
|
query: () => ({
|
||||||
url: `/admin/system/initialized`,
|
url: `/admin/system/initialized`
|
||||||
}),
|
})
|
||||||
}),
|
})
|
||||||
}),
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
@@ -217,5 +217,5 @@ export const {
|
|||||||
useGetThirdPartySecretQuery,
|
useGetThirdPartySecretQuery,
|
||||||
useUpdateThirdPartySecretMutation,
|
useUpdateThirdPartySecretMutation,
|
||||||
useCreateAdminMutation,
|
useCreateAdminMutation,
|
||||||
useGetInitializedQuery,
|
useGetInitializedQuery
|
||||||
} = serverApi;
|
} = serverApi;
|
||||||
|
|||||||
@@ -1,24 +1,18 @@
|
|||||||
import { createSlice } from "@reduxjs/toolkit";
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
import {
|
import { KEY_PWA_INSTALLED, KEY_REFRESH_TOKEN, KEY_TOKEN, KEY_UID, KEY_EXPIRE } from "../config";
|
||||||
KEY_PWA_INSTALLED,
|
|
||||||
KEY_REFRESH_TOKEN,
|
|
||||||
KEY_TOKEN,
|
|
||||||
KEY_UID,
|
|
||||||
KEY_EXPIRE,
|
|
||||||
} from "../config";
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
initialized: true,
|
initialized: true,
|
||||||
uid: null,
|
uid: null,
|
||||||
token: localStorage.getItem(KEY_TOKEN),
|
token: localStorage.getItem(KEY_TOKEN),
|
||||||
expireTime: localStorage.getItem(KEY_EXPIRE) || +new Date(),
|
expireTime: localStorage.getItem(KEY_EXPIRE) || +new Date(),
|
||||||
refreshToken: localStorage.getItem(KEY_REFRESH_TOKEN),
|
refreshToken: localStorage.getItem(KEY_REFRESH_TOKEN)
|
||||||
};
|
};
|
||||||
const emptyState = {
|
const emptyState = {
|
||||||
initialized: true,
|
initialized: true,
|
||||||
uid: null,
|
uid: null,
|
||||||
token: null,
|
token: null,
|
||||||
expireTime: +new Date(),
|
expireTime: +new Date(),
|
||||||
refreshToken: null,
|
refreshToken: null
|
||||||
};
|
};
|
||||||
const authDataSlice = createSlice({
|
const authDataSlice = createSlice({
|
||||||
name: "authData",
|
name: "authData",
|
||||||
@@ -30,7 +24,7 @@ const authDataSlice = createSlice({
|
|||||||
user: { uid },
|
user: { uid },
|
||||||
token,
|
token,
|
||||||
refresh_token,
|
refresh_token,
|
||||||
expired_in = 0,
|
expired_in = 0
|
||||||
} = action.payload;
|
} = action.payload;
|
||||||
state.initialized = initialized;
|
state.initialized = initialized;
|
||||||
state.uid = uid;
|
state.uid = uid;
|
||||||
@@ -76,14 +70,9 @@ const authDataSlice = createSlice({
|
|||||||
localStorage.setItem(KEY_EXPIRE, et);
|
localStorage.setItem(KEY_EXPIRE, et);
|
||||||
localStorage.setItem(KEY_TOKEN, token);
|
localStorage.setItem(KEY_TOKEN, token);
|
||||||
localStorage.setItem(KEY_REFRESH_TOKEN, refresh_token);
|
localStorage.setItem(KEY_REFRESH_TOKEN, refresh_token);
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
export const {
|
export const { updateInitialized, setAuthData, resetAuthData, setUid, updateToken } =
|
||||||
updateInitialized,
|
authDataSlice.actions;
|
||||||
setAuthData,
|
|
||||||
resetAuthData,
|
|
||||||
setUid,
|
|
||||||
updateToken,
|
|
||||||
} = authDataSlice.actions;
|
|
||||||
export default authDataSlice.reducer;
|
export default authDataSlice.reducer;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import BASE_URL from "../config";
|
|||||||
import { getNonNullValues } from "../../common/utils";
|
import { getNonNullValues } from "../../common/utils";
|
||||||
const initialState = {
|
const initialState = {
|
||||||
ids: [],
|
ids: [],
|
||||||
byId: {},
|
byId: {}
|
||||||
};
|
};
|
||||||
const channelsSlice = createSlice({
|
const channelsSlice = createSlice({
|
||||||
name: `channels`,
|
name: `channels`,
|
||||||
@@ -36,18 +36,14 @@ const channelsSlice = createSlice({
|
|||||||
icon:
|
icon:
|
||||||
avatar_updated_at == 0
|
avatar_updated_at == 0
|
||||||
? ""
|
? ""
|
||||||
: `${BASE_URL}/resource/group_avatar?gid=${gid}&t=${avatar_updated_at}`,
|
: `${BASE_URL}/resource/group_avatar?gid=${gid}&t=${avatar_updated_at}`
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateChannel(state, action) {
|
updateChannel(state, action) {
|
||||||
const ignoreInPublic = ["add_member", "remove_member"];
|
const ignoreInPublic = ["add_member", "remove_member"];
|
||||||
const { id, operation, members = [], ...rest } = action.payload;
|
const { id, operation, members = [], ...rest } = action.payload;
|
||||||
const currChannel = state.byId[id];
|
const currChannel = state.byId[id];
|
||||||
if (
|
if (!currChannel || (currChannel.is_public && ignoreInPublic.includes(operation))) return;
|
||||||
!currChannel ||
|
|
||||||
(currChannel.is_public && ignoreInPublic.includes(operation))
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case "remove_member":
|
case "remove_member":
|
||||||
{
|
{
|
||||||
@@ -99,8 +95,8 @@ const channelsSlice = createSlice({
|
|||||||
state.ids.splice(idx, 1);
|
state.ids.splice(idx, 1);
|
||||||
delete state.byId[gid];
|
delete state.byId[gid];
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
export const {
|
export const {
|
||||||
updatePinMessage,
|
updatePinMessage,
|
||||||
@@ -108,6 +104,6 @@ export const {
|
|||||||
fullfillChannels,
|
fullfillChannels,
|
||||||
addChannel,
|
addChannel,
|
||||||
updateChannel,
|
updateChannel,
|
||||||
removeChannel,
|
removeChannel
|
||||||
} = channelsSlice.actions;
|
} = channelsSlice.actions;
|
||||||
export default channelsSlice.reducer;
|
export default channelsSlice.reducer;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { getNonNullValues } from "../../common/utils";
|
|||||||
import BASE_URL from "../config";
|
import BASE_URL from "../config";
|
||||||
const initialState = {
|
const initialState = {
|
||||||
ids: [],
|
ids: [],
|
||||||
byId: {},
|
byId: {}
|
||||||
};
|
};
|
||||||
const contactsSlice = createSlice({
|
const contactsSlice = createSlice({
|
||||||
name: `contacts`,
|
name: `contacts`,
|
||||||
@@ -39,9 +39,7 @@ const contactsSlice = createSlice({
|
|||||||
Object.keys(vals).forEach((k) => {
|
Object.keys(vals).forEach((k) => {
|
||||||
state.byId[uid][k] = vals[k];
|
state.byId[uid][k] = vals[k];
|
||||||
if (k == "avatar_updated_at") {
|
if (k == "avatar_updated_at") {
|
||||||
state.byId[
|
state.byId[uid].avatar = `${BASE_URL}/resource/avatar?uid=${uid}&t=${vals[k]}`;
|
||||||
uid
|
|
||||||
].avatar = `${BASE_URL}/resource/avatar?uid=${uid}&t=${vals[k]}`;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -80,13 +78,9 @@ const contactsSlice = createSlice({
|
|||||||
state.byId[uid].online = online;
|
state.byId[uid].online = online;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
export const {
|
export const { resetContacts, fullfillContacts, updateUsersByLogs, updateUsersStatus } =
|
||||||
resetContacts,
|
contactsSlice.actions;
|
||||||
fullfillContacts,
|
|
||||||
updateUsersByLogs,
|
|
||||||
updateUsersStatus,
|
|
||||||
} = contactsSlice.actions;
|
|
||||||
export default contactsSlice.reducer;
|
export default contactsSlice.reducer;
|
||||||
|
|||||||
@@ -24,13 +24,9 @@ const favoritesSlice = createSlice({
|
|||||||
if (idx > -1) {
|
if (idx > -1) {
|
||||||
state[idx].messages = messages;
|
state[idx].messages = messages;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
export const {
|
export const { addFavorite, deleteFavorite, fullfillFavorites, populateFavorite } =
|
||||||
addFavorite,
|
favoritesSlice.actions;
|
||||||
deleteFavorite,
|
|
||||||
fullfillFavorites,
|
|
||||||
populateFavorite,
|
|
||||||
} = favoritesSlice.actions;
|
|
||||||
export default favoritesSlice.reducer;
|
export default favoritesSlice.reducer;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const initialState = {
|
|||||||
readUsers: {},
|
readUsers: {},
|
||||||
readChannels: {},
|
readChannels: {},
|
||||||
muteUsers: {},
|
muteUsers: {},
|
||||||
muteChannels: {},
|
muteChannels: {}
|
||||||
};
|
};
|
||||||
const footprintSlice = createSlice({
|
const footprintSlice = createSlice({
|
||||||
name: "footprint",
|
name: "footprint",
|
||||||
@@ -21,7 +21,7 @@ const footprintSlice = createSlice({
|
|||||||
readUsers = {},
|
readUsers = {},
|
||||||
readChannels = {},
|
readChannels = {},
|
||||||
muteUsers = {},
|
muteUsers = {},
|
||||||
muteChannels = {},
|
muteChannels = {}
|
||||||
} = action.payload;
|
} = action.payload;
|
||||||
return {
|
return {
|
||||||
usersVersion,
|
usersVersion,
|
||||||
@@ -29,7 +29,7 @@ const footprintSlice = createSlice({
|
|||||||
readUsers,
|
readUsers,
|
||||||
readChannels,
|
readChannels,
|
||||||
muteUsers,
|
muteUsers,
|
||||||
muteChannels,
|
muteChannels
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateUsersVersion(state, action) {
|
updateUsersVersion(state, action) {
|
||||||
@@ -94,8 +94,8 @@ const footprintSlice = createSlice({
|
|||||||
reads.forEach(({ gid, mid }) => {
|
reads.forEach(({ gid, mid }) => {
|
||||||
state.readChannels[gid] = mid;
|
state.readChannels[gid] = mid;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
export const {
|
export const {
|
||||||
resetFootprint,
|
resetFootprint,
|
||||||
@@ -104,6 +104,6 @@ export const {
|
|||||||
updateUsersVersion,
|
updateUsersVersion,
|
||||||
updateReadChannels,
|
updateReadChannels,
|
||||||
updateReadUsers,
|
updateReadUsers,
|
||||||
updateMute,
|
updateMute
|
||||||
} = footprintSlice.actions;
|
} = footprintSlice.actions;
|
||||||
export default footprintSlice.reducer;
|
export default footprintSlice.reducer;
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ const channelMsgSlice = createSlice({
|
|||||||
const { id, mid, local_id = null } = action.payload;
|
const { id, mid, local_id = null } = action.payload;
|
||||||
if (state[id]) {
|
if (state[id]) {
|
||||||
const midExsited = state[id].findIndex((id) => id == mid) > -1;
|
const midExsited = state[id].findIndex((id) => id == mid) > -1;
|
||||||
const localMsgExsited =
|
const localMsgExsited = state[id].findIndex((id) => id == local_id) > -1;
|
||||||
state[id].findIndex((id) => id == local_id) > -1;
|
|
||||||
if (midExsited || localMsgExsited) return;
|
if (midExsited || localMsgExsited) return;
|
||||||
state[id].push(+mid);
|
state[id].push(+mid);
|
||||||
} else {
|
} else {
|
||||||
@@ -44,14 +43,12 @@ const channelMsgSlice = createSlice({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeChannelSession(state, action) {
|
removeChannelSession(state, action) {
|
||||||
const ids = Array.isArray(action.payload)
|
const ids = Array.isArray(action.payload) ? action.payload : [action.payload];
|
||||||
? action.payload
|
|
||||||
: [action.payload];
|
|
||||||
ids.forEach((id) => {
|
ids.forEach((id) => {
|
||||||
delete state[id];
|
delete state[id];
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
export const {
|
export const {
|
||||||
removeChannelSession,
|
removeChannelSession,
|
||||||
@@ -59,6 +56,6 @@ export const {
|
|||||||
fullfillChannelMsg,
|
fullfillChannelMsg,
|
||||||
addChannelMsg,
|
addChannelMsg,
|
||||||
removeChannelMsg,
|
removeChannelMsg,
|
||||||
replaceChannelMsg,
|
replaceChannelMsg
|
||||||
} = channelMsgSlice.actions;
|
} = channelMsgSlice.actions;
|
||||||
export default channelMsgSlice.reducer;
|
export default channelMsgSlice.reducer;
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ const fileMessageSlice = createSlice({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeFileMessage(state, action) {
|
removeFileMessage(state, action) {
|
||||||
const mids = Array.isArray(action.payload)
|
const mids = Array.isArray(action.payload) ? action.payload : [action.payload];
|
||||||
? action.payload
|
|
||||||
: [action.payload];
|
|
||||||
mids.forEach((id) => {
|
mids.forEach((id) => {
|
||||||
// 从file message 列表删掉
|
// 从file message 列表删掉
|
||||||
const fidIdx = state.findIndex((fid) => fid == id);
|
const fidIdx = state.findIndex((fid) => fid == id);
|
||||||
@@ -30,13 +28,9 @@ const fileMessageSlice = createSlice({
|
|||||||
state.splice(fidIdx, 1);
|
state.splice(fidIdx, 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
export const {
|
export const { removeFileMessage, resetFileMessage, fullfillFileMessage, addFileMessage } =
|
||||||
removeFileMessage,
|
fileMessageSlice.actions;
|
||||||
resetFileMessage,
|
|
||||||
fullfillFileMessage,
|
|
||||||
addFileMessage,
|
|
||||||
} = fileMessageSlice.actions;
|
|
||||||
export default fileMessageSlice.reducer;
|
export default fileMessageSlice.reducer;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createSlice } from "@reduxjs/toolkit";
|
|||||||
import BASE_URL, { ContentTypes } from "../config";
|
import BASE_URL, { ContentTypes } from "../config";
|
||||||
import { isImage } from "../../common/utils";
|
import { isImage } from "../../common/utils";
|
||||||
const initialState = {
|
const initialState = {
|
||||||
replying: {},
|
replying: {}
|
||||||
};
|
};
|
||||||
const messageSlice = createSlice({
|
const messageSlice = createSlice({
|
||||||
name: "message",
|
name: "message",
|
||||||
@@ -48,9 +48,7 @@ const messageSlice = createSlice({
|
|||||||
state[mid] = data;
|
state[mid] = data;
|
||||||
},
|
},
|
||||||
removeMessage(state, action) {
|
removeMessage(state, action) {
|
||||||
const mids = Array.isArray(action.payload)
|
const mids = Array.isArray(action.payload) ? action.payload : [action.payload];
|
||||||
? action.payload
|
|
||||||
: [action.payload];
|
|
||||||
mids.forEach((id) => {
|
mids.forEach((id) => {
|
||||||
delete state[id];
|
delete state[id];
|
||||||
});
|
});
|
||||||
@@ -65,8 +63,8 @@ const messageSlice = createSlice({
|
|||||||
if (state.replying[key]) {
|
if (state.replying[key]) {
|
||||||
delete state.replying[key];
|
delete state.replying[key];
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
export const {
|
export const {
|
||||||
resetMessage,
|
resetMessage,
|
||||||
@@ -76,6 +74,6 @@ export const {
|
|||||||
addMessage,
|
addMessage,
|
||||||
removeMessage,
|
removeMessage,
|
||||||
addReplyingMessage,
|
addReplyingMessage,
|
||||||
removeReplyingMessage,
|
removeReplyingMessage
|
||||||
} = messageSlice.actions;
|
} = messageSlice.actions;
|
||||||
export default messageSlice.reducer;
|
export default messageSlice.reducer;
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ const reactionMessageSlice = createSlice({
|
|||||||
return action.payload;
|
return action.payload;
|
||||||
},
|
},
|
||||||
removeReactionMessage(state, action) {
|
removeReactionMessage(state, action) {
|
||||||
const mids = Array.isArray(action.payload)
|
const mids = Array.isArray(action.payload) ? action.payload : [action.payload];
|
||||||
? action.payload
|
|
||||||
: [action.payload];
|
|
||||||
mids.forEach((id) => {
|
mids.forEach((id) => {
|
||||||
delete state[id];
|
delete state[id];
|
||||||
});
|
});
|
||||||
@@ -48,13 +46,13 @@ const reactionMessageSlice = createSlice({
|
|||||||
state[mid][reaction] = [from_uid];
|
state[mid][reaction] = [from_uid];
|
||||||
}
|
}
|
||||||
state[rid] = true;
|
state[rid] = true;
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
export const {
|
export const {
|
||||||
removeReactionMessage,
|
removeReactionMessage,
|
||||||
resetReactionMessage,
|
resetReactionMessage,
|
||||||
fullfillReactionMessage,
|
fullfillReactionMessage,
|
||||||
toggleReactionMessage,
|
toggleReactionMessage
|
||||||
} = reactionMessageSlice.actions;
|
} = reactionMessageSlice.actions;
|
||||||
export default reactionMessageSlice.reducer;
|
export default reactionMessageSlice.reducer;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createSlice } from "@reduxjs/toolkit";
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
const initialState = {
|
const initialState = {
|
||||||
ids: [],
|
ids: [],
|
||||||
byId: {},
|
byId: {}
|
||||||
};
|
};
|
||||||
const userMsgSlice = createSlice({
|
const userMsgSlice = createSlice({
|
||||||
name: "userMessage",
|
name: "userMessage",
|
||||||
@@ -18,8 +18,7 @@ const userMsgSlice = createSlice({
|
|||||||
const { id, mid, local_id } = action.payload;
|
const { id, mid, local_id } = action.payload;
|
||||||
if (state.byId[id]) {
|
if (state.byId[id]) {
|
||||||
const midExsited = state.byId[id].findIndex((id) => id == mid) > -1;
|
const midExsited = state.byId[id].findIndex((id) => id == mid) > -1;
|
||||||
const localMsgExsited =
|
const localMsgExsited = state.byId[id].findIndex((id) => id == local_id) > -1;
|
||||||
state.byId[id].findIndex((id) => id == local_id) > -1;
|
|
||||||
if (midExsited || localMsgExsited) return;
|
if (midExsited || localMsgExsited) return;
|
||||||
|
|
||||||
state.byId[id].push(+mid);
|
state.byId[id].push(+mid);
|
||||||
@@ -53,15 +52,13 @@ const userMsgSlice = createSlice({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeUserSession(state, action) {
|
removeUserSession(state, action) {
|
||||||
const ids = Array.isArray(action.payload)
|
const ids = Array.isArray(action.payload) ? action.payload : [action.payload];
|
||||||
? action.payload
|
|
||||||
: [action.payload];
|
|
||||||
state.ids = state.ids.filter((id) => ids.findIndex((i) => i == id) == -1);
|
state.ids = state.ids.filter((id) => ids.findIndex((i) => i == id) == -1);
|
||||||
// ids.forEach((id) => {
|
// ids.forEach((id) => {
|
||||||
// delete state.byId[id];
|
// delete state.byId[id];
|
||||||
// });
|
// });
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
export const {
|
export const {
|
||||||
removeUserSession,
|
removeUserSession,
|
||||||
@@ -69,6 +66,6 @@ export const {
|
|||||||
fullfillUserMsg,
|
fullfillUserMsg,
|
||||||
addUserMsg,
|
addUserMsg,
|
||||||
removeUserMsg,
|
removeUserMsg,
|
||||||
replaceUserMsg,
|
replaceUserMsg
|
||||||
} = userMsgSlice.actions;
|
} = userMsgSlice.actions;
|
||||||
export default userMsgSlice.reducer;
|
export default userMsgSlice.reducer;
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ set
|
|||||||
add
|
add
|
||||||
update
|
update
|
||||||
remove
|
remove
|
||||||
toggle
|
toggle
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ const initialState = {
|
|||||||
logo: "",
|
logo: "",
|
||||||
inviteLink: {
|
inviteLink: {
|
||||||
link: "",
|
link: "",
|
||||||
expire: 0,
|
expire: 0
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
const serverSlice = createSlice({
|
const serverSlice = createSlice({
|
||||||
name: "server",
|
name: "server",
|
||||||
@@ -19,10 +19,10 @@ const serverSlice = createSlice({
|
|||||||
const {
|
const {
|
||||||
inviteLink = {
|
inviteLink = {
|
||||||
link: "",
|
link: "",
|
||||||
expire: 0,
|
expire: 0
|
||||||
},
|
},
|
||||||
name = "",
|
name = "",
|
||||||
description = "",
|
description = ""
|
||||||
} = action.payload || {};
|
} = action.payload || {};
|
||||||
return { name, description, inviteLink };
|
return { name, description, inviteLink };
|
||||||
},
|
},
|
||||||
@@ -31,8 +31,8 @@ const serverSlice = createSlice({
|
|||||||
Object.keys(values).forEach((_key) => {
|
Object.keys(values).forEach((_key) => {
|
||||||
state[_key] = values[_key];
|
state[_key] = values[_key];
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
export const { updateInfo, resetServer, fullfillServer } = serverSlice.actions;
|
export const { updateInfo, resetServer, fullfillServer } = serverSlice.actions;
|
||||||
export default serverSlice.reducer;
|
export default serverSlice.reducer;
|
||||||
|
|||||||
+8
-18
@@ -5,7 +5,7 @@ const initialState = {
|
|||||||
ready: false,
|
ready: false,
|
||||||
userGuide: {
|
userGuide: {
|
||||||
visible: false,
|
visible: false,
|
||||||
step: 1,
|
step: 1
|
||||||
},
|
},
|
||||||
inputMode: "text",
|
inputMode: "text",
|
||||||
menuExpand: false,
|
menuExpand: false,
|
||||||
@@ -16,8 +16,8 @@ const initialState = {
|
|||||||
draftMixedText: {},
|
draftMixedText: {},
|
||||||
remeberedNavs: {
|
remeberedNavs: {
|
||||||
chat: null,
|
chat: null,
|
||||||
contact: null,
|
contact: null
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
const uiSlice = createSlice({
|
const uiSlice = createSlice({
|
||||||
name: "ui",
|
name: "ui",
|
||||||
@@ -61,12 +61,7 @@ const uiSlice = createSlice({
|
|||||||
},
|
},
|
||||||
|
|
||||||
updateUploadFiles(state, action) {
|
updateUploadFiles(state, action) {
|
||||||
const {
|
const { context = "channel", id = null, operation = "add", ...rest } = action.payload;
|
||||||
context = "channel",
|
|
||||||
id = null,
|
|
||||||
operation = "add",
|
|
||||||
...rest
|
|
||||||
} = action.payload;
|
|
||||||
if (!id || !context) return;
|
if (!id || !context) return;
|
||||||
const _key = `${context}_${id}`;
|
const _key = `${context}_${id}`;
|
||||||
let files = state.uploadFiles[_key];
|
let files = state.uploadFiles[_key];
|
||||||
@@ -126,12 +121,7 @@ const uiSlice = createSlice({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateSelectMessages(state, action) {
|
updateSelectMessages(state, action) {
|
||||||
const {
|
const { context = "channel", id = null, operation = "add", data = null } = action.payload;
|
||||||
context = "channel",
|
|
||||||
id = null,
|
|
||||||
operation = "add",
|
|
||||||
data = null,
|
|
||||||
} = action.payload;
|
|
||||||
let currData = state.selectMessages[`${context}_${id}`];
|
let currData = state.selectMessages[`${context}_${id}`];
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case "add": {
|
case "add": {
|
||||||
@@ -151,8 +141,8 @@ const uiSlice = createSlice({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
state.selectMessages[`${context}_${id}`] = currData;
|
state.selectMessages[`${context}_${id}`] = currData;
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
export const {
|
export const {
|
||||||
fullfillUI,
|
fullfillUI,
|
||||||
@@ -166,6 +156,6 @@ export const {
|
|||||||
updateUserGuide,
|
updateUserGuide,
|
||||||
updateDraftMarkdown,
|
updateDraftMarkdown,
|
||||||
updateDraftMixedText,
|
updateDraftMixedText,
|
||||||
updateRemeberedNavs,
|
updateRemeberedNavs
|
||||||
} = uiSlice.actions;
|
} = uiSlice.actions;
|
||||||
export default uiSlice.reducer;
|
export default uiSlice.reducer;
|
||||||
|
|||||||
+34
-41
@@ -36,7 +36,7 @@ const reducer = combineReducers({
|
|||||||
[messageApi.reducerPath]: messageApi.reducer,
|
[messageApi.reducerPath]: messageApi.reducer,
|
||||||
[contactApi.reducerPath]: contactApi.reducer,
|
[contactApi.reducerPath]: contactApi.reducer,
|
||||||
[channelApi.reducerPath]: channelApi.reducer,
|
[channelApi.reducerPath]: channelApi.reducer,
|
||||||
[serverApi.reducerPath]: serverApi.reducer,
|
[serverApi.reducerPath]: serverApi.reducer
|
||||||
});
|
});
|
||||||
|
|
||||||
const store = configureStore({
|
const store = configureStore({
|
||||||
@@ -50,48 +50,41 @@ const store = configureStore({
|
|||||||
serverApi.middleware,
|
serverApi.middleware,
|
||||||
messageApi.middleware
|
messageApi.middleware
|
||||||
)
|
)
|
||||||
.prepend(listenerMiddleware.middleware),
|
.prepend(listenerMiddleware.middleware)
|
||||||
});
|
});
|
||||||
let initialized = false;
|
let initialized = false;
|
||||||
setupListeners(
|
setupListeners(store.dispatch, (dispatch, { onOnline, onOffline, onFocus, onFocusLost }) => {
|
||||||
store.dispatch,
|
const handleFocus = () => dispatch(onFocus());
|
||||||
(dispatch, { onOnline, onOffline, onFocus, onFocusLost }) => {
|
const handleFocusLost = () => dispatch(onFocusLost());
|
||||||
const handleFocus = () => dispatch(onFocus());
|
const handleOnline = () => dispatch(onOnline());
|
||||||
const handleFocusLost = () => dispatch(onFocusLost());
|
const handleOffline = () => dispatch(onOffline());
|
||||||
const handleOnline = () => dispatch(onOnline());
|
const handleVisibilityChange = () => {
|
||||||
const handleOffline = () => dispatch(onOffline());
|
if (window.document.visibilityState === "visible") {
|
||||||
const handleVisibilityChange = () => {
|
handleFocus();
|
||||||
if (window.document.visibilityState === "visible") {
|
} else {
|
||||||
handleFocus();
|
handleFocusLost();
|
||||||
} else {
|
}
|
||||||
handleFocusLost();
|
};
|
||||||
}
|
|
||||||
};
|
if (!initialized) {
|
||||||
|
if (typeof window !== "undefined" && window.addEventListener) {
|
||||||
if (!initialized) {
|
// Handle focus events
|
||||||
if (typeof window !== "undefined" && window.addEventListener) {
|
window.addEventListener("visibilitychange", handleVisibilityChange, false);
|
||||||
// Handle focus events
|
window.addEventListener("focus", handleFocus, false);
|
||||||
window.addEventListener(
|
|
||||||
"visibilitychange",
|
// Handle connection events
|
||||||
handleVisibilityChange,
|
window.addEventListener("online", handleOnline, false);
|
||||||
false
|
window.addEventListener("offline", handleOffline, false);
|
||||||
);
|
initialized = true;
|
||||||
window.addEventListener("focus", handleFocus, false);
|
|
||||||
|
|
||||||
// Handle connection events
|
|
||||||
window.addEventListener("online", handleOnline, false);
|
|
||||||
window.addEventListener("offline", handleOffline, false);
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const unsubscribe = () => {
|
|
||||||
window.removeEventListener("focus", handleFocus);
|
|
||||||
window.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
||||||
window.removeEventListener("online", handleOnline);
|
|
||||||
window.removeEventListener("offline", handleOffline);
|
|
||||||
initialized = false;
|
|
||||||
};
|
|
||||||
return unsubscribe;
|
|
||||||
}
|
}
|
||||||
);
|
const unsubscribe = () => {
|
||||||
|
window.removeEventListener("focus", handleFocus);
|
||||||
|
window.removeEventListener("visibilitychange", handleVisibilityChange);
|
||||||
|
window.removeEventListener("online", handleOnline);
|
||||||
|
window.removeEventListener("offline", handleOffline);
|
||||||
|
initialized = false;
|
||||||
|
};
|
||||||
|
return unsubscribe;
|
||||||
|
});
|
||||||
export default store;
|
export default store;
|
||||||
|
|||||||
+14
-14
@@ -1,20 +1,20 @@
|
|||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: #ddd;
|
background: #ddd;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
width: 5px;
|
width: 5px;
|
||||||
height: 5px;
|
height: 5px;
|
||||||
}
|
}
|
||||||
:root{
|
:root {
|
||||||
/* border radius */
|
/* border radius */
|
||||||
--br:8px;
|
--br: 8px;
|
||||||
--rustchat-navs-bg:#E5E7EB;
|
--rustchat-navs-bg: #e5e7eb;
|
||||||
--rustchat-body-bg:#F5F6F7;
|
--rustchat-body-bg: #f5f6f7;
|
||||||
--rustchat-chat-bg:#fff;
|
--rustchat-chat-bg: #fff;
|
||||||
--rustchat-channel-text-color:#1C1C1E;
|
--rustchat-channel-text-color: #1c1c1e;
|
||||||
--rustchat-msg-text-color:#374151;
|
--rustchat-msg-text-color: #374151;
|
||||||
}
|
}
|
||||||
/* @media (prefers-color-scheme: dark) {
|
/* @media (prefers-color-scheme: dark) {
|
||||||
:root{
|
:root{
|
||||||
@@ -25,4 +25,4 @@
|
|||||||
--rustchat-channel-text-color:#fff;
|
--rustchat-channel-text-color:#fff;
|
||||||
--rustchat-msg-text-color:#fff;
|
--rustchat-msg-text-color:#fff;
|
||||||
}
|
}
|
||||||
} */
|
} */
|
||||||
|
|||||||
+151
-1294
File diff suppressed because it is too large
Load Diff
@@ -42,9 +42,7 @@ const Styled = styled.ul`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export default function AddEntriesMenu() {
|
export default function AddEntriesMenu() {
|
||||||
const currentUser = useSelector(
|
const currentUser = useSelector((store) => store.contacts.byId[store.authData.uid]);
|
||||||
(store) => store.contacts.byId[store.authData.uid]
|
|
||||||
);
|
|
||||||
const [isPrivate, setIsPrivate] = useState(false);
|
const [isPrivate, setIsPrivate] = useState(false);
|
||||||
const [inviteModalVisible, setInviteModalVisible] = useState(false);
|
const [inviteModalVisible, setInviteModalVisible] = useState(false);
|
||||||
|
|
||||||
@@ -78,10 +76,7 @@ export default function AddEntriesMenu() {
|
|||||||
<>
|
<>
|
||||||
<Styled>
|
<Styled>
|
||||||
{currentUser?.is_admin && (
|
{currentUser?.is_admin && (
|
||||||
<li
|
<li className="item" onClick={handleOpenChannelModal.bind(null, false)}>
|
||||||
className="item"
|
|
||||||
onClick={handleOpenChannelModal.bind(null, false)}
|
|
||||||
>
|
|
||||||
<ChannelIcon className="icon" />
|
<ChannelIcon className="icon" />
|
||||||
New Channel
|
New Channel
|
||||||
</li>
|
</li>
|
||||||
@@ -99,15 +94,9 @@ export default function AddEntriesMenu() {
|
|||||||
Invite People
|
Invite People
|
||||||
</li>
|
</li>
|
||||||
</Styled>
|
</Styled>
|
||||||
{channelModalVisible && (
|
{channelModalVisible && <ChannelModal personal={isPrivate} closeModal={handleCloseModal} />}
|
||||||
<ChannelModal personal={isPrivate} closeModal={handleCloseModal} />
|
{contactsModalVisible && <ContactsModal closeModal={toggleContactsModalVisible} />}
|
||||||
)}
|
{inviteModalVisible && <InviteModal closeModal={toggleInviteModalVisible} />}
|
||||||
{contactsModalVisible && (
|
|
||||||
<ContactsModal closeModal={toggleContactsModalVisible} />
|
|
||||||
)}
|
|
||||||
{inviteModalVisible && (
|
|
||||||
<InviteModal closeModal={toggleInviteModalVisible} />
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const Avatar = ({ url = "", name = "unkonw name", type = "user", ...rest }) => {
|
|||||||
const tmp = getInitialsAvatar({
|
const tmp = getInitialsAvatar({
|
||||||
initials: getInitials(name),
|
initials: getInitials(name),
|
||||||
background: type == "channel" ? "#EAECF0" : undefined,
|
background: type == "channel" ? "#EAECF0" : undefined,
|
||||||
foreground: type == "channel" ? "#475467" : undefined,
|
foreground: type == "channel" ? "#475467" : undefined
|
||||||
});
|
});
|
||||||
setSrc(tmp);
|
setSrc(tmp);
|
||||||
};
|
};
|
||||||
@@ -17,7 +17,7 @@ const Avatar = ({ url = "", name = "unkonw name", type = "user", ...rest }) => {
|
|||||||
const tmp = getInitialsAvatar({
|
const tmp = getInitialsAvatar({
|
||||||
initials: getInitials(name),
|
initials: getInitials(name),
|
||||||
background: type == "channel" ? "#EAECF0" : undefined,
|
background: type == "channel" ? "#EAECF0" : undefined,
|
||||||
foreground: type == "channel" ? "#475467" : undefined,
|
foreground: type == "channel" ? "#475467" : undefined
|
||||||
});
|
});
|
||||||
setSrc(tmp);
|
setSrc(tmp);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export default function AvatarUploader({
|
|||||||
name = "",
|
name = "",
|
||||||
type = "user",
|
type = "user",
|
||||||
uploadImage,
|
uploadImage,
|
||||||
disabled = false,
|
disabled = false
|
||||||
}) {
|
}) {
|
||||||
const [uploading, setUploading] = useState(false);
|
const [uploading, setUploading] = useState(false);
|
||||||
const handleUpload = async (evt) => {
|
const handleUpload = async (evt) => {
|
||||||
@@ -80,9 +80,7 @@ export default function AvatarUploader({
|
|||||||
<Avatar type={type} url={url} name={name} />
|
<Avatar type={type} url={url} name={name} />
|
||||||
{!disabled && (
|
{!disabled && (
|
||||||
<>
|
<>
|
||||||
<div className="tip">
|
<div className="tip">{uploading ? `Uploading` : `Change Avatar`}</div>
|
||||||
{uploading ? `Uploading` : `Change Avatar`}
|
|
||||||
</div>
|
|
||||||
<input
|
<input
|
||||||
multiple={false}
|
multiple={false}
|
||||||
onChange={handleUpload}
|
onChange={handleUpload}
|
||||||
|
|||||||
@@ -80,19 +80,16 @@ export default function BlankPlaceholder({ type = "chat" }) {
|
|||||||
setInviteModalVisible((prev) => !prev);
|
setInviteModalVisible((prev) => !prev);
|
||||||
};
|
};
|
||||||
const chatTip =
|
const chatTip =
|
||||||
type == "chat"
|
type == "chat" ? "Create a Channel to Start a Conversation" : "Send a Direct Message";
|
||||||
? "Create a Channel to Start a Conversation"
|
const chatHanlder = type == "chat" ? toggleChannelModalVisible : toggleContactListVisible;
|
||||||
: "Send a Direct Message";
|
|
||||||
const chatHanlder =
|
|
||||||
type == "chat" ? toggleChannelModalVisible : toggleContactListVisible;
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Styled>
|
<Styled>
|
||||||
<div className="head">
|
<div className="head">
|
||||||
<h2 className="title">Welcome to {server.name} server</h2>
|
<h2 className="title">Welcome to {server.name} server</h2>
|
||||||
<p className="desc">
|
<p className="desc">
|
||||||
Here are some steps to help you get started. For more, check out our
|
Here are some steps to help you get started. For more, check out our Getting Started
|
||||||
Getting Started guide
|
guide
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="boxes">
|
<div className="boxes">
|
||||||
@@ -117,12 +114,8 @@ export default function BlankPlaceholder({ type = "chat" }) {
|
|||||||
{createChannelVisible && (
|
{createChannelVisible && (
|
||||||
<ChannelModal personal={true} closeModal={toggleChannelModalVisible} />
|
<ChannelModal personal={true} closeModal={toggleChannelModalVisible} />
|
||||||
)}
|
)}
|
||||||
{contactListVisible && (
|
{contactListVisible && <ContactsModal closeModal={toggleContactListVisible} />}
|
||||||
<ContactsModal closeModal={toggleContactListVisible} />
|
{inviteModalVisible && <InviteModal closeModal={toggleInviteModalVisible} />}
|
||||||
)}
|
|
||||||
{inviteModalVisible && (
|
|
||||||
<InviteModal closeModal={toggleInviteModalVisible} />
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,16 +47,11 @@ const StyledWrapper = styled.div`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export default function Channel({
|
export default function Channel({ interactive = true, id = "", compact = false, avatarSize = 32 }) {
|
||||||
interactive = true,
|
|
||||||
id = "",
|
|
||||||
compact = false,
|
|
||||||
avatarSize = 32,
|
|
||||||
}) {
|
|
||||||
const { channel, totalMemberCount } = useSelector((store) => {
|
const { channel, totalMemberCount } = useSelector((store) => {
|
||||||
return {
|
return {
|
||||||
channel: store.channels.byId[id],
|
channel: store.channels.byId[id],
|
||||||
totalMemberCount: store.contacts.ids.length,
|
totalMemberCount: store.contacts.ids.length
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
console.log("channel item", id, channel);
|
console.log("channel item", id, channel);
|
||||||
@@ -65,17 +60,14 @@ export default function Channel({
|
|||||||
return (
|
return (
|
||||||
<StyledWrapper
|
<StyledWrapper
|
||||||
size={avatarSize}
|
size={avatarSize}
|
||||||
className={`${interactive ? "interactive" : ""} ${
|
className={`${interactive ? "interactive" : ""} ${compact ? "compact" : ""}`}
|
||||||
compact ? "compact" : ""
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<div className="avatar">
|
<div className="avatar">
|
||||||
<Avatar type="channel" url={avatar} name={"#"} alt="avatar" />
|
<Avatar type="channel" url={avatar} name={"#"} alt="avatar" />
|
||||||
</div>
|
</div>
|
||||||
{!compact && (
|
{!compact && (
|
||||||
<div className="name">
|
<div className="name">
|
||||||
<span className="txt">{name}</span> (
|
<span className="txt">{name}</span> ({is_public ? totalMemberCount : members.length})
|
||||||
{is_public ? totalMemberCount : members.length})
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</StyledWrapper>
|
</StyledWrapper>
|
||||||
|
|||||||
@@ -8,11 +8,7 @@ const Styled = styled.div`
|
|||||||
fill: #d0d5dd;
|
fill: #d0d5dd;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export default function ChannelIcon({
|
export default function ChannelIcon({ personal = false, muted = false, className }) {
|
||||||
personal = false,
|
|
||||||
muted = false,
|
|
||||||
className,
|
|
||||||
}) {
|
|
||||||
return (
|
return (
|
||||||
<Styled className={`${muted ? "muted" : ""} ${className}`}>
|
<Styled className={`${muted ? "muted" : ""} ${className}`}>
|
||||||
{personal ? <LockHashIcon /> : <HashIcon />}
|
{personal ? <LockHashIcon /> : <HashIcon />}
|
||||||
|
|||||||
@@ -22,13 +22,11 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
|||||||
name: "",
|
name: "",
|
||||||
dsecription: "",
|
dsecription: "",
|
||||||
members: [loginUid],
|
members: [loginUid],
|
||||||
is_public: !personal,
|
is_public: !personal
|
||||||
});
|
});
|
||||||
const { contacts, input, updateInput } = useFilteredUsers();
|
const { contacts, input, updateInput } = useFilteredUsers();
|
||||||
const [
|
const [createChannel, { isSuccess, isError, isLoading, data: newChannelId }] =
|
||||||
createChannel,
|
useCreateChannelMutation();
|
||||||
{ isSuccess, isError, isLoading, data: newChannelId },
|
|
||||||
] = useCreateChannelMutation();
|
|
||||||
|
|
||||||
const handleToggle = () => {
|
const handleToggle = () => {
|
||||||
const { is_public } = data;
|
const { is_public } = data;
|
||||||
@@ -72,9 +70,7 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
|||||||
const toggleCheckMember = ({ currentTarget }) => {
|
const toggleCheckMember = ({ currentTarget }) => {
|
||||||
const { members } = data;
|
const { members } = data;
|
||||||
const { uid } = currentTarget.dataset;
|
const { uid } = currentTarget.dataset;
|
||||||
let tmp = members.includes(+uid)
|
let tmp = members.includes(+uid) ? members.filter((m) => m != uid) : [...members, +uid];
|
||||||
? members.filter((m) => m != uid)
|
|
||||||
: [...members, +uid];
|
|
||||||
console.log(uid, currentTarget);
|
console.log(uid, currentTarget);
|
||||||
setData((prev) => {
|
setData((prev) => {
|
||||||
return { ...prev, members: tmp };
|
return { ...prev, members: tmp };
|
||||||
@@ -135,11 +131,7 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
|||||||
<div className="name">
|
<div className="name">
|
||||||
<span className="label normal">Channel Name</span>
|
<span className="label normal">Channel Name</span>
|
||||||
<div className="input">
|
<div className="input">
|
||||||
<input
|
<input onChange={handleNameInput} value={name} placeholder="new channel" />
|
||||||
onChange={handleNameInput}
|
|
||||||
value={name}
|
|
||||||
placeholder="new channel"
|
|
||||||
/>
|
|
||||||
<ChannelIcon personal={!is_public} className="icon" />
|
<ChannelIcon personal={!is_public} className="icon" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -155,11 +147,7 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
|||||||
<Button onClick={closeModal} className="normal cancel">
|
<Button onClick={closeModal} className="normal cancel">
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button disabled={isLoading} onClick={handleCreate} className="normal">
|
||||||
disabled={isLoading}
|
|
||||||
onClick={handleCreate}
|
|
||||||
className="normal"
|
|
||||||
>
|
|
||||||
Create
|
Create
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,14 +4,7 @@ import Tippy from "@tippyjs/react";
|
|||||||
import useContactOperation from "../../hook/useContactOperation";
|
import useContactOperation from "../../hook/useContactOperation";
|
||||||
import ContextMenu from "../ContextMenu";
|
import ContextMenu from "../ContextMenu";
|
||||||
|
|
||||||
export default function ContactContextMenu({
|
export default function ContactContextMenu({ enable = false, uid, cid, visible, hide, children }) {
|
||||||
enable = false,
|
|
||||||
uid,
|
|
||||||
cid,
|
|
||||||
visible,
|
|
||||||
hide,
|
|
||||||
children,
|
|
||||||
}) {
|
|
||||||
const {
|
const {
|
||||||
canCall,
|
canCall,
|
||||||
call,
|
call,
|
||||||
@@ -21,10 +14,10 @@ export default function ContactContextMenu({
|
|||||||
canRemove,
|
canRemove,
|
||||||
canRemoveFromChannel,
|
canRemoveFromChannel,
|
||||||
removeFromChannel,
|
removeFromChannel,
|
||||||
removeUser,
|
removeUser
|
||||||
} = useContactOperation({
|
} = useContactOperation({
|
||||||
uid,
|
uid,
|
||||||
cid,
|
cid
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -43,26 +36,26 @@ export default function ContactContextMenu({
|
|||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
title: "Message",
|
title: "Message",
|
||||||
handler: startChat,
|
handler: startChat
|
||||||
},
|
},
|
||||||
canCall && {
|
canCall && {
|
||||||
title: "Call",
|
title: "Call",
|
||||||
handler: call,
|
handler: call
|
||||||
},
|
},
|
||||||
canCopyEmail && {
|
canCopyEmail && {
|
||||||
title: "Copy Email",
|
title: "Copy Email",
|
||||||
handler: copyEmail,
|
handler: copyEmail
|
||||||
},
|
},
|
||||||
canRemoveFromChannel && {
|
canRemoveFromChannel && {
|
||||||
danger: true,
|
danger: true,
|
||||||
title: "Remove From Channel",
|
title: "Remove From Channel",
|
||||||
handler: removeFromChannel,
|
handler: removeFromChannel
|
||||||
},
|
},
|
||||||
canRemove && {
|
canRemove && {
|
||||||
danger: true,
|
danger: true,
|
||||||
title: "Remove From Server",
|
title: "Remove From Server",
|
||||||
handler: removeUser,
|
handler: removeUser
|
||||||
},
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,14 +17,10 @@ export default function Contact({
|
|||||||
popover = false,
|
popover = false,
|
||||||
compact = false,
|
compact = false,
|
||||||
avatarSize = 32,
|
avatarSize = 32,
|
||||||
enableContextMenu = false,
|
enableContextMenu = false
|
||||||
}) {
|
}) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const {
|
const { visible: contextMenuVisible, handleContextMenuEvent, hideContextMenu } = useContextMenu();
|
||||||
visible: contextMenuVisible,
|
|
||||||
handleContextMenuEvent,
|
|
||||||
hideContextMenu,
|
|
||||||
} = useContextMenu();
|
|
||||||
const curr = useSelector((store) => store.contacts.byId[uid]);
|
const curr = useSelector((store) => store.contacts.byId[uid]);
|
||||||
const handleDoubleClick = () => {
|
const handleDoubleClick = () => {
|
||||||
navigate(`/chat/dm/${uid}`);
|
navigate(`/chat/dm/${uid}`);
|
||||||
@@ -50,15 +46,11 @@ export default function Contact({
|
|||||||
onContextMenu={enableContextMenu ? handleContextMenuEvent : null}
|
onContextMenu={enableContextMenu ? handleContextMenuEvent : null}
|
||||||
size={avatarSize}
|
size={avatarSize}
|
||||||
onDoubleClick={dm ? handleDoubleClick : null}
|
onDoubleClick={dm ? handleDoubleClick : null}
|
||||||
className={`${interactive ? "interactive" : ""} ${
|
className={`${interactive ? "interactive" : ""} ${compact ? "compact" : ""}`}
|
||||||
compact ? "compact" : ""
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<div className="avatar">
|
<div className="avatar">
|
||||||
<Avatar url={curr.avatar} name={curr.name} alt="avatar" />
|
<Avatar url={curr.avatar} name={curr.name} alt="avatar" />
|
||||||
<div
|
<div className={`status ${curr.online ? "online" : "offline"}`}></div>
|
||||||
className={`status ${curr.online ? "online" : "offline"}`}
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
{!compact && <span className="name">{curr?.name}</span>}
|
{!compact && <span className="name">{curr?.name}</span>}
|
||||||
{owner && <IconOwner />}
|
{owner && <IconOwner />}
|
||||||
|
|||||||
@@ -64,11 +64,7 @@ export default function ContactsModal({ closeModal }) {
|
|||||||
<Modal>
|
<Modal>
|
||||||
<StyledWrapper ref={wrapperRef}>
|
<StyledWrapper ref={wrapperRef}>
|
||||||
<div className="search">
|
<div className="search">
|
||||||
<input
|
<input value={input} onChange={handleSearch} placeholder="Type Username to search" />
|
||||||
value={input}
|
|
||||||
onChange={handleSearch}
|
|
||||||
placeholder="Type Username to search"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
{contacts && (
|
{contacts && (
|
||||||
<ul className="users">
|
<ul className="users">
|
||||||
|
|||||||
@@ -16,13 +16,11 @@ export default function ContextMenu({ items = [], hideMenu = null }) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
underline = false,
|
underline = false,
|
||||||
danger = false,
|
danger = false
|
||||||
} = item;
|
} = item;
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
className={`item ${underline ? "underline" : ""} ${
|
className={`item ${underline ? "underline" : ""} ${danger ? "danger" : ""}`}
|
||||||
danger ? "danger" : ""
|
|
||||||
}`}
|
|
||||||
key={title}
|
key={title}
|
||||||
onClick={(evt) => {
|
onClick={(evt) => {
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
|
|||||||
@@ -25,11 +25,7 @@ export default function DeleteMessageConfirmModal({ closeModal, mids = [] }) {
|
|||||||
<Button className="cancel" onClick={closeModal.bind(null, false)}>
|
<Button className="cancel" onClick={closeModal.bind(null, false)}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button disabled={isDeleting} onClick={handleDelete} className="danger">
|
||||||
disabled={isDeleting}
|
|
||||||
onClick={handleDelete}
|
|
||||||
className="danger"
|
|
||||||
>
|
|
||||||
{isDeleting ? "Deleting" : `Delete`}
|
{isDeleting ? "Deleting" : `Delete`}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -13,9 +13,7 @@ export default function FAQ() {
|
|||||||
<Styled>
|
<Styled>
|
||||||
<div className="item">Client Version: {process.env.VERSION}</div>
|
<div className="item">Client Version: {process.env.VERSION}</div>
|
||||||
<div className="item">Server Version: {serverVersion}</div>
|
<div className="item">Server Version: {serverVersion}</div>
|
||||||
<div className="item">
|
<div className="item">Build Timestamp: {process.env.REACT_APP_BUILD_TIME}</div>
|
||||||
Build Timestamp: {process.env.REACT_APP_BUILD_TIME}
|
|
||||||
</div>
|
|
||||||
</Styled>
|
</Styled>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
ImagePreview,
|
ImagePreview,
|
||||||
PdfPreview,
|
PdfPreview,
|
||||||
CodePreview,
|
CodePreview,
|
||||||
DocPreview,
|
DocPreview
|
||||||
} from "./preview";
|
} from "./preview";
|
||||||
import { getFileIcon, formatBytes } from "../../utils";
|
import { getFileIcon, formatBytes } from "../../utils";
|
||||||
import IconDownload from "../../../assets/icons/download.svg";
|
import IconDownload from "../../../assets/icons/download.svg";
|
||||||
@@ -24,7 +24,7 @@ const renderPreview = (data) => {
|
|||||||
video: /^video/gi,
|
video: /^video/gi,
|
||||||
code: /(json|javascript|java|rb|c|php|xml|css|html)$/gi,
|
code: /(json|javascript|java|rb|c|php|xml|css|html)$/gi,
|
||||||
doc: /^text/gi,
|
doc: /^text/gi,
|
||||||
pdf: /\/pdf$/gi,
|
pdf: /\/pdf$/gi
|
||||||
};
|
};
|
||||||
const _arr = name.split(".");
|
const _arr = name.split(".");
|
||||||
const _type = file_type || _arr[_arr.length - 1];
|
const _type = file_type || _arr[_arr.length - 1];
|
||||||
@@ -65,7 +65,7 @@ export default function FileBox({
|
|||||||
size,
|
size,
|
||||||
created_at,
|
created_at,
|
||||||
from_uid,
|
from_uid,
|
||||||
content,
|
content
|
||||||
}) {
|
}) {
|
||||||
const fromUser = useSelector((store) => store.contacts.byId[from_uid]);
|
const fromUser = useSelector((store) => store.contacts.byId[from_uid]);
|
||||||
const icon = getFileIcon(file_type, name);
|
const icon = getFileIcon(file_type, name);
|
||||||
@@ -75,9 +75,9 @@ export default function FileBox({
|
|||||||
const withPreview = preview && previewContent;
|
const withPreview = preview && previewContent;
|
||||||
return (
|
return (
|
||||||
<Styled
|
<Styled
|
||||||
className={`file_box ${flex ? "flex" : ""} ${
|
className={`file_box ${flex ? "flex" : ""} ${withPreview ? "preview" : ""} ${
|
||||||
withPreview ? "preview" : ""
|
file_type.startsWith("audio") ? "audio" : ""
|
||||||
} ${file_type.startsWith("audio") ? "audio" : ""}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="basic">
|
<div className="basic">
|
||||||
{icon}
|
{icon}
|
||||||
@@ -91,11 +91,7 @@ export default function FileBox({
|
|||||||
</i>
|
</i>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a className="download" download={name} href={`${content}&download=true`}>
|
||||||
className="download"
|
|
||||||
download={name}
|
|
||||||
href={`${content}&download=true`}
|
|
||||||
>
|
|
||||||
<IconDownload />
|
<IconDownload />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,11 +5,4 @@ import PdfPreview from "./Pdf";
|
|||||||
import CodePreview from "./Code";
|
import CodePreview from "./Code";
|
||||||
import DocPreview from "./Doc";
|
import DocPreview from "./Doc";
|
||||||
|
|
||||||
export {
|
export { VideoPreview, AudioPreview, ImagePreview, PdfPreview, CodePreview, DocPreview };
|
||||||
VideoPreview,
|
|
||||||
AudioPreview,
|
|
||||||
ImagePreview,
|
|
||||||
PdfPreview,
|
|
||||||
CodePreview,
|
|
||||||
DocPreview,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default function ImageMessage({
|
|||||||
thumbnail,
|
thumbnail,
|
||||||
download,
|
download,
|
||||||
content,
|
content,
|
||||||
properties = {},
|
properties = {}
|
||||||
}) {
|
}) {
|
||||||
const [url, setUrl] = useState(thumbnail);
|
const [url, setUrl] = useState(thumbnail);
|
||||||
const { width = 0, height = 0 } = getDefaultSize(properties);
|
const { width = 0, height = 0 } = getDefaultSize(properties);
|
||||||
@@ -64,7 +64,7 @@ export default function ImageMessage({
|
|||||||
strokeWidth={50}
|
strokeWidth={50}
|
||||||
styles={buildStyles({
|
styles={buildStyles({
|
||||||
storke: "#000",
|
storke: "#000",
|
||||||
strokeLinecap: "butt",
|
strokeLinecap: "butt"
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -74,7 +74,7 @@ export default function ImageMessage({
|
|||||||
className="img preview"
|
className="img preview"
|
||||||
style={{
|
style={{
|
||||||
width: width ? `${width}px` : "",
|
width: width ? `${width}px` : "",
|
||||||
height: height ? `${height}px` : "",
|
height: height ? `${height}px` : ""
|
||||||
}}
|
}}
|
||||||
data-meta={JSON.stringify(properties)}
|
data-meta={JSON.stringify(properties)}
|
||||||
data-origin={content}
|
data-origin={content}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default function FileMessage({
|
|||||||
content = "",
|
content = "",
|
||||||
download = "",
|
download = "",
|
||||||
thumbnail = "",
|
thumbnail = "",
|
||||||
properties = { local_id: 0, name: "", size: 0, content_type: "" },
|
properties = { local_id: 0, name: "", size: 0, content_type: "" }
|
||||||
}) {
|
}) {
|
||||||
const [imageSize, setImageSize] = useState(null);
|
const [imageSize, setImageSize] = useState(null);
|
||||||
const [uploadingFile, setUploadingFile] = useState(false);
|
const [uploadingFile, setUploadingFile] = useState(false);
|
||||||
@@ -31,19 +31,13 @@ export default function FileMessage({
|
|||||||
const {
|
const {
|
||||||
sendMessage,
|
sendMessage,
|
||||||
isSuccess: sendMessageSuccess,
|
isSuccess: sendMessageSuccess,
|
||||||
isSending,
|
isSending
|
||||||
} = useSendMessage({
|
} = useSendMessage({
|
||||||
context,
|
context,
|
||||||
from: from_uid,
|
from: from_uid,
|
||||||
to,
|
to
|
||||||
});
|
});
|
||||||
const {
|
const { stopUploading, data, uploadFile, progress, isSuccess: uploadSuccess } = useUploadFile();
|
||||||
stopUploading,
|
|
||||||
data,
|
|
||||||
uploadFile,
|
|
||||||
progress,
|
|
||||||
isSuccess: uploadSuccess,
|
|
||||||
} = useUploadFile();
|
|
||||||
const fromUser = useSelector((store) => store.contacts.byId[from_uid]);
|
const fromUser = useSelector((store) => store.contacts.byId[from_uid]);
|
||||||
const { size, name, content_type } = properties ?? {};
|
const { size, name, content_type } = properties ?? {};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -75,20 +69,14 @@ export default function FileMessage({
|
|||||||
const propsV2 = imageSize ? { ...props, ...imageSize } : props;
|
const propsV2 = imageSize ? { ...props, ...imageSize } : props;
|
||||||
// 本地文件 并且上传成功
|
// 本地文件 并且上传成功
|
||||||
if (uploadSuccess && isLocalFile(content)) {
|
if (uploadSuccess && isLocalFile(content)) {
|
||||||
console.log(
|
console.log("send local file message", uploadSuccess, propsV2, data, content);
|
||||||
"send local file message",
|
|
||||||
uploadSuccess,
|
|
||||||
propsV2,
|
|
||||||
data,
|
|
||||||
content
|
|
||||||
);
|
|
||||||
// 把已经上传的东西当做消息发出去
|
// 把已经上传的东西当做消息发出去
|
||||||
const { path } = data;
|
const { path } = data;
|
||||||
sendMessage({
|
sendMessage({
|
||||||
ignoreLocal: true,
|
ignoreLocal: true,
|
||||||
type: "file",
|
type: "file",
|
||||||
content: { path },
|
content: { path },
|
||||||
properties: propsV2,
|
properties: propsV2
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [uploadSuccess, data, properties, content]);
|
}, [uploadSuccess, data, properties, content]);
|
||||||
@@ -151,11 +139,7 @@ export default function FileMessage({
|
|||||||
{sending ? (
|
{sending ? (
|
||||||
<IconClose className="cancel" onClick={handleCancel} />
|
<IconClose className="cancel" onClick={handleCancel} />
|
||||||
) : (
|
) : (
|
||||||
<a
|
<a className="download" download={name} href={`${content}&download=true`}>
|
||||||
className="download"
|
|
||||||
download={name}
|
|
||||||
href={`${content}&download=true`}
|
|
||||||
>
|
|
||||||
<IconDownload />
|
<IconDownload />
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
|||||||
const {
|
const {
|
||||||
channels,
|
channels,
|
||||||
// input: channelInput,
|
// input: channelInput,
|
||||||
updateInput: updateChannelInput,
|
updateInput: updateChannelInput
|
||||||
} = useFilteredChannels();
|
} = useFilteredChannels();
|
||||||
const { contacts, input, updateInput } = useFilteredUsers();
|
const { contacts, input, updateInput } = useFilteredUsers();
|
||||||
// const { conactsData, loginUid } = useSelector((store) => {
|
// const { conactsData, loginUid } = useSelector((store) => {
|
||||||
@@ -34,8 +34,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
|||||||
const toggleCheck = ({ currentTarget }) => {
|
const toggleCheck = ({ currentTarget }) => {
|
||||||
const { id, type = "user" } = currentTarget.dataset;
|
const { id, type = "user" } = currentTarget.dataset;
|
||||||
const ids = type == "user" ? selectedMembers : selectedChannels;
|
const ids = type == "user" ? selectedMembers : selectedChannels;
|
||||||
const updateState =
|
const updateState = type == "user" ? setSelectedMembers : setSelectedChannels;
|
||||||
type == "user" ? setSelectedMembers : setSelectedChannels;
|
|
||||||
let tmp = ids.includes(+id) ? ids.filter((m) => m != id) : [...ids, +id];
|
let tmp = ids.includes(+id) ? ids.filter((m) => m != id) : [...ids, +id];
|
||||||
console.log(id, currentTarget);
|
console.log(id, currentTarget);
|
||||||
updateState(tmp);
|
updateState(tmp);
|
||||||
@@ -47,13 +46,13 @@ export default function ForwardModal({ mids, closeModal }) {
|
|||||||
await forwardMessage({
|
await forwardMessage({
|
||||||
mids: mids.map((mid) => +mid),
|
mids: mids.map((mid) => +mid),
|
||||||
users: selectedMembers,
|
users: selectedMembers,
|
||||||
channels: selectedChannels,
|
channels: selectedChannels
|
||||||
});
|
});
|
||||||
if (appendText.trim()) {
|
if (appendText.trim()) {
|
||||||
await sendMessages({
|
await sendMessages({
|
||||||
content: appendText,
|
content: appendText,
|
||||||
users: selectedMembers,
|
users: selectedMembers,
|
||||||
channels: selectedChannels,
|
channels: selectedChannels
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
toast.success("Forward Message Successfully");
|
toast.success("Forward Message Successfully");
|
||||||
@@ -99,12 +98,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
|||||||
className="user channel"
|
className="user channel"
|
||||||
onClick={toggleCheck}
|
onClick={toggleCheck}
|
||||||
>
|
>
|
||||||
<StyledCheckbox
|
<StyledCheckbox readOnly checked={checked} name="cb" id="cb" />
|
||||||
readOnly
|
|
||||||
checked={checked}
|
|
||||||
name="cb"
|
|
||||||
id="cb"
|
|
||||||
/>
|
|
||||||
<Channel id={gid} interactive={false} />
|
<Channel id={gid} interactive={false} />
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
@@ -122,12 +116,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
|||||||
className="user"
|
className="user"
|
||||||
onClick={toggleCheck}
|
onClick={toggleCheck}
|
||||||
>
|
>
|
||||||
<StyledCheckbox
|
<StyledCheckbox readOnly checked={checked} name="cb" id="cb" />
|
||||||
readOnly
|
|
||||||
checked={checked}
|
|
||||||
name="cb"
|
|
||||||
id="cb"
|
|
||||||
/>
|
|
||||||
<Contact uid={uid} interactive={false} />
|
<Contact uid={uid} interactive={false} />
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
@@ -162,10 +151,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
|||||||
interactive={false}
|
interactive={false}
|
||||||
// avatarSize={40}
|
// avatarSize={40}
|
||||||
/>
|
/>
|
||||||
<CloseIcon
|
<CloseIcon className="remove" onClick={removeSelected.bind(null, uid, "user")} />
|
||||||
className="remove"
|
|
||||||
onClick={removeSelected.bind(null, uid, "user")}
|
|
||||||
/>
|
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -185,11 +171,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
|||||||
<Button onClick={closeModal} className="normal cancel">
|
<Button onClick={closeModal} className="normal cancel">
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button className="normal" disabled={sendButtonDisabled} onClick={handleForward}>
|
||||||
className="normal"
|
|
||||||
disabled={sendButtonDisabled}
|
|
||||||
onClick={handleForward}
|
|
||||||
>
|
|
||||||
Send To {selectedCount == 0 ? null : `(${selectedCount})`}
|
Send To {selectedCount == 0 ? null : `(${selectedCount})`}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -58,11 +58,7 @@ const StyledWrapper = styled.div`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export default function ImagePreviewModal({
|
export default function ImagePreviewModal({ download = true, data, closeModal }) {
|
||||||
download = true,
|
|
||||||
data,
|
|
||||||
closeModal,
|
|
||||||
}) {
|
|
||||||
const [url, setUrl] = useState(data?.thumbnail);
|
const [url, setUrl] = useState(data?.thumbnail);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const wrapperRef = useRef();
|
const wrapperRef = useRef();
|
||||||
|
|||||||
@@ -38,28 +38,15 @@ const StyledWrapper = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export default function InviteLink() {
|
export default function InviteLink() {
|
||||||
const {
|
const { generating, link, linkCopied, copyLink, generateNewLink } = useInviteLink();
|
||||||
generating,
|
|
||||||
link,
|
|
||||||
linkCopied,
|
|
||||||
copyLink,
|
|
||||||
generateNewLink,
|
|
||||||
} = useInviteLink();
|
|
||||||
const handleNewLink = () => {
|
const handleNewLink = () => {
|
||||||
generateNewLink();
|
generateNewLink();
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<StyledWrapper>
|
<StyledWrapper>
|
||||||
<span className="tip">
|
<span className="tip">Share this link to invite people to this server.</span>
|
||||||
Share this link to invite people to this server.
|
|
||||||
</span>
|
|
||||||
<div className="link">
|
<div className="link">
|
||||||
<Input
|
<Input readOnly className={"large"} placeholder="Generating" value={link} />
|
||||||
readOnly
|
|
||||||
className={"large"}
|
|
||||||
placeholder="Generating"
|
|
||||||
value={link}
|
|
||||||
/>
|
|
||||||
<Button onClick={copyLink} className="ghost small border_less">
|
<Button onClick={copyLink} className="ghost small border_less">
|
||||||
{linkCopied ? "Copied" : `Copy`}
|
{linkCopied ? "Copied" : `Copy`}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -97,15 +97,12 @@ const Styled = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export default function AddMembers({ cid = null, closeModal }) {
|
export default function AddMembers({ cid = null, closeModal }) {
|
||||||
const [
|
const [addMembers, { isLoading: isAdding, isSuccess }] = useAddMembersMutation();
|
||||||
addMembers,
|
|
||||||
{ isLoading: isAdding, isSuccess },
|
|
||||||
] = useAddMembersMutation();
|
|
||||||
const [selects, setSelects] = useState([]);
|
const [selects, setSelects] = useState([]);
|
||||||
const { channel, contactData } = useSelector((store) => {
|
const { channel, contactData } = useSelector((store) => {
|
||||||
return {
|
return {
|
||||||
channel: store.channels.byId[cid],
|
channel: store.channels.byId[cid],
|
||||||
contactData: store.contacts.byId,
|
contactData: store.contacts.byId
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -145,11 +142,7 @@ export default function AddMembers({ cid = null, closeModal }) {
|
|||||||
return (
|
return (
|
||||||
<li className="select" key={uid}>
|
<li className="select" key={uid}>
|
||||||
{contactData[uid].name}
|
{contactData[uid].name}
|
||||||
<CloseIcon
|
<CloseIcon data-uid={uid} onClick={toggleCheckMember} className="close" />
|
||||||
data-uid={uid}
|
|
||||||
onClick={toggleCheckMember}
|
|
||||||
className="close"
|
|
||||||
/>
|
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -185,11 +178,7 @@ export default function AddMembers({ cid = null, closeModal }) {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
<Button
|
<Button disabled={selects.length == 0 || isAdding} className="btn" onClick={handleAddMembers}>
|
||||||
disabled={selects.length == 0 || isAdding}
|
|
||||||
className="btn"
|
|
||||||
onClick={handleAddMembers}
|
|
||||||
>
|
|
||||||
{isAdding ? `Adding` : "Add"} to #{channel.name}
|
{isAdding ? `Adding` : "Add"} to #{channel.name}
|
||||||
</Button>
|
</Button>
|
||||||
</Styled>
|
</Styled>
|
||||||
|
|||||||
@@ -68,14 +68,8 @@ import Button from "../styled/Button";
|
|||||||
import Input from "../styled/Input";
|
import Input from "../styled/Input";
|
||||||
export default function InviteByEmail({ cid = null }) {
|
export default function InviteByEmail({ cid = null }) {
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const {
|
const { enableSMTP, linkCopied, link, copyLink, generateNewLink, generating } =
|
||||||
enableSMTP,
|
useInviteLink(cid);
|
||||||
linkCopied,
|
|
||||||
link,
|
|
||||||
copyLink,
|
|
||||||
generateNewLink,
|
|
||||||
generating,
|
|
||||||
} = useInviteLink(cid);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (linkCopied) {
|
if (linkCopied) {
|
||||||
toast.success("Invite Link Copied!");
|
toast.success("Invite Link Copied!");
|
||||||
@@ -104,12 +98,7 @@ export default function InviteByEmail({ cid = null }) {
|
|||||||
<div className="link">
|
<div className="link">
|
||||||
<label htmlFor="">Or Send invite link to your friends</label>
|
<label htmlFor="">Or Send invite link to your friends</label>
|
||||||
<div className="input">
|
<div className="input">
|
||||||
<Input
|
<Input readOnly className="invite" placeholder="Generating" value={link} />
|
||||||
readOnly
|
|
||||||
className="invite"
|
|
||||||
placeholder="Generating"
|
|
||||||
value={link}
|
|
||||||
/>
|
|
||||||
<button className="copy" onClick={copyLink}>
|
<button className="copy" onClick={copyLink}>
|
||||||
Copy
|
Copy
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -28,20 +28,14 @@ const Styled = styled.div`
|
|||||||
`;
|
`;
|
||||||
import Modal from "../Modal";
|
import Modal from "../Modal";
|
||||||
// type: server,channel
|
// type: server,channel
|
||||||
export default function InviteModal({
|
export default function InviteModal({ type = "server", cid = null, title = "", closeModal }) {
|
||||||
type = "server",
|
|
||||||
cid = null,
|
|
||||||
title = "",
|
|
||||||
closeModal,
|
|
||||||
}) {
|
|
||||||
const { channel, server } = useSelector((store) => {
|
const { channel, server } = useSelector((store) => {
|
||||||
return {
|
return {
|
||||||
channel: store.channels.byId[cid],
|
channel: store.channels.byId[cid],
|
||||||
server: store.server,
|
server: store.server
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const finalTitle =
|
const finalTitle = type == "server" ? server.name : `#${title || channel?.name}`;
|
||||||
type == "server" ? server.name : `#${title || channel?.name}`;
|
|
||||||
return (
|
return (
|
||||||
<Modal>
|
<Modal>
|
||||||
<Styled>
|
<Styled>
|
||||||
@@ -49,9 +43,7 @@ export default function InviteModal({
|
|||||||
Add friends to {finalTitle}
|
Add friends to {finalTitle}
|
||||||
<CloseIcon className="close" onClick={closeModal} />
|
<CloseIcon className="close" onClick={closeModal} />
|
||||||
</h2>
|
</h2>
|
||||||
{!channel?.is_public && (
|
{!channel?.is_public && <AddMembers cid={cid} closeModal={closeModal} />}
|
||||||
<AddMembers cid={cid} closeModal={closeModal} />
|
|
||||||
)}
|
|
||||||
<InviteByEmail cid={channel?.is_public ? null : cid} />
|
<InviteByEmail cid={channel?.is_public ? null : cid} />
|
||||||
</Styled>
|
</Styled>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -30,10 +30,7 @@ export default function LeaveConfirmModal({ id, closeModal, handleNextStep }) {
|
|||||||
}
|
}
|
||||||
buttons={
|
buttons={
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button onClick={closeModal.bind(null, undefined)} className="cancel">
|
||||||
onClick={closeModal.bind(null, undefined)}
|
|
||||||
className="cancel"
|
|
||||||
>
|
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
{isOwner ? (
|
{isOwner ? (
|
||||||
|
|||||||
@@ -28,11 +28,7 @@ const UserList = styled.ul`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export default function TransferOwnerModal({
|
export default function TransferOwnerModal({ id, closeModal, withLeave = true }) {
|
||||||
id,
|
|
||||||
closeModal,
|
|
||||||
withLeave = true,
|
|
||||||
}) {
|
|
||||||
const {
|
const {
|
||||||
transferOwner,
|
transferOwner,
|
||||||
otherMembers,
|
otherMembers,
|
||||||
@@ -40,7 +36,7 @@ export default function TransferOwnerModal({
|
|||||||
leaveChannel,
|
leaveChannel,
|
||||||
leaveSuccess,
|
leaveSuccess,
|
||||||
transferSuccess,
|
transferSuccess,
|
||||||
transfering,
|
transfering
|
||||||
} = useLeaveChannel(id);
|
} = useLeaveChannel(id);
|
||||||
|
|
||||||
const [uid, setUid] = useState(null);
|
const [uid, setUid] = useState(null);
|
||||||
@@ -75,17 +71,10 @@ export default function TransferOwnerModal({
|
|||||||
description={"This cannot be undone."}
|
description={"This cannot be undone."}
|
||||||
buttons={
|
buttons={
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button onClick={closeModal.bind(null, undefined)} className="cancel">
|
||||||
onClick={closeModal.bind(null, undefined)}
|
|
||||||
className="cancel"
|
|
||||||
>
|
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button disabled={!uid} onClick={handleTransferAndLeave} className="danger">
|
||||||
disabled={!uid}
|
|
||||||
onClick={handleTransferAndLeave}
|
|
||||||
className="danger"
|
|
||||||
>
|
|
||||||
{operating ? "Assigning" : `Assign and Leave`}
|
{operating ? "Assigning" : `Assign and Leave`}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -2,22 +2,11 @@ import { useState } from "react";
|
|||||||
// import styled from "styled-components";
|
// import styled from "styled-components";
|
||||||
import TransferOwnerModal from "./TransferOwnerModal";
|
import TransferOwnerModal from "./TransferOwnerModal";
|
||||||
import LeaveConfirmModal from "./LeaveConfirmModal";
|
import LeaveConfirmModal from "./LeaveConfirmModal";
|
||||||
export default function LeaveChannel({
|
export default function LeaveChannel({ id = null, isOwner = false, closeModal }) {
|
||||||
id = null,
|
|
||||||
isOwner = false,
|
|
||||||
closeModal,
|
|
||||||
}) {
|
|
||||||
const [transferOwner, setTransferOwner] = useState(isOwner);
|
const [transferOwner, setTransferOwner] = useState(isOwner);
|
||||||
const handleNextStep = () => {
|
const handleNextStep = () => {
|
||||||
setTransferOwner(true);
|
setTransferOwner(true);
|
||||||
};
|
};
|
||||||
if (transferOwner)
|
if (transferOwner) return <TransferOwnerModal id={id} closeModal={closeModal} />;
|
||||||
return <TransferOwnerModal id={id} closeModal={closeModal} />;
|
return <LeaveConfirmModal id={id} closeModal={closeModal} handleNextStep={handleNextStep} />;
|
||||||
return (
|
|
||||||
<LeaveConfirmModal
|
|
||||||
id={id}
|
|
||||||
closeModal={closeModal}
|
|
||||||
handleNextStep={handleNextStep}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,17 +45,8 @@ export default function Loading({ reload = false, fullscreen = false }) {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<StyledWrapper className={fullscreen ? "fullscreen" : ""}>
|
<StyledWrapper className={fullscreen ? "fullscreen" : ""}>
|
||||||
<Ring
|
<Ring className="loading" size={40} lineWeight={5} speed={2} color="black" />
|
||||||
className="loading"
|
<Button className={`reload danger ${reload ? "visible" : ""}`} onClick={handleReload}>
|
||||||
size={40}
|
|
||||||
lineWeight={5}
|
|
||||||
speed={2}
|
|
||||||
color="black"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
className={`reload danger ${reload ? "visible" : ""}`}
|
|
||||||
onClick={handleReload}
|
|
||||||
>
|
|
||||||
Reload
|
Reload
|
||||||
</Button>
|
</Button>
|
||||||
</StyledWrapper>
|
</StyledWrapper>
|
||||||
|
|||||||
@@ -119,20 +119,12 @@ export default function ManageMembers({ cid = null }) {
|
|||||||
return {
|
return {
|
||||||
contacts: store.contacts,
|
contacts: store.contacts,
|
||||||
channels: store.channels,
|
channels: store.channels,
|
||||||
loginUser: store.contacts.byId[store.authData.uid],
|
loginUser: store.contacts.byId[store.authData.uid]
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const {
|
const { copyEmail, removeFromChannel, removeUser, canRemove, canRemoveFromChannel } =
|
||||||
copyEmail,
|
useContactOperation({ cid });
|
||||||
removeFromChannel,
|
const [updateContact, { isSuccess: updateSuccess }] = useUpdateContactMutation();
|
||||||
removeUser,
|
|
||||||
canRemove,
|
|
||||||
canRemoveFromChannel,
|
|
||||||
} = useContactOperation({ cid });
|
|
||||||
const [
|
|
||||||
updateContact,
|
|
||||||
{ isSuccess: updateSuccess },
|
|
||||||
] = useUpdateContactMutation();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (updateSuccess) {
|
if (updateSuccess) {
|
||||||
@@ -146,11 +138,7 @@ export default function ManageMembers({ cid = null }) {
|
|||||||
updateContact({ id: uid, is_admin: isAdmin });
|
updateContact({ id: uid, is_admin: isAdmin });
|
||||||
};
|
};
|
||||||
const channel = channels.byId[cid] ?? null;
|
const channel = channels.byId[cid] ?? null;
|
||||||
const uids = channel
|
const uids = channel ? (channel.is_public ? contacts.ids : channel.members) : contacts.ids;
|
||||||
? channel.is_public
|
|
||||||
? contacts.ids
|
|
||||||
: channel.members
|
|
||||||
: contacts.ids;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledWrapper>
|
<StyledWrapper>
|
||||||
@@ -158,8 +146,7 @@ export default function ManageMembers({ cid = null }) {
|
|||||||
<div className="intro">
|
<div className="intro">
|
||||||
<h4 className="title">Manage Members</h4>
|
<h4 className="title">Manage Members</h4>
|
||||||
<p className="desc">
|
<p className="desc">
|
||||||
Disabling your account means you can recover it at any time after
|
Disabling your account means you can recover it at any time after taking this action.
|
||||||
taking this action.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<ul className="members">
|
<ul className="members">
|
||||||
@@ -194,7 +181,7 @@ export default function ManageMembers({ cid = null }) {
|
|||||||
onClick={handleToggleRole.bind(null, {
|
onClick={handleToggleRole.bind(null, {
|
||||||
ignore: is_admin,
|
ignore: is_admin,
|
||||||
uid,
|
uid,
|
||||||
isAdmin: true,
|
isAdmin: true
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
Admin
|
Admin
|
||||||
@@ -205,7 +192,7 @@ export default function ManageMembers({ cid = null }) {
|
|||||||
onClick={handleToggleRole.bind(null, {
|
onClick={handleToggleRole.bind(null, {
|
||||||
ignore: !is_admin,
|
ignore: !is_admin,
|
||||||
uid,
|
uid,
|
||||||
isAdmin: false,
|
isAdmin: false
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
User
|
User
|
||||||
@@ -226,10 +213,7 @@ export default function ManageMembers({ cid = null }) {
|
|||||||
content={
|
content={
|
||||||
<StyledMenu className="menu">
|
<StyledMenu className="menu">
|
||||||
{email && (
|
{email && (
|
||||||
<li
|
<li className="item" onClick={copyEmail.bind(null, email)}>
|
||||||
className="item"
|
|
||||||
onClick={copyEmail.bind(null, email)}
|
|
||||||
>
|
|
||||||
Copy Email
|
Copy Email
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
@@ -237,18 +221,12 @@ export default function ManageMembers({ cid = null }) {
|
|||||||
{/* <li className="item underline">Change Nickname</li> */}
|
{/* <li className="item underline">Change Nickname</li> */}
|
||||||
{/* <li className="item danger">Ban</li> */}
|
{/* <li className="item danger">Ban</li> */}
|
||||||
{canRemoveFromChannel && (
|
{canRemoveFromChannel && (
|
||||||
<li
|
<li className="item danger" onClick={removeFromChannel.bind(null, uid)}>
|
||||||
className="item danger"
|
|
||||||
onClick={removeFromChannel.bind(null, uid)}
|
|
||||||
>
|
|
||||||
Remove From Channel
|
Remove From Channel
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
{canRemove && !cid && (
|
{canRemove && !cid && (
|
||||||
<li
|
<li className="item danger" onClick={removeUser.bind(null, uid)}>
|
||||||
className="item danger"
|
|
||||||
onClick={removeUser.bind(null, uid)}
|
|
||||||
>
|
|
||||||
Remove From Server
|
Remove From Server
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ export default function usePrompt() {
|
|||||||
return {
|
return {
|
||||||
setCanneled: setPrompt,
|
setCanneled: setPrompt,
|
||||||
prompted: !!localStorage.getItem(KEY_PWA_INSTALLED),
|
prompted: !!localStorage.getItem(KEY_PWA_INSTALLED),
|
||||||
resetPrompt,
|
resetPrompt
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ function MarkdownEditor({
|
|||||||
height = "50vh",
|
height = "50vh",
|
||||||
placeholder,
|
placeholder,
|
||||||
sendMarkdown,
|
sendMarkdown,
|
||||||
setEditorInstance,
|
setEditorInstance
|
||||||
}) {
|
}) {
|
||||||
const editorRef = useRef(undefined);
|
const editorRef = useRef(undefined);
|
||||||
const { uploadFile } = useUploadFile();
|
const { uploadFile } = useUploadFile();
|
||||||
|
|||||||
@@ -62,12 +62,7 @@ const StyledCmds = styled.ul`
|
|||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export default function Commands({
|
export default function Commands({ context = "user", contextId = 0, mid = 0, toggleEditMessage }) {
|
||||||
context = "user",
|
|
||||||
contextId = 0,
|
|
||||||
mid = 0,
|
|
||||||
toggleEditMessage,
|
|
||||||
}) {
|
|
||||||
const {
|
const {
|
||||||
canDelete,
|
canDelete,
|
||||||
canReply,
|
canReply,
|
||||||
@@ -80,11 +75,11 @@ export default function Commands({
|
|||||||
togglePinModal,
|
togglePinModal,
|
||||||
PinModal,
|
PinModal,
|
||||||
DeleteModal,
|
DeleteModal,
|
||||||
ForwardModal,
|
ForwardModal
|
||||||
} = useMessageOperation({ mid, context, contextId });
|
} = useMessageOperation({ mid, context, contextId });
|
||||||
const { setReplying } = useSendMessage({ context, to: contextId });
|
const { setReplying } = useSendMessage({ context, to: contextId });
|
||||||
const { addFavorite, isFavorited } = useFavMessage({
|
const { addFavorite, isFavorited } = useFavMessage({
|
||||||
cid: context == "channel" ? contextId : null,
|
cid: context == "channel" ? contextId : null
|
||||||
});
|
});
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [tippyVisible, setTippyVisible] = useState(false);
|
const [tippyVisible, setTippyVisible] = useState(false);
|
||||||
@@ -126,10 +121,7 @@ export default function Commands({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StyledCmds
|
<StyledCmds ref={cmdsRef} className={`cmds ${tippyVisible ? "visible" : ""}`}>
|
||||||
ref={cmdsRef}
|
|
||||||
className={`cmds ${tippyVisible ? "visible" : ""}`}
|
|
||||||
>
|
|
||||||
<Tippy
|
<Tippy
|
||||||
onShow={handleTippyVisible.bind(null, true)}
|
onShow={handleTippyVisible.bind(null, true)}
|
||||||
onHide={handleTippyVisible.bind(null, false)}
|
onHide={handleTippyVisible.bind(null, false)}
|
||||||
@@ -175,25 +167,25 @@ export default function Commands({
|
|||||||
canPin && {
|
canPin && {
|
||||||
title: pinned ? `Unpin Message` : `Pin Message`,
|
title: pinned ? `Unpin Message` : `Pin Message`,
|
||||||
icon: <IconPin className="icon" />,
|
icon: <IconPin className="icon" />,
|
||||||
handler: pinned ? handleUnpin : togglePinModal,
|
handler: pinned ? handleUnpin : togglePinModal
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Forward",
|
title: "Forward",
|
||||||
icon: <IconForward className="icon" />,
|
icon: <IconForward className="icon" />,
|
||||||
handler: toggleForwardModal,
|
handler: toggleForwardModal
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Select",
|
title: "Select",
|
||||||
icon: <IconSelect className="icon" />,
|
icon: <IconSelect className="icon" />,
|
||||||
handler: handleSelect.bind(null, mid),
|
handler: handleSelect.bind(null, mid)
|
||||||
},
|
},
|
||||||
|
|
||||||
canDelete && {
|
canDelete && {
|
||||||
title: " Delete",
|
title: " Delete",
|
||||||
danger: true,
|
danger: true,
|
||||||
icon: <IconDelete className="icon" />,
|
icon: <IconDelete className="icon" />,
|
||||||
handler: toggleDeleteModal,
|
handler: toggleDeleteModal
|
||||||
},
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default function MessageContextMenu({
|
|||||||
visible,
|
visible,
|
||||||
hide,
|
hide,
|
||||||
editMessage,
|
editMessage,
|
||||||
children,
|
children
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
copyContent,
|
copyContent,
|
||||||
@@ -37,7 +37,7 @@ export default function MessageContextMenu({
|
|||||||
togglePinModal,
|
togglePinModal,
|
||||||
PinModal,
|
PinModal,
|
||||||
ForwardModal,
|
ForwardModal,
|
||||||
DeleteModal,
|
DeleteModal
|
||||||
} = useMessageOperation({ mid, contextId, context });
|
} = useMessageOperation({ mid, contextId, context });
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { setReplying } = useSendMessage({ context, to: contextId });
|
const { setReplying } = useSendMessage({ context, to: contextId });
|
||||||
@@ -71,39 +71,39 @@ export default function MessageContextMenu({
|
|||||||
canEdit && {
|
canEdit && {
|
||||||
title: "Edit Message",
|
title: "Edit Message",
|
||||||
icon: <IconEdit className="icon" />,
|
icon: <IconEdit className="icon" />,
|
||||||
handler: editMessage,
|
handler: editMessage
|
||||||
},
|
},
|
||||||
canReply && {
|
canReply && {
|
||||||
title: "Reply",
|
title: "Reply",
|
||||||
icon: <IconReply className="icon" />,
|
icon: <IconReply className="icon" />,
|
||||||
handler: handleReply,
|
handler: handleReply
|
||||||
},
|
},
|
||||||
canCopy && {
|
canCopy && {
|
||||||
title: "Copy",
|
title: "Copy",
|
||||||
icon: <IconCopy className="icon" />,
|
icon: <IconCopy className="icon" />,
|
||||||
handler: copyContent,
|
handler: copyContent
|
||||||
},
|
},
|
||||||
canPin && {
|
canPin && {
|
||||||
title: pinned ? "Unpin" : "Pin",
|
title: pinned ? "Unpin" : "Pin",
|
||||||
icon: <IconPin className="icon" />,
|
icon: <IconPin className="icon" />,
|
||||||
handler: pinned ? unPin.bind(null, mid) : togglePinModal,
|
handler: pinned ? unPin.bind(null, mid) : togglePinModal
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Forward",
|
title: "Forward",
|
||||||
icon: <IconForward className="icon" />,
|
icon: <IconForward className="icon" />,
|
||||||
handler: toggleForwardModal,
|
handler: toggleForwardModal
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Select",
|
title: "Select",
|
||||||
icon: <IconSelect className="icon" />,
|
icon: <IconSelect className="icon" />,
|
||||||
handler: handleSelect,
|
handler: handleSelect
|
||||||
},
|
},
|
||||||
canDelete && {
|
canDelete && {
|
||||||
title: "Delete",
|
title: "Delete",
|
||||||
danger: true,
|
danger: true,
|
||||||
icon: <IconDelete className="icon" />,
|
icon: <IconDelete className="icon" />,
|
||||||
handler: toggleDeleteModal,
|
handler: toggleDeleteModal
|
||||||
},
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export default function EditMessage({ mid, cancelEdit }) {
|
|||||||
edit({
|
edit({
|
||||||
mid,
|
mid,
|
||||||
content: currMsg,
|
content: currMsg,
|
||||||
type: msg.content_type == ContentTypes.markdown ? "markdown" : "text",
|
type: msg.content_type == ContentTypes.markdown ? "markdown" : "text"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
if (!msg) return null;
|
if (!msg) return null;
|
||||||
@@ -121,8 +121,7 @@ export default function EditMessage({ mid, cancelEdit }) {
|
|||||||
esc to <button onClick={cancelEdit}>cancel</button>
|
esc to <button onClick={cancelEdit}>cancel</button>
|
||||||
</span>
|
</span>
|
||||||
<span className="opt">
|
<span className="opt">
|
||||||
enter to{" "}
|
enter to <button onClick={handleSave}>{isEditing ? "saving" : `save`}</button>
|
||||||
<button onClick={handleSave}>{isEditing ? "saving" : `save`}</button>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</StyledWrapper>
|
</StyledWrapper>
|
||||||
|
|||||||
@@ -23,20 +23,10 @@ const FavoritedMessage = ({ id }) => {
|
|||||||
const favorite_mids = messages.map(({ from_mid }) => from_mid) || [];
|
const favorite_mids = messages.map(({ from_mid }) => from_mid) || [];
|
||||||
|
|
||||||
setMsgs(
|
setMsgs(
|
||||||
<StyledFav
|
<StyledFav data-favorite-mids={favorite_mids.join(",")} className="favorite">
|
||||||
data-favorite-mids={favorite_mids.join(",")}
|
|
||||||
className="favorite"
|
|
||||||
>
|
|
||||||
<div className="list">
|
<div className="list">
|
||||||
{messages.map((msg, idx) => {
|
{messages.map((msg, idx) => {
|
||||||
const {
|
const { user = {}, download, content, content_type, properties, thumbnail } = msg;
|
||||||
user = {},
|
|
||||||
download,
|
|
||||||
content,
|
|
||||||
content_type,
|
|
||||||
properties,
|
|
||||||
thumbnail,
|
|
||||||
} = msg;
|
|
||||||
return (
|
return (
|
||||||
<StyledMsg className="archive" key={idx}>
|
<StyledMsg className="archive" key={idx}>
|
||||||
{user && (
|
{user && (
|
||||||
@@ -54,7 +44,7 @@ const FavoritedMessage = ({ id }) => {
|
|||||||
content,
|
content,
|
||||||
content_type,
|
content_type,
|
||||||
properties,
|
properties,
|
||||||
thumbnail,
|
thumbnail
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ const ForwardedMessage = ({ context, to, from_uid, id }) => {
|
|||||||
content,
|
content,
|
||||||
content_type,
|
content_type,
|
||||||
properties,
|
properties,
|
||||||
thumbnail,
|
thumbnail
|
||||||
} = msg;
|
} = msg;
|
||||||
return (
|
return (
|
||||||
<StyledMsg className="archive" key={idx}>
|
<StyledMsg className="archive" key={idx}>
|
||||||
@@ -78,7 +78,7 @@ const ForwardedMessage = ({ context, to, from_uid, id }) => {
|
|||||||
content,
|
content,
|
||||||
content_type,
|
content_type,
|
||||||
properties,
|
properties,
|
||||||
thumbnail,
|
thumbnail
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,14 +9,7 @@ export default function PreviewMessage({ mid = 0 }) {
|
|||||||
return { msg: store.message[mid], contactsData: store.contacts.byId };
|
return { msg: store.message[mid], contactsData: store.contacts.byId };
|
||||||
});
|
});
|
||||||
if (!msg) return null;
|
if (!msg) return null;
|
||||||
const {
|
const { from_uid, created_at, content_type, content, thumbnail, properties } = msg;
|
||||||
from_uid,
|
|
||||||
created_at,
|
|
||||||
content_type,
|
|
||||||
content,
|
|
||||||
thumbnail,
|
|
||||||
properties,
|
|
||||||
} = msg;
|
|
||||||
const { name, avatar } = contactsData[from_uid];
|
const { name, avatar } = contactsData[from_uid];
|
||||||
return (
|
return (
|
||||||
<StyledWrapper className={`preview`}>
|
<StyledWrapper className={`preview`}>
|
||||||
@@ -26,9 +19,7 @@ export default function PreviewMessage({ mid = 0 }) {
|
|||||||
<div className="details">
|
<div className="details">
|
||||||
<div className="up">
|
<div className="up">
|
||||||
<span className="name">{name}</span>
|
<span className="name">{name}</span>
|
||||||
<i className="time">
|
<i className="time">{dayjs(created_at).format("YYYY-MM-DD h:mm:ss A")}</i>
|
||||||
{dayjs(created_at).format("YYYY-MM-DD h:mm:ss A")}
|
|
||||||
</i>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={`down`}>
|
<div className={`down`}>
|
||||||
{renderContent({
|
{renderContent({
|
||||||
@@ -36,7 +27,7 @@ export default function PreviewMessage({ mid = 0 }) {
|
|||||||
content,
|
content,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
from_uid,
|
from_uid,
|
||||||
properties,
|
properties
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -70,8 +70,7 @@ const StyledDetails = styled.div`
|
|||||||
position: relative;
|
position: relative;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border-radius: var(--br);
|
border-radius: var(--br);
|
||||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08),
|
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||||
0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@@ -132,7 +131,7 @@ export default function Reaction({ mid, reactions = null }) {
|
|||||||
const [reactWithEmoji] = useReactMessageMutation();
|
const [reactWithEmoji] = useReactMessageMutation();
|
||||||
const { currUid } = useSelector((store) => {
|
const { currUid } = useSelector((store) => {
|
||||||
return {
|
return {
|
||||||
currUid: store.authData.uid,
|
currUid: store.authData.uid
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const handleReact = (emoji) => {
|
const handleReact = (emoji) => {
|
||||||
@@ -156,18 +155,14 @@ export default function Reaction({ mid, reactions = null }) {
|
|||||||
// visible={true}
|
// visible={true}
|
||||||
interactive
|
interactive
|
||||||
placement="top"
|
placement="top"
|
||||||
content={
|
content={<ReactionDetails uids={uids} emoji={reaction} index={idx} />}
|
||||||
<ReactionDetails uids={uids} emoji={reaction} index={idx} />
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<i className="emoji">
|
<i className="emoji">
|
||||||
<ReactionItem native={reaction} />
|
<ReactionItem native={reaction} />
|
||||||
</i>
|
</i>
|
||||||
</Tippy>
|
</Tippy>
|
||||||
|
|
||||||
{uids.length > 1 ? (
|
{uids.length > 1 ? <em className="count">{`${uids.length}`} </em> : null}
|
||||||
<em className="count">{`${uids.length}`} </em>
|
|
||||||
) : null}
|
|
||||||
</span>
|
</span>
|
||||||
) : null;
|
) : null;
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default function ReactionPicker({ mid, hidePicker }) {
|
|||||||
const { reactionData, currUid } = useSelector((store) => {
|
const { reactionData, currUid } = useSelector((store) => {
|
||||||
return {
|
return {
|
||||||
reactionData: store.reactionMessage[mid] || {},
|
reactionData: store.reactionMessage[mid] || {},
|
||||||
currUid: store.authData.uid,
|
currUid: store.authData.uid
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
// useOutsideClick(wrapperRef, hidePicker);
|
// useOutsideClick(wrapperRef, hidePicker);
|
||||||
@@ -55,8 +55,7 @@ export default function ReactionPicker({ mid, hidePicker }) {
|
|||||||
<ul className={`emojis ${isLoading ? "reacting" : ""}`}>
|
<ul className={`emojis ${isLoading ? "reacting" : ""}`}>
|
||||||
{Emojis.map((emoji) => {
|
{Emojis.map((emoji) => {
|
||||||
let reacted =
|
let reacted =
|
||||||
reactionData[emoji] &&
|
reactionData[emoji] && reactionData[emoji].findIndex((id) => id == currUid) > -1;
|
||||||
reactionData[emoji].findIndex((id) => id == currUid) > -1;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
|
|||||||
@@ -66,11 +66,7 @@ const Styled = styled.div`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
content: "";
|
content: "";
|
||||||
background: linear-gradient(
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0) 63.54%, #e5e7eb 93.09%);
|
||||||
180deg,
|
|
||||||
rgba(255, 255, 255, 0) 63.54%,
|
|
||||||
#e5e7eb 93.09%
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.pic {
|
.pic {
|
||||||
|
|||||||
@@ -26,26 +26,24 @@ function Message({
|
|||||||
mid = "",
|
mid = "",
|
||||||
context = "user",
|
context = "user",
|
||||||
updateReadIndex,
|
updateReadIndex,
|
||||||
read = true,
|
read = true
|
||||||
}) {
|
}) {
|
||||||
const {
|
const { visible: contextMenuVisible, handleContextMenuEvent, hideContextMenu } = useContextMenu();
|
||||||
visible: contextMenuVisible,
|
|
||||||
handleContextMenuEvent,
|
|
||||||
hideContextMenu,
|
|
||||||
} = useContextMenu();
|
|
||||||
const inviewRef = useInView();
|
const inviewRef = useInView();
|
||||||
const [edit, setEdit] = useState(false);
|
const [edit, setEdit] = useState(false);
|
||||||
const avatarRef = useRef(null);
|
const avatarRef = useRef(null);
|
||||||
const { getPinInfo } = usePinMessage(context == "channel" ? contextId : null);
|
const { getPinInfo } = usePinMessage(context == "channel" ? contextId : null);
|
||||||
const { message = {}, reactionMessageData, contactsData } = useSelector(
|
const {
|
||||||
(store) => {
|
message = {},
|
||||||
return {
|
reactionMessageData,
|
||||||
reactionMessageData: store.reactionMessage,
|
contactsData
|
||||||
message: store.message[mid] || {},
|
} = useSelector((store) => {
|
||||||
contactsData: store.contacts.byId,
|
return {
|
||||||
};
|
reactionMessageData: store.reactionMessage,
|
||||||
}
|
message: store.message[mid] || {},
|
||||||
);
|
contactsData: store.contacts.byId
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const toggleEditMessage = () => {
|
const toggleEditMessage = () => {
|
||||||
setEdit((prev) => !prev);
|
setEdit((prev) => !prev);
|
||||||
@@ -61,7 +59,7 @@ function Message({
|
|||||||
download,
|
download,
|
||||||
content_type = "text/plain",
|
content_type = "text/plain",
|
||||||
edited,
|
edited,
|
||||||
properties,
|
properties
|
||||||
} = message;
|
} = message;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -79,11 +77,7 @@ function Message({
|
|||||||
// if (!message) return null;
|
// if (!message) return null;
|
||||||
let timePrefix = null;
|
let timePrefix = null;
|
||||||
const dayjsTime = dayjs(time);
|
const dayjsTime = dayjs(time);
|
||||||
timePrefix = dayjsTime.isToday()
|
timePrefix = dayjsTime.isToday() ? "Today" : dayjsTime.isYesterday() ? "Yesterday" : null;
|
||||||
? "Today"
|
|
||||||
: dayjsTime.isYesterday()
|
|
||||||
? "Yesterday"
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const pinInfo = getPinInfo(mid);
|
const pinInfo = getPinInfo(mid);
|
||||||
// return null;
|
// return null;
|
||||||
@@ -94,9 +88,9 @@ function Message({
|
|||||||
onContextMenu={handleContextMenuEvent}
|
onContextMenu={handleContextMenuEvent}
|
||||||
data-msg-mid={mid}
|
data-msg-mid={mid}
|
||||||
ref={inviewRef}
|
ref={inviewRef}
|
||||||
className={`message ${readOnly ? "readonly" : ""} ${
|
className={`message ${readOnly ? "readonly" : ""} ${pinInfo ? "pinned" : ""} ${
|
||||||
pinInfo ? "pinned" : ""
|
contextMenuVisible ? "contextVisible" : ""
|
||||||
} ${contextMenuVisible ? "contextVisible" : ""} `}
|
} `}
|
||||||
>
|
>
|
||||||
<Tippy
|
<Tippy
|
||||||
key={_key}
|
key={_key}
|
||||||
@@ -153,7 +147,7 @@ function Message({
|
|||||||
content,
|
content,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
download,
|
download,
|
||||||
edited,
|
edited
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
{reactions && <Reaction mid={mid} reactions={reactions} />}
|
{reactions && <Reaction mid={mid} reactions={reactions} />}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const renderContent = ({
|
|||||||
content,
|
content,
|
||||||
download,
|
download,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
edited = false,
|
edited = false
|
||||||
}) => {
|
}) => {
|
||||||
let ctn = null;
|
let ctn = null;
|
||||||
switch (content_type) {
|
switch (content_type) {
|
||||||
@@ -30,38 +30,23 @@ const renderContent = ({
|
|||||||
<Linkit
|
<Linkit
|
||||||
componentDecorator={(decoratedHref, decoratedText, key) => (
|
componentDecorator={(decoratedHref, decoratedText, key) => (
|
||||||
<React.Fragment key={key}>
|
<React.Fragment key={key}>
|
||||||
<a
|
<a className="link" target="_blank" href={decoratedHref} key={key} rel="noreferrer">
|
||||||
className="link"
|
|
||||||
target="_blank"
|
|
||||||
href={decoratedHref}
|
|
||||||
key={key}
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
{decoratedText}
|
{decoratedText}
|
||||||
</a>
|
</a>
|
||||||
{!decoratedHref.startsWith("mailto") && (
|
{!decoratedHref.startsWith("mailto") && <URLPreview url={decoratedHref} />}
|
||||||
<URLPreview url={decoratedHref} />
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{reactStringReplace(
|
{reactStringReplace(content, /(\s{1}@[0-9]+\s{1})/g, (match, idx) => {
|
||||||
content,
|
console.log("match", match);
|
||||||
/(\s{1}@[0-9]+\s{1})/g,
|
const uid = match.trim().slice(1);
|
||||||
(match, idx) => {
|
return <Mention key={idx} uid={uid} cid={to} />;
|
||||||
console.log("match", match);
|
})}
|
||||||
const uid = match.trim().slice(1);
|
|
||||||
return <Mention key={idx} uid={uid} cid={to} />;
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
{/* {content.replace(/\s{1}\@[1-9]+\s{1}/g,)} */}
|
{/* {content.replace(/\s{1}\@[1-9]+\s{1}/g,)} */}
|
||||||
{/* {new RegExp(/\s{1}\@[1-9]+\s{1}/g).exec(content)} */}
|
{/* {new RegExp(/\s{1}\@[1-9]+\s{1}/g).exec(content)} */}
|
||||||
</Linkit>
|
</Linkit>
|
||||||
{edited && (
|
{edited && (
|
||||||
<span
|
<span className="edited" title={dayjs(edited).format("YYYY-MM-DD h:mm:ss A")}>
|
||||||
className="edited"
|
|
||||||
title={dayjs(edited).format("YYYY-MM-DD h:mm:ss A")}
|
|
||||||
>
|
|
||||||
(edited)
|
(edited)
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -10,17 +10,15 @@ import usePinMessage from "../../hook/usePinMessage";
|
|||||||
|
|
||||||
export default function useMessageOperation({ mid, context, contextId }) {
|
export default function useMessageOperation({ mid, context, contextId }) {
|
||||||
const { copy } = useCopy();
|
const { copy } = useCopy();
|
||||||
const { content_type, properties, currUid, from_uid, content } = useSelector(
|
const { content_type, properties, currUid, from_uid, content } = useSelector((store) => {
|
||||||
(store) => {
|
return {
|
||||||
return {
|
content: store.message[mid]?.content,
|
||||||
content: store.message[mid]?.content,
|
from_uid: store.message[mid]?.from_uid,
|
||||||
from_uid: store.message[mid]?.from_uid,
|
content_type: store.message[mid]?.content_type,
|
||||||
content_type: store.message[mid]?.content_type,
|
properties: store.message[mid]?.properties,
|
||||||
properties: store.message[mid]?.properties,
|
currUid: store.authData.uid
|
||||||
currUid: store.authData.uid,
|
};
|
||||||
};
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
const { canPin, pins, unpinMessage, isUnpinSuccess } = usePinMessage(
|
const { canPin, pins, unpinMessage, isUnpinSuccess } = usePinMessage(
|
||||||
context == "channel" ? contextId : undefined
|
context == "channel" ? contextId : undefined
|
||||||
);
|
);
|
||||||
@@ -71,16 +69,11 @@ export default function useMessageOperation({ mid, context, contextId }) {
|
|||||||
properties?.content_type &&
|
properties?.content_type &&
|
||||||
properties?.content_type.startsWith("image");
|
properties?.content_type.startsWith("image");
|
||||||
const enableEdit =
|
const enableEdit =
|
||||||
currUid == from_uid &&
|
currUid == from_uid && [ContentTypes.text, ContentTypes.markdown].includes(content_type);
|
||||||
[ContentTypes.text, ContentTypes.markdown].includes(content_type);
|
|
||||||
const canDelete = currUid == from_uid;
|
const canDelete = currUid == from_uid;
|
||||||
const canCopy =
|
const canCopy = [ContentTypes.text, ContentTypes.markdown].includes(content_type) || isImage;
|
||||||
[ContentTypes.text, ContentTypes.markdown].includes(content_type) ||
|
|
||||||
isImage;
|
|
||||||
return {
|
return {
|
||||||
copyContent: isImage
|
copyContent: isImage ? copyContent.bind(null, true) : copyContent.bind(null, false),
|
||||||
? copyContent.bind(null, true)
|
|
||||||
: copyContent.bind(null, false),
|
|
||||||
canCopy,
|
canCopy,
|
||||||
isImage,
|
isImage,
|
||||||
isMarkdown: content_type == ContentTypes.markdown,
|
isMarkdown: content_type == ContentTypes.markdown,
|
||||||
@@ -101,6 +94,6 @@ export default function useMessageOperation({ mid, context, contextId }) {
|
|||||||
) : null,
|
) : null,
|
||||||
PinModal: pinModalVisible ? (
|
PinModal: pinModalVisible ? (
|
||||||
<PinMessageModal mid={mid} gid={contextId} closeModal={togglePinModal} />
|
<PinMessageModal mid={mid} gid={contextId} closeModal={togglePinModal} />
|
||||||
) : null,
|
) : null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export const CONFIG = {
|
|||||||
editableProps: {
|
editableProps: {
|
||||||
spellCheck: false,
|
spellCheck: false,
|
||||||
autoFocus: true,
|
autoFocus: true,
|
||||||
placeholder: "Type…",
|
placeholder: "Type…"
|
||||||
},
|
},
|
||||||
trailingBlock: { type: ELEMENT_PARAGRAPH },
|
trailingBlock: { type: ELEMENT_PARAGRAPH },
|
||||||
softBreak: {
|
softBreak: {
|
||||||
@@ -13,11 +13,11 @@ export const CONFIG = {
|
|||||||
{
|
{
|
||||||
hotkey: "shift+enter",
|
hotkey: "shift+enter",
|
||||||
query: {
|
query: {
|
||||||
allow: [ELEMENT_IMAGE, ELEMENT_PARAGRAPH],
|
allow: [ELEMENT_IMAGE, ELEMENT_PARAGRAPH]
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
exitBreak: {
|
exitBreak: {
|
||||||
options: {
|
options: {
|
||||||
@@ -25,9 +25,9 @@ export const CONFIG = {
|
|||||||
{
|
{
|
||||||
hotkey: "mod+enter",
|
hotkey: "mod+enter",
|
||||||
query: {
|
query: {
|
||||||
allow: [ELEMENT_IMAGE, ELEMENT_PARAGRAPH],
|
allow: [ELEMENT_IMAGE, ELEMENT_PARAGRAPH]
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
// {
|
// {
|
||||||
// hotkey: "mod+shift+enter",
|
// hotkey: "mod+shift+enter",
|
||||||
// before: true,
|
// before: true,
|
||||||
@@ -40,14 +40,14 @@ export const CONFIG = {
|
|||||||
// allow: KEYS_HEADING,
|
// allow: KEYS_HEADING,
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
],
|
]
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
selectOnBackspace: {
|
selectOnBackspace: {
|
||||||
options: {
|
options: {
|
||||||
query: {
|
query: {
|
||||||
allow: [ELEMENT_IMAGE],
|
allow: [ELEMENT_IMAGE]
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
// usePlateEditorRef,
|
// usePlateEditorRef,
|
||||||
// ELEMENT_IMAGE,
|
// ELEMENT_IMAGE,
|
||||||
// useComboboxControls,
|
// useComboboxControls,
|
||||||
MentionCombobox,
|
MentionCombobox
|
||||||
} from "@udecode/plate";
|
} from "@udecode/plate";
|
||||||
import { createComboboxPlugin } from "@udecode/plate-combobox";
|
import { createComboboxPlugin } from "@udecode/plate-combobox";
|
||||||
import { ReactEditor } from "slate-react";
|
import { ReactEditor } from "slate-react";
|
||||||
@@ -44,7 +44,7 @@ const Plugins = ({
|
|||||||
id = "",
|
id = "",
|
||||||
placeholder = "Write some markdown...",
|
placeholder = "Write some markdown...",
|
||||||
sendMessages,
|
sendMessages,
|
||||||
members = [],
|
members = []
|
||||||
}) => {
|
}) => {
|
||||||
// const { getMenuProps, getItemProps } = useComboboxControls();
|
// const { getMenuProps, getItemProps } = useComboboxControls();
|
||||||
const [context, to] = id.split("_");
|
const [context, to] = id.split("_");
|
||||||
@@ -57,7 +57,7 @@ const Plugins = ({
|
|||||||
const initialProps = {
|
const initialProps = {
|
||||||
...CONFIG.editableProps,
|
...CONFIG.editableProps,
|
||||||
className: "box",
|
className: "box",
|
||||||
placeholder,
|
placeholder
|
||||||
};
|
};
|
||||||
const plateEditor = getPlateEditorRef(`${TEXT_EDITOR_PREFIX}_${id}`);
|
const plateEditor = getPlateEditorRef(`${TEXT_EDITOR_PREFIX}_${id}`);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -91,13 +91,7 @@ const Plugins = ({
|
|||||||
(evt) => {
|
(evt) => {
|
||||||
// 是否在at操作
|
// 是否在at操作
|
||||||
const mentionInputs = findMentionInput(plateEditor);
|
const mentionInputs = findMentionInput(plateEditor);
|
||||||
if (
|
if (mentionInputs || evt.shiftKey || evt.ctrlKey || evt.altKey || evt.isComposing) {
|
||||||
mentionInputs ||
|
|
||||||
evt.shiftKey ||
|
|
||||||
evt.ctrlKey ||
|
|
||||||
evt.altKey ||
|
|
||||||
evt.isComposing
|
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
@@ -107,14 +101,14 @@ const Plugins = ({
|
|||||||
Transforms.delete(plateEditor, {
|
Transforms.delete(plateEditor, {
|
||||||
at: {
|
at: {
|
||||||
anchor: Editor.start(plateEditor, []),
|
anchor: Editor.start(plateEditor, []),
|
||||||
focus: Editor.end(plateEditor, []),
|
focus: Editor.end(plateEditor, [])
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// eventTypes: ["keydown"],
|
// eventTypes: ["keydown"],
|
||||||
target: editableRef,
|
target: editableRef,
|
||||||
when: !cmdKey,
|
when: !cmdKey
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
useKey(
|
useKey(
|
||||||
@@ -125,7 +119,7 @@ const Plugins = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventTypes: ["keydown", "keyup"],
|
eventTypes: ["keydown", "keyup"],
|
||||||
target: editableRef,
|
target: editableRef
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const pluginArr = [
|
const pluginArr = [
|
||||||
@@ -133,7 +127,7 @@ const Plugins = ({
|
|||||||
createNodeIdPlugin(),
|
createNodeIdPlugin(),
|
||||||
createSoftBreakPlugin(CONFIG.softBreak),
|
createSoftBreakPlugin(CONFIG.softBreak),
|
||||||
createTrailingBlockPlugin(CONFIG.trailingBlock),
|
createTrailingBlockPlugin(CONFIG.trailingBlock),
|
||||||
createExitBreakPlugin(CONFIG.exitBreak),
|
createExitBreakPlugin(CONFIG.exitBreak)
|
||||||
];
|
];
|
||||||
const plugins = createPlugins(
|
const plugins = createPlugins(
|
||||||
enableMentions
|
enableMentions
|
||||||
@@ -145,17 +139,17 @@ const Plugins = ({
|
|||||||
console.log("mention", item);
|
console.log("mention", item);
|
||||||
const {
|
const {
|
||||||
text,
|
text,
|
||||||
data: { uid },
|
data: { uid }
|
||||||
} = item;
|
} = item;
|
||||||
return { value: `@${text}`, uid };
|
return { value: `@${text}`, uid };
|
||||||
},
|
},
|
||||||
insertSpaceAfterMention: true,
|
insertSpaceAfterMention: true
|
||||||
},
|
}
|
||||||
}),
|
})
|
||||||
])
|
])
|
||||||
: pluginArr,
|
: pluginArr,
|
||||||
{
|
{
|
||||||
components,
|
components
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -179,20 +173,16 @@ const Plugins = ({
|
|||||||
const { value, mentions } = getMixedText(v.children);
|
const { value, mentions } = getMixedText(v.children);
|
||||||
const prev = tmps[tmps.length - 1];
|
const prev = tmps[tmps.length - 1];
|
||||||
if (!prev) {
|
if (!prev) {
|
||||||
tmps.push([
|
tmps.push([{ type: "text", content: value, properties: { mentions } }]);
|
||||||
{ type: "text", content: value, properties: { mentions } },
|
|
||||||
]);
|
|
||||||
} else {
|
} else {
|
||||||
if (Array.isArray(prev)) {
|
if (Array.isArray(prev)) {
|
||||||
tmps[tmps.length - 1].push({
|
tmps[tmps.length - 1].push({
|
||||||
type: "text",
|
type: "text",
|
||||||
content: value,
|
content: value,
|
||||||
properties: { mentions },
|
properties: { mentions }
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
tmps.push([
|
tmps.push([{ type: "text", content: value, properties: { mentions } }]);
|
||||||
{ type: "text", content: value, properties: { mentions } },
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,8 +192,8 @@ const Plugins = ({
|
|||||||
type: "text",
|
type: "text",
|
||||||
content: tmp.map((t) => t.content).join("\n"),
|
content: tmp.map((t) => t.content).join("\n"),
|
||||||
properties: {
|
properties: {
|
||||||
mentions: tmp.map((t) => t.properties?.mentions || []).flat(),
|
mentions: tmp.map((t) => t.properties?.mentions || []).flat()
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
: tmp;
|
: tmp;
|
||||||
});
|
});
|
||||||
@@ -229,13 +219,7 @@ const Plugins = ({
|
|||||||
// component={StyledCombobox}
|
// component={StyledCombobox}
|
||||||
onRenderItem={({ item }) => {
|
onRenderItem={({ item }) => {
|
||||||
console.log("wtf", item);
|
console.log("wtf", item);
|
||||||
return (
|
return <Contact key={item.data.uid} uid={item.data.uid} interactive={false} />;
|
||||||
<Contact
|
|
||||||
key={item.data.uid}
|
|
||||||
uid={item.data.uid}
|
|
||||||
interactive={false}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
items={members.map((id) => {
|
items={members.map((id) => {
|
||||||
const data = contactData[id];
|
const data = contactData[id];
|
||||||
@@ -246,8 +230,8 @@ const Plugins = ({
|
|||||||
text: name,
|
text: name,
|
||||||
data: {
|
data: {
|
||||||
uid,
|
uid,
|
||||||
...rest,
|
...rest
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
@@ -273,7 +257,7 @@ export const useMixedEditor = (key) => {
|
|||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
focus,
|
focus,
|
||||||
insertText,
|
insertText
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export default Plugins;
|
export default Plugins;
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import {
|
|||||||
createBasicElementsPlugin,
|
createBasicElementsPlugin,
|
||||||
createImagePlugin,
|
createImagePlugin,
|
||||||
createParagraphPlugin,
|
createParagraphPlugin,
|
||||||
createSelectOnBackspacePlugin,
|
createSelectOnBackspacePlugin
|
||||||
} from "@udecode/plate";
|
} from "@udecode/plate";
|
||||||
import { CONFIG } from "./config";
|
import { CONFIG } from "./config";
|
||||||
|
|
||||||
const basicElements = [
|
const basicElements = [
|
||||||
createParagraphPlugin(), // paragraph element
|
createParagraphPlugin() // paragraph element
|
||||||
];
|
];
|
||||||
|
|
||||||
export const PLUGINS = {
|
export const PLUGINS = {
|
||||||
@@ -16,6 +16,6 @@ export const PLUGINS = {
|
|||||||
image: [
|
image: [
|
||||||
createBasicElementsPlugin(),
|
createBasicElementsPlugin(),
|
||||||
createImagePlugin(),
|
createImagePlugin(),
|
||||||
createSelectOnBackspacePlugin(CONFIG.selectOnBackspace),
|
createSelectOnBackspacePlugin(CONFIG.selectOnBackspace)
|
||||||
],
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
paragraph: "p",
|
paragraph: "p",
|
||||||
image: "img",
|
image: "img"
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nodeTypes;
|
export default nodeTypes;
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
ELEMENT_PARAGRAPH,
|
ELEMENT_PARAGRAPH
|
||||||
// TElement,
|
// TElement,
|
||||||
} from "@udecode/plate";
|
} from "@udecode/plate";
|
||||||
// import { Text } from 'slate'
|
// import { Text } from 'slate'
|
||||||
|
|
||||||
export const createElement = (
|
export const createElement = (text = "", { type = ELEMENT_PARAGRAPH, mark } = {}) => {
|
||||||
text = "",
|
|
||||||
{ type = ELEMENT_PARAGRAPH, mark } = {}
|
|
||||||
) => {
|
|
||||||
const leaf = { text };
|
const leaf = { text };
|
||||||
if (mark) {
|
if (mark) {
|
||||||
leaf[mark] = true;
|
leaf[mark] = true;
|
||||||
@@ -15,7 +12,7 @@ export const createElement = (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
type,
|
type,
|
||||||
children: [leaf],
|
children: [leaf]
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ const Styled = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export default function MrakdownRender({ content }) {
|
export default function MrakdownRender({ content }) {
|
||||||
const mdContainer = useRef(null);
|
const mdContainer = useRef(undefined);
|
||||||
const [previewImage, setPreviewImage] = useState(null);
|
const [previewImage, setPreviewImage] = useState(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (mdContainer) {
|
const container = mdContainer?.current;
|
||||||
const container = mdContainer.current;
|
if (container) {
|
||||||
// 点击查看大图
|
// 点击查看大图
|
||||||
container.addEventListener(
|
container.addEventListener(
|
||||||
"click",
|
"click",
|
||||||
@@ -56,11 +56,7 @@ export default function MrakdownRender({ content }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{previewImage && (
|
{previewImage && (
|
||||||
<ImagePreviewModal
|
<ImagePreviewModal download={false} data={previewImage} closeModal={closePreviewModal} />
|
||||||
download={false}
|
|
||||||
data={previewImage}
|
|
||||||
closeModal={closePreviewModal}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<Styled ref={mdContainer}>
|
<Styled ref={mdContainer}>
|
||||||
<Viewer
|
<Viewer
|
||||||
|
|||||||
@@ -21,15 +21,9 @@ const Notification = () => {
|
|||||||
navigateTo(newPath);
|
navigateTo(newPath);
|
||||||
};
|
};
|
||||||
// https only
|
// https only
|
||||||
navigator.serviceWorker?.addEventListener(
|
navigator.serviceWorker?.addEventListener("message", handleServiceworkerMessage);
|
||||||
"message",
|
|
||||||
handleServiceworkerMessage
|
|
||||||
);
|
|
||||||
return () => {
|
return () => {
|
||||||
navigator.serviceWorker?.removeEventListener(
|
navigator.serviceWorker?.removeEventListener("message", handleServiceworkerMessage);
|
||||||
"message",
|
|
||||||
handleServiceworkerMessage
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const useDeviceToken = (vapidKey) => {
|
|||||||
const messaging = getMessaging(initializeApp(firebaseConfig));
|
const messaging = getMessaging(initializeApp(firebaseConfig));
|
||||||
|
|
||||||
getToken(messaging, {
|
getToken(messaging, {
|
||||||
vapidKey,
|
vapidKey
|
||||||
})
|
})
|
||||||
.then((currentToken) => {
|
.then((currentToken) => {
|
||||||
if (currentToken) {
|
if (currentToken) {
|
||||||
@@ -19,9 +19,7 @@ const useDeviceToken = (vapidKey) => {
|
|||||||
// Perform any other neccessary action with the token
|
// Perform any other neccessary action with the token
|
||||||
} else {
|
} else {
|
||||||
// Show permission request UI
|
// Show permission request UI
|
||||||
console.log(
|
console.log("No registration token available. Request permission to generate one.");
|
||||||
"No registration token available. Request permission to generate one."
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ export default function Profile({ uid = null, type = "embed", cid = null }) {
|
|||||||
removeFromChannel,
|
removeFromChannel,
|
||||||
canRemoveFromChannel,
|
canRemoveFromChannel,
|
||||||
canRemove,
|
canRemove,
|
||||||
removeUser,
|
removeUser
|
||||||
} = useContactOperation({ uid, cid });
|
} = useContactOperation({ uid, cid });
|
||||||
|
|
||||||
const { data } = useSelector((store) => {
|
const { data } = useSelector((store) => {
|
||||||
return {
|
return {
|
||||||
data: store.contacts.byId[uid],
|
data: store.contacts.byId[uid]
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,13 +35,12 @@ export default function Profile({ uid = null, type = "embed", cid = null }) {
|
|||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
email,
|
email,
|
||||||
avatar,
|
avatar
|
||||||
// introduction = "This guy has nothing to introduce",
|
// introduction = "This guy has nothing to introduce",
|
||||||
} = data;
|
} = data;
|
||||||
const enableCall = type == "card" && canCall;
|
const enableCall = type == "card" && canCall;
|
||||||
const canRemoveFromServer = type == "embed" && canRemove;
|
const canRemoveFromServer = type == "embed" && canRemove;
|
||||||
const hasMore =
|
const hasMore = enableCall || email || canRemoveFromChannel || canRemoveFromServer;
|
||||||
enableCall || email || canRemoveFromChannel || canRemoveFromServer;
|
|
||||||
return (
|
return (
|
||||||
<StyledWrapper className={type}>
|
<StyledWrapper className={type}>
|
||||||
<Avatar className="avatar" url={avatar} name={name} />
|
<Avatar className="avatar" url={avatar} name={name} />
|
||||||
|
|||||||
@@ -86,8 +86,7 @@ const StyledWrapper = styled.div`
|
|||||||
padding: 16px;
|
padding: 16px;
|
||||||
width: 280px;
|
width: 280px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
box-shadow: 0px 20px 25px 20px rgba(31, 41, 55, 0.1),
|
box-shadow: 0px 20px 25px 20px rgba(31, 41, 55, 0.1), 0px 10px 10px rgba(31, 41, 55, 0.04);
|
||||||
0px 10px 10px rgba(31, 41, 55, 0.04);
|
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
.icons {
|
.icons {
|
||||||
padding-bottom: 2px;
|
padding-bottom: 2px;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const Emojis = {
|
|||||||
"🚀": <EmojiRocket className="emoji" />,
|
"🚀": <EmojiRocket className="emoji" />,
|
||||||
"❤️": <EmojiHeart className="emoji" />,
|
"❤️": <EmojiHeart className="emoji" />,
|
||||||
"🙁": <EmojiUnhappy className="emoji" />,
|
"🙁": <EmojiUnhappy className="emoji" />,
|
||||||
"🎉": <EmojiCelebrate className="emoji" />,
|
"🎉": <EmojiCelebrate className="emoji" />
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ReactionItem({ native = "" }) {
|
export default function ReactionItem({ native = "" }) {
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ const StyledWrapper = styled.div`
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
/* gap: 20px; */
|
/* gap: 20px; */
|
||||||
border: 1px solid #e5e7eb;
|
border: 1px solid #e5e7eb;
|
||||||
box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1),
|
box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
||||||
0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
.txt {
|
.txt {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|||||||
@@ -44,12 +44,7 @@ export default function Search() {
|
|||||||
<input placeholder="Search..." className="input" />
|
<input placeholder="Search..." className="input" />
|
||||||
</div>
|
</div>
|
||||||
<Tooltip tip="More" placement="bottom">
|
<Tooltip tip="More" placement="bottom">
|
||||||
<Tippy
|
<Tippy interactive placement="bottom-end" trigger="click" content={<AddEntriesMenu />}>
|
||||||
interactive
|
|
||||||
placement="bottom-end"
|
|
||||||
trigger="click"
|
|
||||||
content={<AddEntriesMenu />}
|
|
||||||
>
|
|
||||||
<img src={addIcon} alt="add icon" className="add" />
|
<img src={addIcon} alt="add icon" className="add" />
|
||||||
</Tippy>
|
</Tippy>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ export default function EmojiPicker({ selectEmoji }) {
|
|||||||
const clickEle = evt.target;
|
const clickEle = evt.target;
|
||||||
const ignore =
|
const ignore =
|
||||||
(clickEle.nodeName == "svg" && clickEle.dataset.emoji == "toggler") ||
|
(clickEle.nodeName == "svg" && clickEle.dataset.emoji == "toggler") ||
|
||||||
(clickEle.nodeName == "path" &&
|
(clickEle.nodeName == "path" && clickEle.parentElement.dataset.emoji == "toggler");
|
||||||
clickEle.parentElement.dataset.emoji == "toggler");
|
|
||||||
// console.log("outside click", clickEle, clickEle.parentElement, ignore);
|
// console.log("outside click", clickEle, clickEle.parentElement, ignore);
|
||||||
if (ignore) return;
|
if (ignore) return;
|
||||||
// if(clickEle)
|
// if(clickEle)
|
||||||
@@ -60,11 +59,7 @@ export default function EmojiPicker({ selectEmoji }) {
|
|||||||
<div ref={ref} className={`picker ${visible ? "visible" : ""}`}>
|
<div ref={ref} className={`picker ${visible ? "visible" : ""}`}>
|
||||||
<Picker onSelect={handleSelect} />
|
<Picker onSelect={handleSelect} />
|
||||||
</div>
|
</div>
|
||||||
<SmileIcon
|
<SmileIcon data-emoji="toggler" className="emoji" onClick={togglePickerVisible} />
|
||||||
data-emoji="toggler"
|
|
||||||
className="emoji"
|
|
||||||
onClick={togglePickerVisible}
|
|
||||||
/>
|
|
||||||
</Styled>
|
</Styled>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -57,11 +57,7 @@ const Styled = styled.div`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
content: "";
|
content: "";
|
||||||
background: linear-gradient(
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0) 63.54%, #f3f4f6 93.09%);
|
||||||
180deg,
|
|
||||||
rgba(255, 255, 255, 0) 63.54%,
|
|
||||||
#f3f4f6 93.09%
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.icon {
|
.icon {
|
||||||
@@ -87,15 +83,11 @@ const renderContent = (data) => {
|
|||||||
let res = null;
|
let res = null;
|
||||||
switch (content_type) {
|
switch (content_type) {
|
||||||
case ContentTypes.text:
|
case ContentTypes.text:
|
||||||
res = reactStringReplace(
|
res = reactStringReplace(content, /(\s{1}@[0-9]+\s{1})/g, (match, idx) => {
|
||||||
content,
|
console.log("match", match);
|
||||||
/(\s{1}@[0-9]+\s{1})/g,
|
const uid = match.trim().slice(1);
|
||||||
(match, idx) => {
|
return <Mention popover={false} key={idx} uid={uid} />;
|
||||||
console.log("match", match);
|
});
|
||||||
const uid = match.trim().slice(1);
|
|
||||||
return <Mention popover={false} key={idx} uid={uid} />;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case ContentTypes.markdown:
|
case ContentTypes.markdown:
|
||||||
res = (
|
res = (
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export default function Toolbar({
|
|||||||
toggleMode,
|
toggleMode,
|
||||||
mode,
|
mode,
|
||||||
to,
|
to,
|
||||||
context,
|
context
|
||||||
}) {
|
}) {
|
||||||
const { addStageFile } = useUploadFile({ context, id: to });
|
const { addStageFile } = useUploadFile({ context, id: to });
|
||||||
const fileInputRef = useRef(null);
|
const fileInputRef = useRef(null);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export default function UploadFileList({ context = "", id = null }) {
|
|||||||
const [editInfo, setEditInfo] = useState(null);
|
const [editInfo, setEditInfo] = useState(null);
|
||||||
const { stageFiles, updateStageFile, removeStageFile } = useUploadFile({
|
const { stageFiles, updateStageFile, removeStageFile } = useUploadFile({
|
||||||
context,
|
context,
|
||||||
id,
|
id
|
||||||
});
|
});
|
||||||
const toggleModalVisible = (info) => {
|
const toggleModalVisible = (info) => {
|
||||||
setEditInfo((prev) => (prev ? null : info));
|
setEditInfo((prev) => (prev ? null : info));
|
||||||
@@ -50,19 +50,12 @@ export default function UploadFileList({ context = "", id = null }) {
|
|||||||
return (
|
return (
|
||||||
<li className="file" key={url}>
|
<li className="file" key={url}>
|
||||||
<div className="preview">
|
<div className="preview">
|
||||||
{type.startsWith("image") ? (
|
{type.startsWith("image") ? <img src={url} alt="image" /> : getFileIcon(type, name)}
|
||||||
<img src={url} alt="image" />
|
|
||||||
) : (
|
|
||||||
getFileIcon(type, name)
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<h4 className="name">{name}</h4>
|
<h4 className="name">{name}</h4>
|
||||||
<span className="size">{formatBytes(size)}</span>
|
<span className="size">{formatBytes(size)}</span>
|
||||||
<ul className="opts">
|
<ul className="opts">
|
||||||
<li
|
<li className="opt edit" onClick={handleOpenEditModal.bind(null, idx)}>
|
||||||
className="opt edit"
|
|
||||||
onClick={handleOpenEditModal.bind(null, idx)}
|
|
||||||
>
|
|
||||||
<EditIcon />
|
<EditIcon />
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user