Mono repo -> Master (#22553)
Combines the following repositories into one: https://github.com/mattermost/mattermost-server https://github.com/mattermost/mattermost-webapp https://github.com/mattermost/focalboard https://github.com/mattermost/mattermost-plugin-playbooks
Этот коммит содержится в:
182
webapp/platform/README.md
Обычный файл
182
webapp/platform/README.md
Обычный файл
@@ -0,0 +1,182 @@
|
||||
This folder contains a number of packages intended to be built and shipped separately on NPM as well as a few legacy packages for internal use only (`reselect` and `mattermost-redux`). The following documentation only applies to the newer packages and not to the legacy ones.
|
||||
|
||||
### Working with subpackages
|
||||
|
||||
To interact with one or more packages in a workspace, such as to add a dependency or run a script, use the `--workspace` (or `--workspaces`) flag. This can be done when using built-in NPM commands such as `npm add` or when running scripts. This doesn't need to be done from inside the package.
|
||||
|
||||
```sh
|
||||
# Add a dependency to a single package
|
||||
npm add react --workspace=packages/apple
|
||||
|
||||
# Build multiple packages
|
||||
npm run build --workspace=packages/banana --workspace=packages/carrot
|
||||
|
||||
# Clean all workspaces
|
||||
npm run clean --workspaces
|
||||
```
|
||||
|
||||
To install dependencies for a workspace, simply run `npm install` from the root of the source tree as you would do normally. Every packages' dependencies will be included in `node_modules` and in the `package-lock.json` which are shared across the repo.
|
||||
|
||||
### Importing a subpackage
|
||||
|
||||
Subpackages should be imported using their full name, both inside the web app and when installing them using `npm`. They should not be imported using a relative path, and the `src` folder shouldn't be necessary to include.
|
||||
|
||||
```javascript
|
||||
// Correct
|
||||
import {Client4} from '@mattermost/client';
|
||||
import {UserProfile} from '@mattermost/types/users';
|
||||
|
||||
// Incorrect
|
||||
import Client4 from 'packages/client/src/client4.ts';
|
||||
import {UserProfile} from '../../types/src/users';
|
||||
```
|
||||
|
||||
Some tools have difficulty doing this on their own, but they often support import path aliases so that we can keep them consistent acrosss the code base. More details on how to do this will be provided in packages where this is necessary such as `types`.
|
||||
|
||||
#### Importing one subpackage into another
|
||||
|
||||
When building packages that depend on each other, be careful to:
|
||||
|
||||
1. Avoid import loops. While JavaScript lets us get away with these in most cases within a project, we cannot have two packages that depend directly with each other.
|
||||
1. Not compile one subpackage into another. We don't want the published libraries to include code from one subpackage into another. They should be set up so that they're peer dependencies in the `package.json`, and if a project wants to use multiple packages, they can install them each separately.
|
||||
|
||||
As above, some tooling may need additional configuration to have one subpackage use code from another. For example, in packages compiled with the TypeScript compiler (tsc), you'll need to have the `tsconfig.json` from the dependent pacakge reference its dependency using the `references` field.
|
||||
|
||||
### Versioning subpackages
|
||||
|
||||
At this time, we'll have the version of each package match the version of the web app. Versions can be incremented for each affected package by using [`npm version`](https://docs.npmjs.com/cli/v6/commands/npm-version), and then `npm install` should be run to propagate those changes into the shared `package-lock.json`.
|
||||
|
||||
```sh
|
||||
# Set a version of a single package
|
||||
npm version 6.7.8 --workspace=packages/apple
|
||||
|
||||
# Increment the version of each package to the next minor version
|
||||
npm version minor --workspaces
|
||||
|
||||
## Increment the version of a package to a pre-release version of the next minor version
|
||||
npm version preminor --workspace=packages/apple
|
||||
```
|
||||
|
||||
When a subpackage imports another, it should be set to depend on the `*` version of the other subpackage.
|
||||
|
||||
### Adding a new subpackage
|
||||
|
||||
To set up a new package:
|
||||
|
||||
1. Add a `package.json` and `README.md` for that package.
|
||||
1. Ensure all source files are located in `src` and all compiled files are built to `lib`.
|
||||
1. Add an entry to the `workspaces` section of the root `package.json` so that NPM is aware of your package.
|
||||
1. Set up import aliases so that the package is visible from the web app to the following tools:
|
||||
1. **TypeScript** - In the root `tsconfig.json`, add an entry to the `compilerOptions.paths` section pointing to the `src` folder and an entry to the `references` section pointing to the root of your package which should contain its own `tsconfig.json`.
|
||||
|
||||
Note that the `compilerOptions.paths` entry will differ based on if your package exports just a single module (ie a single `index.js` file) or if it exports multiple submodules.
|
||||
|
||||
```json5
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@mattermost/apple": ["packages/apple/lib"], // import * as Apple from '@mattermost/apple';
|
||||
"@mattermost/banana/*": ["packages/banana/lib/*"], // import Yellow from '@mattermost/banana/yellow';
|
||||
}
|
||||
},
|
||||
"references": [
|
||||
{"path": "./packages/apple"},
|
||||
{"path": "./packages/banana"},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
1. **Jest** - Add an entry to the `jest.moduleNameMapper` section of the root `jest.config.js` for your package. Since that setting supports regexes, you can add these to the existing patterns used by the `client` and `types` packages.
|
||||
|
||||
Similar to TypeScript, this will differ based on if the package exports a single module or multiple modules.
|
||||
|
||||
```json
|
||||
{
|
||||
"jest": {
|
||||
"moduleNameMapper": {
|
||||
"^@mattermost/(apple|client)$": "<rootDir>/packages/$1/src",
|
||||
"^@mattermost/(banana|types)/(.*)$": "<rootDir>/packages/$1/src/$2",
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
1. Add the compiled code to the CircleCI dependency cache. This is done by modifying the `paths` used by the `save_cache` step in `.circleci/config.yml`
|
||||
```yml
|
||||
aliases:
|
||||
- &save_cache
|
||||
save_cache:
|
||||
paths:
|
||||
- ~/mattermost/mattermost-webapp/packages/apple/lib
|
||||
- ~/mattermost/mattermost-webapp/packages/banana/lib
|
||||
```
|
||||
|
||||
### Publishing a subpackage
|
||||
|
||||
The following is the rough process for releasing these packages. They'll require someone with write access on our NPM organization to run them, and they'll likely change over time as we improve this process.
|
||||
|
||||
For full releases accompanying new versions of Mattermost:
|
||||
|
||||
1. Clean the repo.
|
||||
|
||||
```sh
|
||||
make clean
|
||||
```
|
||||
|
||||
1. Update the version of the desired packages to match the server/web app as described above.
|
||||
|
||||
1. Download an up to date copy of the dependencies and update package-lock.json.
|
||||
|
||||
```sh
|
||||
make node_modules
|
||||
```
|
||||
|
||||
1. Check in the changes to the package-lock.json.
|
||||
|
||||
1. Build the desired packages.
|
||||
|
||||
```sh
|
||||
npm run build --workspace=packages/apple --workspace=packages/banana
|
||||
```
|
||||
|
||||
1. Test everything in the web app. This will be needed until the packages get their own standalone tests.
|
||||
|
||||
```sh
|
||||
make check-style check-types test
|
||||
```
|
||||
|
||||
1. Assuming those pass, you can now publish those packages to npm. You can also do a dry run first or use `npm pack` to see exactly which files will be pushed.
|
||||
|
||||
```sh
|
||||
# Run a dry run which will list all the files to be included in the published package.
|
||||
npm publish --dry-run --workspace=packages/apple
|
||||
|
||||
# Generate the tar file that will be uploaded to NPM for inspection.
|
||||
npm pack --workspace=packages/apple
|
||||
|
||||
# Actually publish these packages. You can also use --workspaces to publish everything.
|
||||
npm publish --access=public --workspace=packages/apple --workspace=packages/banana
|
||||
```
|
||||
|
||||
The packages have now been published! There's still a few remaining cleanup tasks to do though.
|
||||
|
||||
1. Tag the commit for each package that has been updated. The tag name should be of the form `@mattermost/package-name@x.y.z`.
|
||||
|
||||
1. Push that commit and the corresponding tags up to GitHub
|
||||
|
||||
```sh
|
||||
git push release-x.y
|
||||
git push origin @mattermost/apple@x.y.z @mattermost/banana@x.y.z
|
||||
```
|
||||
|
||||
#### Publishing a pre-release version
|
||||
|
||||
Similarly, you can publish a pre-release version of the package. This can be done either to use changes from master while developing another product/plugin or to generate a release candidate.
|
||||
|
||||
This process is the same as above, except the version will have a suffix like `-1`, `-2`, etc. As explained above, this can be automatically done by using `npm version preminor` for minor releases, `npm version premajor` for major releases, and `npm version prerelease` for patch releases. These versions won't be automatically installed when people add them using `npm add` without a version, but they can be installed by specifying the version number manually.
|
||||
|
||||
### Caveats
|
||||
|
||||
1. Currently, all packages are treated by CI as if they're part of the web app. This means that, for example, their style checking and tests are ran as part of the web app. In turn, that means that regardless of what tooling we use to build each package, they'll be compiled into the web app using webpack directly from source, and that it's possible for them to behave slightly differently in development compared to after release.
|
||||
|
||||
Eventually, we hope to get these building in parallel (so instead of having webpack watch the whole repo for changes during development, we'll have multiple watchers for the web app and each package) which should solve this issue, but that requires much larger changes that we're not ready to do yet.
|
||||
1. For packages that export multiple submodules (such as `types`), we've chosen to expose these using Node's [subpath exports](https://nodejs.org/api/packages.html#subpath-exports) feature. Some tools like Webpack support this natively, but others like TypeScript and Jest don't support it yet. We've provided steps on how to support this in the `README.md` for the `types` package, but this may vary depending on the project's setup.
|
||||
14
webapp/platform/client/README.md
Обычный файл
14
webapp/platform/client/README.md
Обычный файл
@@ -0,0 +1,14 @@
|
||||
# Mattermost Client
|
||||
|
||||
This package contains the JavaScript/TypeScript client for [Mattermost](https://github.com/mattermost/mattermost-server). It's used by [the Mattermost web app](https://github.com/mattermost/mattermost-webapp) and related projects.
|
||||
|
||||
## Compilation and Packaging
|
||||
|
||||
As a member of Mattermost with write access to our NPM organization, you can build and publish this package by running the following commands:
|
||||
|
||||
```bash
|
||||
npm run build --workspace=packages/client
|
||||
npm publish --workspace=packages/client
|
||||
```
|
||||
|
||||
Make sure to increment the version number in `package.json` first! You can add `-0`, `-1`, etc for pre-release versions.
|
||||
9
webapp/platform/client/babel.config.js
Обычный файл
9
webapp/platform/client/babel.config.js
Обычный файл
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
module.exports = {
|
||||
presets: [
|
||||
['@babel/preset-env', {targets: {node: 'current'}}],
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
};
|
||||
11
webapp/platform/client/jest.config.js
Обычный файл
11
webapp/platform/client/jest.config.js
Обычный файл
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/** @type {import('jest').Config} */
|
||||
|
||||
module.exports = {
|
||||
moduleNameMapper: {
|
||||
'^@mattermost/types/(.*)$': '<rootDir>/../types/src/$1',
|
||||
},
|
||||
setupFiles: ['isomorphic-fetch'],
|
||||
};
|
||||
43
webapp/platform/client/package.json
Обычный файл
43
webapp/platform/client/package.json
Обычный файл
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@mattermost/client",
|
||||
"version": "7.9.0",
|
||||
"description": "JavaScript/TypeScript client for Mattermost",
|
||||
"keywords": [
|
||||
"mattermost"
|
||||
],
|
||||
"homepage": "https://github.com/mattermost/mattermost-webapp/tree/master/packages/client#readme",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "github:mattermost/mattermost-webapp",
|
||||
"directory": "packages/client"
|
||||
},
|
||||
"dependencies": {
|
||||
"form-data": "4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-typescript": "7.21.0",
|
||||
"isomorphic-fetch": "3.0.0",
|
||||
"jest": "*",
|
||||
"nock": "*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@mattermost/types": "*",
|
||||
"typescript": "^4.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc --build --verbose",
|
||||
"run": "tsc --watch --preserveWatchOutput",
|
||||
"test": "jest",
|
||||
"clean": "rm -rf tsconfig.tsbuildinfo ./lib"
|
||||
}
|
||||
}
|
||||
92
webapp/platform/client/src/client4.test.ts
Обычный файл
92
webapp/platform/client/src/client4.test.ts
Обычный файл
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import nock from 'nock';
|
||||
|
||||
import Client4, {ClientError, HEADER_X_VERSION_ID} from './client4';
|
||||
import {TelemetryHandler} from './telemetry';
|
||||
|
||||
describe('Client4', () => {
|
||||
beforeAll(() => {
|
||||
if (!nock.isActive()) {
|
||||
nock.activate();
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
nock.restore();
|
||||
});
|
||||
|
||||
describe('doFetchWithResponse', () => {
|
||||
test('serverVersion should be set from response header', async () => {
|
||||
const client = new Client4();
|
||||
client.setUrl('http://mattermost.example.com');
|
||||
|
||||
expect(client.serverVersion).toEqual('');
|
||||
|
||||
nock(client.getBaseRoute()).
|
||||
get('/users/me').
|
||||
reply(200, '{}', {[HEADER_X_VERSION_ID]: '5.0.0.5.0.0.abc123'});
|
||||
|
||||
await client.getMe();
|
||||
|
||||
expect(client.serverVersion).toEqual('5.0.0.5.0.0.abc123');
|
||||
|
||||
nock(client.getBaseRoute()).
|
||||
get('/users/me').
|
||||
reply(200, '{}', {[HEADER_X_VERSION_ID]: '5.3.0.5.3.0.abc123'});
|
||||
|
||||
await client.getMe();
|
||||
|
||||
expect(client.serverVersion).toEqual('5.3.0.5.3.0.abc123');
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchWithGraphQL', () => {
|
||||
test('Should have correct graphql url', async () => {
|
||||
const client = new Client4();
|
||||
client.setUrl('http://mattermost.example.com');
|
||||
|
||||
expect(client.getGraphQLUrl()).toEqual('http://mattermost.example.com/api/v5/graphql');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ClientError', () => {
|
||||
test('standard fields should be enumerable', () => {
|
||||
const error = new ClientError('https://example.com', {
|
||||
message: 'This is a message',
|
||||
server_error_id: 'test.app_error',
|
||||
status_code: 418,
|
||||
url: 'https://example.com/api/v4/error',
|
||||
});
|
||||
|
||||
const copy = {...error};
|
||||
|
||||
expect(copy.message).toEqual(error.message);
|
||||
expect(copy.server_error_id).toEqual(error.server_error_id);
|
||||
expect(copy.status_code).toEqual(error.status_code);
|
||||
expect(copy.url).toEqual(error.url);
|
||||
});
|
||||
});
|
||||
|
||||
describe('trackEvent', () => {
|
||||
class TestTelemetryHandler implements TelemetryHandler {
|
||||
trackEvent = jest.fn();
|
||||
pageVisited = jest.fn();
|
||||
}
|
||||
|
||||
test('should call the attached RudderTelemetryHandler, if one is attached to Client4', () => {
|
||||
const client = new Client4();
|
||||
client.setUrl('http://mattermost.example.com');
|
||||
|
||||
expect(() => client.trackEvent('test', 'onClick')).not.toThrowError();
|
||||
|
||||
const handler = new TestTelemetryHandler();
|
||||
|
||||
client.setTelemetryHandler(handler);
|
||||
client.trackEvent('test', 'onClick');
|
||||
|
||||
expect(handler.trackEvent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
4307
webapp/platform/client/src/client4.ts
Обычный файл
4307
webapp/platform/client/src/client4.ts
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
37
webapp/platform/client/src/errors.test.ts
Обычный файл
37
webapp/platform/client/src/errors.test.ts
Обычный файл
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import Client4 from './client4';
|
||||
|
||||
import {cleanUrlForLogging} from './errors';
|
||||
|
||||
describe('cleanUrlForLogging', () => {
|
||||
const baseUrl = 'https://mattermost.example.com/subpath';
|
||||
|
||||
const client = new Client4();
|
||||
client.setUrl(baseUrl);
|
||||
|
||||
const testCases = [{
|
||||
name: 'should remove server URL',
|
||||
input: client.getUserRoute('me'),
|
||||
expected: `${client.urlVersion}/users/me`,
|
||||
}, {
|
||||
name: 'should filter user IDs',
|
||||
input: client.getUserRoute('1234'),
|
||||
expected: `${client.urlVersion}/users/<filtered>`,
|
||||
}, {
|
||||
name: 'should filter email addresses',
|
||||
input: `${client.getUsersRoute()}/email/test@example.com`,
|
||||
expected: `${client.urlVersion}/users/email/<filtered>`,
|
||||
}, {
|
||||
name: 'should filter query parameters',
|
||||
input: `${client.getUserRoute('me')}?foo=bar`,
|
||||
expected: `${client.urlVersion}/users/me?<filtered>`,
|
||||
}];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
test(testCase.name, () => {
|
||||
expect(cleanUrlForLogging(baseUrl, testCase.input)).toEqual(testCase.expected);
|
||||
});
|
||||
}
|
||||
});
|
||||
52
webapp/platform/client/src/errors.ts
Обычный файл
52
webapp/platform/client/src/errors.ts
Обычный файл
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// Given a URL from an API request, return a URL that has any parts removed that are either sensitive or that would
|
||||
// prevent properly grouping the messages in Sentry.
|
||||
export function cleanUrlForLogging(baseUrl: string, apiUrl: string): string {
|
||||
let url = apiUrl;
|
||||
|
||||
// Trim the host name
|
||||
url = url.substring(baseUrl.length);
|
||||
|
||||
// Filter the query string
|
||||
const index = url.indexOf('?');
|
||||
if (index !== -1) {
|
||||
url = url.substring(0, index);
|
||||
}
|
||||
|
||||
// A non-exhaustive whitelist to exclude parts of the URL that are unimportant (eg IDs) or may be sentsitive
|
||||
// (eg email addresses). We prefer filtering out fields that aren't recognized because there should generally
|
||||
// be enough left over for debugging.
|
||||
//
|
||||
// Note that new API routes don't need to be added here since this shouldn't be happening for newly added routes.
|
||||
const whitelist = [
|
||||
'api', 'v4', 'users', 'teams', 'scheme', 'name', 'members', 'channels', 'posts', 'reactions', 'commands',
|
||||
'files', 'preferences', 'hooks', 'incoming', 'outgoing', 'oauth', 'apps', 'emoji', 'brand', 'image',
|
||||
'data_retention', 'jobs', 'plugins', 'roles', 'system', 'timezones', 'schemes', 'redirect_location', 'patch',
|
||||
'mfa', 'password', 'reset', 'send', 'active', 'verify', 'terms_of_service', 'login', 'logout', 'ids',
|
||||
'usernames', 'me', 'username', 'email', 'default', 'sessions', 'revoke', 'all', 'audits', 'device', 'status',
|
||||
'search', 'switch', 'authorized', 'authorize', 'deauthorize', 'tokens', 'disable', 'enable', 'exists', 'unread',
|
||||
'invite', 'batch', 'stats', 'import', 'schemeRoles', 'direct', 'group', 'convert', 'view', 'search_autocomplete',
|
||||
'thread', 'info', 'flagged', 'pinned', 'pin', 'unpin', 'opengraph', 'actions', 'thumbnail', 'preview', 'link',
|
||||
'delete', 'logs', 'ping', 'config', 'client', 'license', 'websocket', 'webrtc', 'token', 'regen_token',
|
||||
'autocomplete', 'execute', 'regen_secret', 'policy', 'type', 'cancel', 'reload', 'environment', 's3_test', 'file',
|
||||
'caches', 'invalidate', 'database', 'recycle', 'compliance', 'reports', 'cluster', 'ldap', 'test', 'sync', 'saml',
|
||||
'certificate', 'public', 'private', 'idp', 'elasticsearch', 'purge_indexes', 'analytics', 'old', 'webapp', 'fake',
|
||||
];
|
||||
|
||||
url = url.split('/').map((part) => {
|
||||
if (part !== '' && whitelist.indexOf(part) === -1) {
|
||||
return '<filtered>';
|
||||
}
|
||||
|
||||
return part;
|
||||
}).join('/');
|
||||
|
||||
if (index !== -1) {
|
||||
// Add this on afterwards since it wouldn't pass the whitelist
|
||||
url += '?<filtered>';
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
18
webapp/platform/client/src/helpers.test.ts
Обычный файл
18
webapp/platform/client/src/helpers.test.ts
Обычный файл
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {buildQueryString} from './helpers';
|
||||
|
||||
describe('Helpers', () => {
|
||||
test.each([
|
||||
[{}, ''],
|
||||
[{a: 1}, '?a=1'],
|
||||
[{a: 1, b: 'str'}, '?a=1&b=str'],
|
||||
[{a: 1, b: 'str', c: undefined}, '?a=1&b=str'],
|
||||
[{a: 1, b: 'str', c: 0}, '?a=1&b=str&c=0'],
|
||||
[{a: 1, b: 'str', c: ''}, '?a=1&b=str&c='],
|
||||
[{a: 1, b: undefined, c: 'str'}, '?a=1&c=str'],
|
||||
])('buildQueryString with %o should return %s', (params, expected) => {
|
||||
expect(buildQueryString(params)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
17
webapp/platform/client/src/helpers.ts
Обычный файл
17
webapp/platform/client/src/helpers.ts
Обычный файл
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export function buildQueryString(parameters: Record<string, any>): string {
|
||||
const keys = Object.keys(parameters);
|
||||
if (keys.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const queryParams = Object.entries(parameters).
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
filter(([_, value]) => value !== undefined).
|
||||
map(([key, value]) => `${key}=${encodeURIComponent(value)}`).
|
||||
join('&');
|
||||
|
||||
return queryParams.length > 0 ? `?${queryParams}` : '';
|
||||
}
|
||||
13
webapp/platform/client/src/index.ts
Обычный файл
13
webapp/platform/client/src/index.ts
Обычный файл
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export {
|
||||
default as Client4,
|
||||
ClientError,
|
||||
DEFAULT_LIMIT_AFTER,
|
||||
DEFAULT_LIMIT_BEFORE,
|
||||
} from './client4';
|
||||
|
||||
export type {TelemetryHandler} from './telemetry';
|
||||
export type {WebSocketMessage} from './websocket';
|
||||
export {default as WebSocketClient} from './websocket';
|
||||
7
webapp/platform/client/src/telemetry.ts
Обычный файл
7
webapp/platform/client/src/telemetry.ts
Обычный файл
@@ -0,0 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export interface TelemetryHandler {
|
||||
trackEvent: (userId: string, userRoles: string, category: string, event: string, props?: any) => void;
|
||||
pageVisited: (userId: string, userRoles: string, category: string, name: string) => void;
|
||||
}
|
||||
410
webapp/platform/client/src/websocket.ts
Обычный файл
410
webapp/platform/client/src/websocket.ts
Обычный файл
@@ -0,0 +1,410 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
const MAX_WEBSOCKET_FAILS = 7;
|
||||
const MIN_WEBSOCKET_RETRY_TIME = 3000; // 3 sec
|
||||
const MAX_WEBSOCKET_RETRY_TIME = 300000; // 5 mins
|
||||
const JITTER_RANGE = 2000; // 2 sec
|
||||
|
||||
const WEBSOCKET_HELLO = 'hello';
|
||||
|
||||
export type MessageListener = (msg: WebSocketMessage) => void;
|
||||
export type FirstConnectListener = () => void;
|
||||
export type ReconnectListener = () => void;
|
||||
export type MissedMessageListener = () => void;
|
||||
export type ErrorListener = (event: Event) => void;
|
||||
export type CloseListener = (connectFailCount: number) => void;
|
||||
|
||||
export default class WebSocketClient {
|
||||
private conn: WebSocket | null;
|
||||
private connectionUrl: string | null;
|
||||
|
||||
// responseSequence is the number to track a response sent
|
||||
// via the websocket. A response will always have the same sequence number
|
||||
// as the request.
|
||||
private responseSequence: number;
|
||||
|
||||
// serverSequence is the incrementing sequence number from the
|
||||
// server-sent event stream.
|
||||
private serverSequence: number;
|
||||
private connectFailCount: number;
|
||||
private responseCallbacks: {[x: number]: ((msg: any) => void)};
|
||||
|
||||
/**
|
||||
* @deprecated Use messageListeners instead
|
||||
*/
|
||||
private eventCallback: MessageListener | null = null;
|
||||
|
||||
/**
|
||||
* @deprecated Use firstConnectListeners instead
|
||||
*/
|
||||
private firstConnectCallback: FirstConnectListener | null = null;
|
||||
|
||||
/**
|
||||
* @deprecated Use reconnectListeners instead
|
||||
*/
|
||||
private reconnectCallback: ReconnectListener | null = null;
|
||||
|
||||
/**
|
||||
* @deprecated Use missedMessageListeners instead
|
||||
*/
|
||||
private missedEventCallback: MissedMessageListener | null = null;
|
||||
|
||||
/**
|
||||
* @deprecated Use errorListeners instead
|
||||
*/
|
||||
private errorCallback: ErrorListener | null = null;
|
||||
|
||||
/**
|
||||
* @deprecated Use closeListeners instead
|
||||
*/
|
||||
private closeCallback: CloseListener | null = null;
|
||||
|
||||
private messageListeners = new Set<MessageListener>();
|
||||
private firstConnectListeners = new Set<FirstConnectListener>();
|
||||
private reconnectListeners = new Set<ReconnectListener>();
|
||||
private missedMessageListeners = new Set<MissedMessageListener>();
|
||||
private errorListeners = new Set<ErrorListener>();
|
||||
private closeListeners = new Set<CloseListener>();
|
||||
|
||||
private connectionId: string | null;
|
||||
|
||||
constructor() {
|
||||
this.conn = null;
|
||||
this.connectionUrl = null;
|
||||
this.responseSequence = 1;
|
||||
this.serverSequence = 0;
|
||||
this.connectFailCount = 0;
|
||||
this.responseCallbacks = {};
|
||||
this.connectionId = '';
|
||||
}
|
||||
|
||||
// on connect, only send auth cookie and blank state.
|
||||
// on hello, get the connectionID and store it.
|
||||
// on reconnect, send cookie, connectionID, sequence number.
|
||||
initialize(connectionUrl = this.connectionUrl, token?: string) {
|
||||
if (this.conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (connectionUrl == null) {
|
||||
console.log('websocket must have connection url'); //eslint-disable-line no-console
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.connectFailCount === 0) {
|
||||
console.log('websocket connecting to ' + connectionUrl); //eslint-disable-line no-console
|
||||
}
|
||||
|
||||
// Add connection id, and last_sequence_number to the query param.
|
||||
// We cannot use a cookie because it will bleed across tabs.
|
||||
// We cannot also send it as part of the auth_challenge, because the session cookie is already sent with the request.
|
||||
this.conn = new WebSocket(`${connectionUrl}?connection_id=${this.connectionId}&sequence_number=${this.serverSequence}`);
|
||||
this.connectionUrl = connectionUrl;
|
||||
|
||||
this.conn.onopen = () => {
|
||||
if (token) {
|
||||
this.sendMessage('authentication_challenge', {token});
|
||||
}
|
||||
|
||||
if (this.connectFailCount > 0) {
|
||||
console.log('websocket re-established connection'); //eslint-disable-line no-console
|
||||
|
||||
this.reconnectCallback?.();
|
||||
this.reconnectListeners.forEach((listener) => listener());
|
||||
} else if (this.firstConnectCallback || this.firstConnectListeners.size > 0) {
|
||||
this.firstConnectCallback?.();
|
||||
this.firstConnectListeners.forEach((listener) => listener());
|
||||
}
|
||||
|
||||
this.connectFailCount = 0;
|
||||
};
|
||||
|
||||
this.conn.onclose = () => {
|
||||
this.conn = null;
|
||||
this.responseSequence = 1;
|
||||
|
||||
if (this.connectFailCount === 0) {
|
||||
console.log('websocket closed'); //eslint-disable-line no-console
|
||||
}
|
||||
|
||||
this.connectFailCount++;
|
||||
|
||||
this.closeCallback?.(this.connectFailCount);
|
||||
this.closeListeners.forEach((listener) => listener(this.connectFailCount));
|
||||
|
||||
let retryTime = MIN_WEBSOCKET_RETRY_TIME;
|
||||
|
||||
// If we've failed a bunch of connections then start backing off
|
||||
if (this.connectFailCount > MAX_WEBSOCKET_FAILS) {
|
||||
retryTime = MIN_WEBSOCKET_RETRY_TIME * this.connectFailCount * this.connectFailCount;
|
||||
if (retryTime > MAX_WEBSOCKET_RETRY_TIME) {
|
||||
retryTime = MAX_WEBSOCKET_RETRY_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
// Applying jitter to avoid thundering herd problems.
|
||||
retryTime += Math.random() * JITTER_RANGE;
|
||||
|
||||
setTimeout(
|
||||
() => {
|
||||
this.initialize(connectionUrl, token);
|
||||
},
|
||||
retryTime,
|
||||
);
|
||||
};
|
||||
|
||||
this.conn.onerror = (evt) => {
|
||||
if (this.connectFailCount <= 1) {
|
||||
console.log('websocket error'); //eslint-disable-line no-console
|
||||
console.log(evt); //eslint-disable-line no-console
|
||||
}
|
||||
|
||||
this.errorCallback?.(evt);
|
||||
this.errorListeners.forEach((listener) => listener(evt));
|
||||
};
|
||||
|
||||
this.conn.onmessage = (evt) => {
|
||||
const msg = JSON.parse(evt.data);
|
||||
if (msg.seq_reply) {
|
||||
// This indicates a reply to a websocket request.
|
||||
// We ignore sequence number validation of message responses
|
||||
// and only focus on the purely server side event stream.
|
||||
if (msg.error) {
|
||||
console.log(msg); //eslint-disable-line no-console
|
||||
}
|
||||
|
||||
if (this.responseCallbacks[msg.seq_reply]) {
|
||||
this.responseCallbacks[msg.seq_reply](msg);
|
||||
Reflect.deleteProperty(this.responseCallbacks, msg.seq_reply);
|
||||
}
|
||||
} else if (this.eventCallback || this.messageListeners.size > 0) {
|
||||
// We check the hello packet, which is always the first packet in a stream.
|
||||
if (msg.event === WEBSOCKET_HELLO && (this.missedEventCallback || this.missedMessageListeners.size > 0)) {
|
||||
console.log('got connection id ', msg.data.connection_id); //eslint-disable-line no-console
|
||||
// If we already have a connectionId present, and server sends a different one,
|
||||
// that means it's either a long timeout, or server restart, or sequence number is not found.
|
||||
// Then we do the sync calls, and reset sequence number to 0.
|
||||
if (this.connectionId !== '' && this.connectionId !== msg.data.connection_id) {
|
||||
console.log('long timeout, or server restart, or sequence number is not found.'); //eslint-disable-line no-console
|
||||
|
||||
this.missedEventCallback?.();
|
||||
this.missedMessageListeners.forEach((listener) => listener());
|
||||
|
||||
this.serverSequence = 0;
|
||||
}
|
||||
|
||||
// If it's a fresh connection, we have to set the connectionId regardless.
|
||||
// And if it's an existing connection, setting it again is harmless, and keeps the code simple.
|
||||
this.connectionId = msg.data.connection_id;
|
||||
}
|
||||
|
||||
// Now we check for sequence number, and if it does not match,
|
||||
// we just disconnect and reconnect.
|
||||
if (msg.seq !== this.serverSequence) {
|
||||
console.log('missed websocket event, act_seq=' + msg.seq + ' exp_seq=' + this.serverSequence); //eslint-disable-line no-console
|
||||
// We are not calling this.close() because we need to auto-restart.
|
||||
this.connectFailCount = 0;
|
||||
this.responseSequence = 1;
|
||||
this.conn?.close(); // Will auto-reconnect after MIN_WEBSOCKET_RETRY_TIME.
|
||||
return;
|
||||
}
|
||||
this.serverSequence = msg.seq + 1;
|
||||
|
||||
this.eventCallback?.(msg);
|
||||
this.messageListeners.forEach((listener) => listener(msg));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use addMessageListener instead
|
||||
*/
|
||||
setEventCallback(callback: MessageListener) {
|
||||
this.eventCallback = callback;
|
||||
}
|
||||
|
||||
addMessageListener(listener: MessageListener) {
|
||||
this.messageListeners.add(listener);
|
||||
|
||||
if (this.messageListeners.size > 5) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`WebSocketClient has ${this.messageListeners.size} message listeners registered`);
|
||||
}
|
||||
}
|
||||
|
||||
removeMessageListener(listener: MessageListener) {
|
||||
this.messageListeners.delete(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use addFirstConnectListener instead
|
||||
*/
|
||||
setFirstConnectCallback(callback: FirstConnectListener) {
|
||||
this.firstConnectCallback = callback;
|
||||
}
|
||||
|
||||
addFirstConnectListener(listener: FirstConnectListener) {
|
||||
this.firstConnectListeners.add(listener);
|
||||
|
||||
if (this.firstConnectListeners.size > 5) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`WebSocketClient has ${this.firstConnectListeners.size} first connect listeners registered`);
|
||||
}
|
||||
}
|
||||
|
||||
removeFirstConnectListener(listener: FirstConnectListener) {
|
||||
this.firstConnectListeners.delete(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use addReconnectListener instead
|
||||
*/
|
||||
setReconnectCallback(callback: ReconnectListener) {
|
||||
this.reconnectCallback = callback;
|
||||
}
|
||||
|
||||
addReconnectListener(listener: ReconnectListener) {
|
||||
this.reconnectListeners.add(listener);
|
||||
|
||||
if (this.reconnectListeners.size > 5) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`WebSocketClient has ${this.reconnectListeners.size} reconnect listeners registered`);
|
||||
}
|
||||
}
|
||||
|
||||
removeReconnectListener(listener: ReconnectListener) {
|
||||
this.reconnectListeners.delete(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use addMissedMessageListener instead
|
||||
*/
|
||||
setMissedEventCallback(callback: MissedMessageListener) {
|
||||
this.missedEventCallback = callback;
|
||||
}
|
||||
|
||||
addMissedMessageListener(listener: MissedMessageListener) {
|
||||
this.missedMessageListeners.add(listener);
|
||||
|
||||
if (this.missedMessageListeners.size > 5) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`WebSocketClient has ${this.missedMessageListeners.size} missed message listeners registered`);
|
||||
}
|
||||
}
|
||||
|
||||
removeMissedMessageListener(listener: MissedMessageListener) {
|
||||
this.missedMessageListeners.delete(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use addErrorListener instead
|
||||
*/
|
||||
setErrorCallback(callback: ErrorListener) {
|
||||
this.errorCallback = callback;
|
||||
}
|
||||
|
||||
addErrorListener(listener: ErrorListener) {
|
||||
this.errorListeners.add(listener);
|
||||
|
||||
if (this.errorListeners.size > 5) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`WebSocketClient has ${this.errorListeners.size} error listeners registered`);
|
||||
}
|
||||
}
|
||||
|
||||
removeErrorListener(listener: ErrorListener) {
|
||||
this.errorListeners.delete(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use addCloseListener instead
|
||||
*/
|
||||
setCloseCallback(callback: CloseListener) {
|
||||
this.closeCallback = callback;
|
||||
}
|
||||
|
||||
addCloseListener(listener: CloseListener) {
|
||||
this.closeListeners.add(listener);
|
||||
|
||||
if (this.closeListeners.size > 5) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`WebSocketClient has ${this.closeListeners.size} close listeners registered`);
|
||||
}
|
||||
}
|
||||
|
||||
removeCloseListener(listener: CloseListener) {
|
||||
this.closeListeners.delete(listener);
|
||||
}
|
||||
|
||||
close() {
|
||||
this.connectFailCount = 0;
|
||||
this.responseSequence = 1;
|
||||
if (this.conn && this.conn.readyState === WebSocket.OPEN) {
|
||||
this.conn.onclose = () => {};
|
||||
this.conn.close();
|
||||
this.conn = null;
|
||||
console.log('websocket closed'); //eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage(action: string, data: any, responseCallback?: () => void) {
|
||||
const msg = {
|
||||
action,
|
||||
seq: this.responseSequence++,
|
||||
data,
|
||||
};
|
||||
|
||||
if (responseCallback) {
|
||||
this.responseCallbacks[msg.seq] = responseCallback;
|
||||
}
|
||||
|
||||
if (this.conn && this.conn.readyState === WebSocket.OPEN) {
|
||||
this.conn.send(JSON.stringify(msg));
|
||||
} else if (!this.conn || this.conn.readyState === WebSocket.CLOSED) {
|
||||
this.conn = null;
|
||||
this.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
userTyping(channelId: string, parentId: string, callback?: () => void) {
|
||||
const data = {
|
||||
channel_id: channelId,
|
||||
parent_id: parentId,
|
||||
};
|
||||
this.sendMessage('user_typing', data, callback);
|
||||
}
|
||||
|
||||
userUpdateActiveStatus(userIsActive: boolean, manual: boolean, callback?: () => void) {
|
||||
const data = {
|
||||
user_is_active: userIsActive,
|
||||
manual,
|
||||
};
|
||||
this.sendMessage('user_update_active_status', data, callback);
|
||||
}
|
||||
|
||||
getStatuses(callback?: () => void) {
|
||||
this.sendMessage('get_statuses', null, callback);
|
||||
}
|
||||
|
||||
getStatusesByIds(userIds: string[], callback?: () => void) {
|
||||
const data = {
|
||||
user_ids: userIds,
|
||||
};
|
||||
this.sendMessage('get_statuses_by_ids', data, callback);
|
||||
}
|
||||
}
|
||||
|
||||
export type WebSocketBroadcast = {
|
||||
omit_users: Record<string, boolean>;
|
||||
user_id: string;
|
||||
channel_id: string;
|
||||
team_id: string;
|
||||
}
|
||||
|
||||
export type WebSocketMessage<T = any> = {
|
||||
event: string;
|
||||
data: T;
|
||||
broadcast: WebSocketBroadcast;
|
||||
seq: number;
|
||||
}
|
||||
24
webapp/platform/client/tsconfig.json
Обычный файл
24
webapp/platform/client/tsconfig.json
Обычный файл
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"target": "es6",
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"jsx": "react",
|
||||
"outDir": "./lib",
|
||||
"rootDir": "./src",
|
||||
"composite": true,
|
||||
"paths": {
|
||||
"@mattermost/types/*": ["../types/lib/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["**/node_modules", "**/lib", "**/*.test.js", "**/*.test.ts"],
|
||||
"references": [
|
||||
{"path": "../types"}
|
||||
]
|
||||
}
|
||||
24
webapp/platform/components/README.md
Обычный файл
24
webapp/platform/components/README.md
Обычный файл
@@ -0,0 +1,24 @@
|
||||
# Mattermost Components
|
||||
|
||||
The goal of this package is to be a place where components common to all products can be shared.
|
||||
|
||||
Currently a work in progress. Next steps involve implementing webpack module federation in the webapp and locking down how the development experience will work for the webapp multi product architecture.
|
||||
|
||||
## Usage
|
||||
|
||||
Coming soon with multi product architecture.
|
||||
|
||||
## Compilation
|
||||
|
||||
Building is done using rollup. This must be done so the webapp webpack will pick up the changes. (multi product development experience coming soon)
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
or from the root of the webapp with
|
||||
|
||||
```bash
|
||||
npm run build --workspace=packages/mattermost
|
||||
```
|
||||
|
||||
42
webapp/platform/components/babel.config.js
Обычный файл
42
webapp/platform/components/babel.config.js
Обычный файл
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
const config = {
|
||||
presets: [
|
||||
['@babel/preset-env', {
|
||||
targets: {
|
||||
chrome: 66,
|
||||
firefox: 60,
|
||||
edge: 42,
|
||||
safari: 12,
|
||||
},
|
||||
modules: false,
|
||||
}],
|
||||
['@babel/preset-react', {
|
||||
useBuiltIns: true,
|
||||
}],
|
||||
['@babel/typescript', {
|
||||
allExtensions: true,
|
||||
isTSX: true,
|
||||
}],
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-transform-runtime',
|
||||
[
|
||||
'babel-plugin-styled-components',
|
||||
{
|
||||
ssr: false,
|
||||
fileName: false,
|
||||
},
|
||||
],
|
||||
[
|
||||
'formatjs',
|
||||
{
|
||||
idInterpolationPattern: '[sha512:contenthash:base64:6]',
|
||||
ast: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
61
webapp/platform/components/package.json
Обычный файл
61
webapp/platform/components/package.json
Обычный файл
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"name": "@mattermost/components",
|
||||
"version": "7.4.0",
|
||||
"module": "dist/index.esm.js",
|
||||
"types": "dist/index.esm.d.ts",
|
||||
"styles": "dist/index.esm.css",
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"run": "rollup -c --watch",
|
||||
"clean": "rm -rf node_modules dist"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.17.6",
|
||||
"@babel/core": "^7.17.7",
|
||||
"@babel/plugin-transform-runtime": "^7.17.0",
|
||||
"@babel/preset-env": "^7.16.11",
|
||||
"@babel/preset-react": "^7.16.7",
|
||||
"@babel/preset-typescript": "^7.16.7",
|
||||
"@rollup/plugin-babel": "^5.3.1",
|
||||
"@rollup/plugin-commonjs": "^21.0.2",
|
||||
"@rollup/plugin-node-resolve": "^13.1.3",
|
||||
"@rollup/plugin-typescript": "^8.3.1",
|
||||
"@types/lodash": "^4.14.178",
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-bootstrap": "^0.32.22",
|
||||
"@types/react-dom": "^17.0.2",
|
||||
"@types/react-redux": "^7.1.21",
|
||||
"@types/shallow-equals": "^1.0.0",
|
||||
"@types/styled-components": "^5.1.19",
|
||||
"babel-loader": "^8.2.3",
|
||||
"babel-plugin-formatjs": "10.3.14",
|
||||
"babel-plugin-styled-components": "^2.0.6",
|
||||
"css-loader": "^6.7.1",
|
||||
"rollup": "^2.75.7",
|
||||
"rollup-plugin-auto-external": "^2.0.0",
|
||||
"rollup-plugin-peer-deps-external": "^2.2.4",
|
||||
"rollup-plugin-scss": "^3.0.0",
|
||||
"rollup-plugin-ts": "^2.0.5",
|
||||
"sass": "^1.49.9",
|
||||
"sass-loader": "^12.6.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"typescript": "^4.3.4",
|
||||
"webpack": "^5.70.0",
|
||||
"webpack-cli": "^4.9.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/runtime-corejs3": "^7.17.8",
|
||||
"@mui/base": "5.0.0-alpha.116",
|
||||
"@mui/material": "5.11.7",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"classnames": "^2.3.1",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^17.0.2",
|
||||
"react-bootstrap": "github:mattermost/react-bootstrap#d821e2b1db1059bd36112d7587fd1b0912b27626",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-intl": "^5.20.0",
|
||||
"shallow-equals": "^1.0.0",
|
||||
"styled-components": "^5.3.5",
|
||||
"tippy.js": "^6.3.7"
|
||||
}
|
||||
}
|
||||
44
webapp/platform/components/rollup.config.js
Обычный файл
44
webapp/platform/components/rollup.config.js
Обычный файл
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import scss from 'rollup-plugin-scss';
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
|
||||
import packagejson from './package.json';
|
||||
|
||||
const externals = [
|
||||
...Object.keys(packagejson.dependencies || {}),
|
||||
...Object.keys(packagejson.peerDependencies || {}),
|
||||
'mattermost-redux',
|
||||
'reselect',
|
||||
];
|
||||
|
||||
export default [
|
||||
{
|
||||
input: 'src/index.tsx',
|
||||
output: [
|
||||
{
|
||||
sourcemap: true,
|
||||
file: packagejson.module,
|
||||
format: 'es',
|
||||
globals: {'styled-components': 'styled'},
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
scss(),
|
||||
resolve({
|
||||
browser: true,
|
||||
extensions: ['.ts', '.tsx'],
|
||||
}),
|
||||
commonjs(),
|
||||
typescript(),
|
||||
],
|
||||
external: (pkg) => externals.some((external) => pkg.startsWith(external)),
|
||||
watch: {
|
||||
clearScreen: false,
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {MutableRefObject, useEffect} from 'react';
|
||||
|
||||
export function useClickOutsideRef(ref: MutableRefObject<HTMLElement | null>, handler: (event: MouseEvent) => void): void {
|
||||
useEffect(() => {
|
||||
function onMouseDown(event: MouseEvent) {
|
||||
const target = event.target as any;
|
||||
if (ref.current && target instanceof Node && !ref.current.contains(target)) {
|
||||
handler(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Bind the event listener
|
||||
document.addEventListener('mousedown', onMouseDown);
|
||||
return () => {
|
||||
// Unbind the event listener on clean up
|
||||
document.removeEventListener('mousedown', onMouseDown);
|
||||
};
|
||||
}, [ref, handler]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {useEffect, useRef, useState, useMemo} from 'react';
|
||||
|
||||
export function useElementAvailable(
|
||||
elementIds: string[],
|
||||
intervalMS = 250,
|
||||
): boolean {
|
||||
const checkAvailableInterval = useRef<NodeJS.Timeout | null>(null);
|
||||
const [available, setAvailable] = useState(false);
|
||||
useEffect(() => {
|
||||
if (available) {
|
||||
if (checkAvailableInterval.current) {
|
||||
clearInterval(checkAvailableInterval.current);
|
||||
checkAvailableInterval.current = null;
|
||||
}
|
||||
return;
|
||||
} else if (checkAvailableInterval.current) {
|
||||
return;
|
||||
}
|
||||
checkAvailableInterval.current = setInterval(() => {
|
||||
if (elementIds.every((x) => document.getElementById(x))) {
|
||||
setAvailable(true);
|
||||
if (checkAvailableInterval.current) {
|
||||
clearInterval(checkAvailableInterval.current);
|
||||
checkAvailableInterval.current = null;
|
||||
}
|
||||
}
|
||||
}, intervalMS);
|
||||
}, []);
|
||||
|
||||
return useMemo(() => available, [available]);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {useLayoutEffect, useMemo, useState} from 'react';
|
||||
import throttle from 'lodash/throttle';
|
||||
|
||||
import {useElementAvailable} from './useElementAvailable';
|
||||
|
||||
export type Coords = {
|
||||
x?: string;
|
||||
y?: string;
|
||||
}
|
||||
|
||||
export type PunchOutCoordsHeightAndWidth = Coords & {
|
||||
width: string;
|
||||
height: string;
|
||||
}
|
||||
|
||||
type PunchOutOffset = {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export const useMeasurePunchouts = (elementIds: string[], additionalDeps: any[], offset?: PunchOutOffset): PunchOutCoordsHeightAndWidth | null => {
|
||||
const elementsAvailable = useElementAvailable(elementIds);
|
||||
const [size, setSize] = useState({x: window.innerWidth, y: window.innerHeight});
|
||||
const updateSize = throttle(() => {
|
||||
setSize({x: window.innerWidth, y: window.innerHeight});
|
||||
}, 100, {trailing: true});
|
||||
|
||||
useLayoutEffect(() => {
|
||||
window.addEventListener('resize', updateSize);
|
||||
return () =>
|
||||
window.removeEventListener('resize', updateSize);
|
||||
}, []);
|
||||
|
||||
const channelPunchout = useMemo(() => {
|
||||
let minX = Number.MAX_SAFE_INTEGER;
|
||||
let minY = Number.MAX_SAFE_INTEGER;
|
||||
let maxX = Number.MIN_SAFE_INTEGER;
|
||||
let maxY = Number.MIN_SAFE_INTEGER;
|
||||
for (let i = 0; i < elementIds.length; i++) {
|
||||
const rectangle = document.getElementById(elementIds[i])?.getBoundingClientRect();
|
||||
if (!rectangle) {
|
||||
return null;
|
||||
}
|
||||
if (rectangle.x < minX) {
|
||||
minX = rectangle.x;
|
||||
}
|
||||
if (rectangle.y < minY) {
|
||||
minY = rectangle.y;
|
||||
}
|
||||
if (rectangle.x + rectangle.width > maxX) {
|
||||
maxX = rectangle.x + rectangle.width;
|
||||
}
|
||||
if (rectangle.y + rectangle.height > maxY) {
|
||||
maxY = rectangle.y + rectangle.height;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
x: `${minX + (offset ? offset.x : 0)}px`,
|
||||
y: `${minY + (offset ? offset.y : 0)}px`,
|
||||
width: `${(maxX - minX) + (offset ? offset.width : 0)}px`,
|
||||
height: `${(maxY - minY) + (offset ? offset.height : 0)}px`,
|
||||
};
|
||||
}, [...elementIds, ...additionalDeps, size, elementsAvailable]);
|
||||
return channelPunchout;
|
||||
};
|
||||
18
webapp/platform/components/src/focus_trap/index.tsx
Обычный файл
18
webapp/platform/components/src/focus_trap/index.tsx
Обычный файл
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import MuiFocusTrap, {FocusTrapProps as MuiFocusTrapProps} from '@mui/base/FocusTrap';
|
||||
|
||||
export interface Props {
|
||||
active: MuiFocusTrapProps['open'];
|
||||
children: MuiFocusTrapProps['children'];
|
||||
}
|
||||
|
||||
export const FocusTrap = ({active, children}: Props) => {
|
||||
return (
|
||||
<MuiFocusTrap open={active}>
|
||||
{children}
|
||||
</MuiFocusTrap>
|
||||
);
|
||||
};
|
||||
226
webapp/platform/components/src/generic_modal/generic_modal.tsx
Обычный файл
226
webapp/platform/components/src/generic_modal/generic_modal.tsx
Обычный файл
@@ -0,0 +1,226 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import {Modal} from 'react-bootstrap';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {FocusTrap} from '../focus_trap';
|
||||
|
||||
export type Props = {
|
||||
className?: string;
|
||||
onExited: () => void;
|
||||
modalHeaderText?: React.ReactNode;
|
||||
show?: boolean;
|
||||
handleCancel?: () => void;
|
||||
handleConfirm?: () => void;
|
||||
handleEnterKeyPress?: () => void;
|
||||
handleKeydown?: (event?: React.KeyboardEvent<HTMLDivElement>) => void;
|
||||
confirmButtonText?: React.ReactNode;
|
||||
confirmButtonClassName?: string;
|
||||
cancelButtonText?: React.ReactNode;
|
||||
cancelButtonClassName?: string;
|
||||
isConfirmDisabled?: boolean;
|
||||
isDeleteModal?: boolean;
|
||||
id: string;
|
||||
autoCloseOnCancelButton?: boolean;
|
||||
autoCloseOnConfirmButton?: boolean;
|
||||
|
||||
/**
|
||||
* If false, bootrap's Modal will not enforce focus on the modal and will
|
||||
* transfer the mechanism to the FocusTrap component instead.
|
||||
*/
|
||||
enforceFocus?: boolean;
|
||||
container?: React.ReactNode | React.ReactNodeArray;
|
||||
ariaLabel?: string;
|
||||
errorText?: string;
|
||||
compassDesign?: boolean;
|
||||
backdrop?: boolean;
|
||||
backdropClassName?: string;
|
||||
headerButton?: React.ReactNode;
|
||||
tabIndex?: number;
|
||||
children: React.ReactNode;
|
||||
keyboardEscape?: boolean;
|
||||
};
|
||||
|
||||
type State = {
|
||||
show: boolean;
|
||||
isFocalTrapActive: boolean;
|
||||
}
|
||||
|
||||
export class GenericModal extends React.PureComponent<Props, State> {
|
||||
static defaultProps: Partial<Props> = {
|
||||
show: true,
|
||||
id: 'genericModal',
|
||||
autoCloseOnCancelButton: true,
|
||||
autoCloseOnConfirmButton: true,
|
||||
enforceFocus: true,
|
||||
keyboardEscape: true,
|
||||
};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
show: props.show!,
|
||||
isFocalTrapActive: false,
|
||||
};
|
||||
}
|
||||
|
||||
onHide = () => {
|
||||
this.setState({show: false});
|
||||
}
|
||||
|
||||
handleCancel = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
event.preventDefault();
|
||||
if (this.props.autoCloseOnCancelButton) {
|
||||
this.onHide();
|
||||
}
|
||||
if (this.props.handleCancel) {
|
||||
this.props.handleCancel();
|
||||
}
|
||||
}
|
||||
|
||||
handleConfirm = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
event.preventDefault();
|
||||
if (this.props.autoCloseOnConfirmButton) {
|
||||
this.onHide();
|
||||
}
|
||||
if (this.props.handleConfirm) {
|
||||
this.props.handleConfirm();
|
||||
}
|
||||
}
|
||||
|
||||
private onEnterKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.key === 'Enter') {
|
||||
if (this.props.autoCloseOnConfirmButton) {
|
||||
this.onHide();
|
||||
}
|
||||
if (this.props.handleEnterKeyPress) {
|
||||
this.props.handleEnterKeyPress();
|
||||
}
|
||||
}
|
||||
this.props.handleKeydown?.(event);
|
||||
}
|
||||
|
||||
private handleShow = () => {
|
||||
if (this.props.enforceFocus === false) {
|
||||
this.setState({isFocalTrapActive: true});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let confirmButton;
|
||||
if (this.props.handleConfirm) {
|
||||
const isConfirmOrDeleteClassName = this.props.isDeleteModal ? 'delete' : 'confirm';
|
||||
let confirmButtonText: React.ReactNode = (
|
||||
<FormattedMessage
|
||||
id='generic_modal.confirm'
|
||||
defaultMessage='Confirm'
|
||||
/>
|
||||
);
|
||||
if (this.props.confirmButtonText) {
|
||||
confirmButtonText = this.props.confirmButtonText;
|
||||
}
|
||||
|
||||
confirmButton = (
|
||||
<button
|
||||
type='submit'
|
||||
className={classNames('GenericModal__button', isConfirmOrDeleteClassName, this.props.confirmButtonClassName, {
|
||||
disabled: this.props.isConfirmDisabled,
|
||||
})}
|
||||
onClick={this.handleConfirm}
|
||||
disabled={this.props.isConfirmDisabled}
|
||||
>
|
||||
{confirmButtonText}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
let cancelButton;
|
||||
if (this.props.handleCancel) {
|
||||
let cancelButtonText: React.ReactNode = (
|
||||
<FormattedMessage
|
||||
id='generic_modal.cancel'
|
||||
defaultMessage='Cancel'
|
||||
/>
|
||||
);
|
||||
if (this.props.cancelButtonText) {
|
||||
cancelButtonText = this.props.cancelButtonText;
|
||||
}
|
||||
|
||||
cancelButton = (
|
||||
<button
|
||||
type='button'
|
||||
className={classNames('GenericModal__button cancel', this.props.cancelButtonClassName)}
|
||||
onClick={this.handleCancel}
|
||||
>
|
||||
{cancelButtonText}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
const headerText = this.props.modalHeaderText && (
|
||||
<div className='GenericModal__header'>
|
||||
<h1 id='genericModalLabel'>
|
||||
{this.props.modalHeaderText}
|
||||
</h1>
|
||||
{this.props.headerButton}
|
||||
</div>
|
||||
);
|
||||
|
||||
const isFocusTrapActive = this.props.enforceFocus === false ? this.state.isFocalTrapActive : false;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
id={this.props.id}
|
||||
role='dialog'
|
||||
aria-label={this.props.ariaLabel}
|
||||
aria-labelledby={this.props.ariaLabel ? undefined : 'genericModalLabel'}
|
||||
dialogClassName={classNames('a11y__modal GenericModal', {GenericModal__compassDesign: this.props.compassDesign}, this.props.className)}
|
||||
show={this.state.show}
|
||||
onShow={this.handleShow}
|
||||
restoreFocus={true}
|
||||
enforceFocus={this.props.enforceFocus}
|
||||
onHide={this.onHide}
|
||||
onExited={this.props.onExited}
|
||||
backdrop={this.props.backdrop}
|
||||
backdropClassName={this.props.backdropClassName}
|
||||
container={this.props.container}
|
||||
keyboard={this.props.keyboardEscape}
|
||||
>
|
||||
<FocusTrap active={isFocusTrapActive}>
|
||||
<div
|
||||
onKeyDown={this.onEnterKeyDown}
|
||||
tabIndex={this.props.tabIndex || 0}
|
||||
className='GenericModal__wrapper-enter-key-press-catcher'
|
||||
>
|
||||
<Modal.Header closeButton={true}>
|
||||
{this.props.compassDesign && headerText}
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
{this.props.compassDesign ? (
|
||||
this.props.errorText && (
|
||||
<div className='genericModalError'>
|
||||
<i className='icon icon-alert-outline'/>
|
||||
<span>{this.props.errorText}</span>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
headerText
|
||||
)}
|
||||
<div className='GenericModal__body'>
|
||||
{this.props.children}
|
||||
</div>
|
||||
</Modal.Body>
|
||||
{(cancelButton || confirmButton) && <Modal.Footer>
|
||||
{cancelButton}
|
||||
{confirmButton}
|
||||
</Modal.Footer>}
|
||||
</div>
|
||||
</FocusTrap>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
18
webapp/platform/components/src/index.tsx
Обычный файл
18
webapp/platform/components/src/index.tsx
Обычный файл
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// type
|
||||
export type {Props as GenericModalProps} from './generic_modal/generic_modal';
|
||||
export type {CircleSkeletonLoaderProps, RectangleSkeletonLoaderProps} from './skeleton_loader';
|
||||
export type {Props as FocusTrapProps} from './focus_trap';
|
||||
|
||||
// components
|
||||
export {GenericModal} from './generic_modal/generic_modal';
|
||||
export {CircleSkeletonLoader, RectangleSkeletonLoader} from './skeleton_loader';
|
||||
export * from './tour_tip';
|
||||
export * from './pulsating_dot';
|
||||
export {FocusTrap} from './focus_trap';
|
||||
|
||||
// hooks
|
||||
export * from './common/hooks/useMeasurePunchouts';
|
||||
export {useElementAvailable} from './common/hooks/useElementAvailable';
|
||||
43
webapp/platform/components/src/pulsating_dot/index.tsx
Обычный файл
43
webapp/platform/components/src/pulsating_dot/index.tsx
Обычный файл
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {Coords} from '../common/hooks/useMeasurePunchouts';
|
||||
|
||||
import './pulsating_dot.scss';
|
||||
|
||||
type Props = {
|
||||
targetRef?: React.RefObject<HTMLImageElement>;
|
||||
className?: string;
|
||||
onClick?: (e: React.MouseEvent) => void;
|
||||
coords?: Coords;
|
||||
}
|
||||
|
||||
export class PulsatingDot extends React.PureComponent<Props> {
|
||||
public render() {
|
||||
let customStyles = {};
|
||||
if (this.props?.coords) {
|
||||
customStyles = {
|
||||
transform: `translate(${this.props.coords?.x}px, ${this.props.coords?.y}px)`,
|
||||
};
|
||||
}
|
||||
let effectiveClassName = 'pulsating_dot';
|
||||
if (this.props.onClick) {
|
||||
effectiveClassName += ' pulsating_dot-clickable';
|
||||
}
|
||||
if (this.props.className) {
|
||||
effectiveClassName = effectiveClassName + ' ' + this.props.className;
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className={effectiveClassName}
|
||||
onClick={this.props.onClick}
|
||||
ref={this.props.targetRef}
|
||||
style={{...customStyles}}
|
||||
data-testid={'pulsating_dot'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
37
webapp/platform/components/src/pulsating_dot/pulsating_dot.scss
Обычный файл
37
webapp/platform/components/src/pulsating_dot/pulsating_dot.scss
Обычный файл
@@ -0,0 +1,37 @@
|
||||
.pulsating_dot {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: auto;
|
||||
|
||||
&-clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&,
|
||||
&::before,
|
||||
&::after {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: var(--online-indicator);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: block;
|
||||
content: "";
|
||||
}
|
||||
|
||||
&::after {
|
||||
animation: pulse1 2s ease 0s infinite;
|
||||
}
|
||||
|
||||
&::before {
|
||||
animation: pulse2 2s ease 0s infinite;
|
||||
}
|
||||
}
|
||||
81
webapp/platform/components/src/skeleton_loader/index.tsx
Обычный файл
81
webapp/platform/components/src/skeleton_loader/index.tsx
Обычный файл
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import styled, {keyframes} from 'styled-components';
|
||||
|
||||
const skeletonFade = keyframes`
|
||||
0% {
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.08);
|
||||
}
|
||||
50% {
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.16);
|
||||
}
|
||||
100% {
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.08);
|
||||
}
|
||||
`;
|
||||
|
||||
const BaseLoader = styled.div`
|
||||
animation-duration: 1500ms;
|
||||
animation-iteration-count: infinite;
|
||||
animation-name: ${skeletonFade};
|
||||
animation-timing-function: ease-in-out;
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.08);
|
||||
`;
|
||||
|
||||
export interface CircleSkeletonLoaderProps {
|
||||
size: string | number;
|
||||
}
|
||||
|
||||
/**
|
||||
* CircleSkeletonLoader is a component that renders a filled circle with a loading animation.
|
||||
* It is used to indicate that the content is loading.
|
||||
* @param props.size - The size of the circle. When in number, it is treated as pixels.
|
||||
* @example
|
||||
* <CircleSkeletonLoader size={20}/>
|
||||
* <CircleSkeletonLoader size="50%"/>
|
||||
*/
|
||||
export const CircleSkeletonLoader = styled(BaseLoader)<CircleSkeletonLoaderProps>`
|
||||
display: block;
|
||||
border-radius: 50%;
|
||||
height: ${(props) => getCorrectSizeDimension(props.size)};
|
||||
width: ${(props) => getCorrectSizeDimension(props.size)};
|
||||
`;
|
||||
|
||||
export interface RectangleSkeletonLoaderProps {
|
||||
height: string | number;
|
||||
width?: string | number;
|
||||
borderRadius?: number;
|
||||
margin?: string;
|
||||
flex?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* RectangleSkeletonLoader is a component that renders a filled rectangle with a loading animation.
|
||||
* It is used to indicate that the content is loading.
|
||||
* @param props.height - The height of the rectangle eg. 20, "20em", "20%". When in number, it is treated as pixels.
|
||||
* @param props.width - The width of the rectangle eg. 30, '100%'. When in number, it is treated as pixels.
|
||||
* @param props.borderRadius - The border radius of the rectangle eg. 4
|
||||
* @param props.margin - The margin of the rectangle eg. '0 10px', '10px 0 0 10px'
|
||||
* @param props.flex - The flex short hand of flex grow, shrink, basis of the rectangle, under flex parent css eg. '1 1 auto'
|
||||
* @default
|
||||
* width: 100% , borderRadius: 8px
|
||||
* @example
|
||||
* <RectangleSkeletonLoader height='100px' />
|
||||
* <RectangleSkeletonLoader height={40} width={100} borderRadius={4} margin='0 10px 0 0' flex='1' />
|
||||
*/
|
||||
export const RectangleSkeletonLoader = styled(BaseLoader)<RectangleSkeletonLoaderProps>`
|
||||
height: ${(props) => getCorrectSizeDimension(props.height)};
|
||||
width: ${(props) => getCorrectSizeDimension(props.width, '100%')};
|
||||
border-radius: ${(props) => props?.borderRadius ?? 8}px;
|
||||
margin: ${(props) => props?.margin ?? null};
|
||||
flex: ${(props) => props?.flex ?? null};
|
||||
`;
|
||||
|
||||
function getCorrectSizeDimension(size: number | string | undefined, fallback: string | null = null) {
|
||||
if (size) {
|
||||
return (typeof size === 'string') ? size : `${size}px`;
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
5
webapp/platform/components/src/tour_tip/index.ts
Обычный файл
5
webapp/platform/components/src/tour_tip/index.ts
Обычный файл
@@ -0,0 +1,5 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export * from './tour_tip';
|
||||
export * from './tour_tip_backdrop';
|
||||
456
webapp/platform/components/src/tour_tip/tour_tip.scss
Обычный файл
456
webapp/platform/components/src/tour_tip/tour_tip.scss
Обычный файл
@@ -0,0 +1,456 @@
|
||||
.tour-tip {
|
||||
display: flex;
|
||||
|
||||
&__box {
|
||||
&.tippy-box {
|
||||
padding: 18px 24px 24px;
|
||||
border: 1px solid rgba(var(--center-channel-color-rgb), 0.16);
|
||||
background: var(--center-channel-bg);
|
||||
border-radius: 4px;
|
||||
color: var(--center-channel-color-rgb);
|
||||
filter: drop-shadow(0 12px 32px rgba(0, 0, 0, 0.12));
|
||||
|
||||
.tippy-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.tippy-arrow {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-color: rgba(var(--center-channel-color-rgb), 0.16);
|
||||
color: var(--center-channel-bg);
|
||||
}
|
||||
|
||||
.tippy-arrow::before {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-color: rgba(var(--center-channel-color-rgb), 0.16);
|
||||
background: var(--center-channel-bg);
|
||||
color: var(--center-channel-bg);
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
// fix for https://mattermost.atlassian.net/browse/MM-41711. This covers the current placements we use for the channels and other tools tour
|
||||
&[data-placement^=right] > .tippy-arrow {
|
||||
transform: translate3d(0, 14px, 0) !important;
|
||||
}
|
||||
|
||||
&[data-placement^=top] > .tippy-arrow {
|
||||
transform: translate3d(14px, 0, 0) !important;
|
||||
}
|
||||
|
||||
&[data-placement^=bottom] > .tippy-arrow {
|
||||
transform: translate3d(14px, 0, 0) !important;
|
||||
}
|
||||
|
||||
&[data-placement=bottom-end] > .tippy-arrow {
|
||||
transform: translate3d(317px, 0, 0) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__pulsating-dot-ctr {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__pulsating-dot-ctr[data-pulsating-dot-placement=top] {
|
||||
top: 0;
|
||||
left: calc(50% - 6px);
|
||||
transform: translate(0, 6px);
|
||||
}
|
||||
|
||||
&__pulsating-dot-ctr[data-pulsating-dot-placement=top-start] {
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: translate(6px, 6px);
|
||||
}
|
||||
|
||||
&__pulsating-dot-ctr[data-pulsating-dot-placement=top-end] {
|
||||
top: 0;
|
||||
right: 0;
|
||||
transform: translate(-6px, 6px);
|
||||
}
|
||||
|
||||
&__pulsating-dot-ctr[data-pulsating-dot-placement=bottom] {
|
||||
bottom: 0;
|
||||
left: calc(50% - 6px);
|
||||
transform: translate(0, -6px);
|
||||
}
|
||||
|
||||
&__pulsating-dot-ctr[data-pulsating-dot-placement=bottom-start] {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
transform: translate(6px, -6px);
|
||||
}
|
||||
|
||||
&__pulsating-dot-ctr[data-pulsating-dot-placement=bottom-end] {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
transform: translate(-6px, -6px);
|
||||
}
|
||||
|
||||
&__pulsating-dot-ctr[data-pulsating-dot-placement=left] {
|
||||
top: calc(50% - 6px);
|
||||
left: 0;
|
||||
transform: translate(6px, 0);
|
||||
}
|
||||
|
||||
&__pulsating-dot-ctr[data-pulsating-dot-placement=left-start] {
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: translate(6px, 6px);
|
||||
}
|
||||
|
||||
&__pulsating-dot-ctr[data-pulsating-dot-placement=left-end] {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
transform: translate(6px, -6px);
|
||||
}
|
||||
|
||||
&__pulsating-dot-ctr[data-pulsating-dot-placement=right] {
|
||||
top: calc(50% - 6px);
|
||||
right: 0;
|
||||
transform: translate(-6px, 0);
|
||||
}
|
||||
|
||||
&__pulsating-dot-ctr[data-pulsating-dot-placement=right-start] {
|
||||
top: 0;
|
||||
right: 0;
|
||||
transform: translate(-6px, 6px);
|
||||
}
|
||||
|
||||
&__pulsating-dot-ctr[data-pulsating-dot-placement=right-end] {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
transform: translate(-6px, -6px);
|
||||
}
|
||||
|
||||
&__overlay {
|
||||
position: fixed;
|
||||
z-index: 999;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
&__title {
|
||||
flex: none;
|
||||
flex-grow: 1;
|
||||
order: 0;
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: 1.4rem;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
&__close {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
width: 3.2rem;
|
||||
height: 3.2rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: unset;
|
||||
margin-right: -8px;
|
||||
margin-left: 1.2rem;
|
||||
background: transparent;
|
||||
border-radius: 4px;
|
||||
color: rgba(var(--center-channel-color-rgb), 0.56);
|
||||
font-weight: 600;
|
||||
|
||||
&:hover {
|
||||
background: rgba(var(--center-channel-color-rgb), 0.08);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: rgba(var(--center-channel-color-rgb), 0.08);
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 1.8rem;
|
||||
line-height: 1.8rem;
|
||||
|
||||
::before {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 0.6rem;
|
||||
|
||||
p,
|
||||
div {
|
||||
margin: 0 0 0.8rem;
|
||||
font-size: 1.4rem;
|
||||
line-height: 2rem;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__body:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&__image {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 2.4rem;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 136px;
|
||||
border-radius: 4px;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
&__btn-ctr {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__btn {
|
||||
display: flex;
|
||||
height: 3.2rem;
|
||||
align-items: center;
|
||||
padding: 10px 16px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
&__confirm-btn {
|
||||
background: var(--button-bg);
|
||||
color: var(--button-color);
|
||||
|
||||
&:hover:not(.disabled) {
|
||||
background:
|
||||
linear-gradient(
|
||||
0deg,
|
||||
rgba(var(--center-channel-color-rgb), 0.16),
|
||||
rgba(var(--center-channel-color-rgb), 0.16)
|
||||
),
|
||||
var(--button-bg);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background:
|
||||
linear-gradient(
|
||||
0deg,
|
||||
rgba(var(--center-channel-color-rgb), 0.32),
|
||||
rgba(var(--center-channel-color-rgb), 0.32)
|
||||
),
|
||||
var(--button-bg);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: inset 0 0 0 2px var(--sidebar-text-active-border);
|
||||
}
|
||||
|
||||
.icon-chevron-right::before {
|
||||
margin-right: -7px;
|
||||
}
|
||||
}
|
||||
|
||||
&__cancel-btn {
|
||||
margin-right: 4px;
|
||||
background: rgba(var(--button-bg-rgb), 0.08);
|
||||
border-radius: 4px;
|
||||
color: var(--button-bg);
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
background: rgba(var(--button-bg-rgb), 0.04);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: rgba(var(--button-bg-rgb), 0.08);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: inset 0 0 0 2px var(--sidebar-text-active-border);
|
||||
}
|
||||
|
||||
.icon-chevron-left::before {
|
||||
margin-left: -7px;
|
||||
}
|
||||
}
|
||||
|
||||
&__dot-ctr {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
&__dot-ring {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 4px;
|
||||
background: transparent;
|
||||
border-radius: 50%;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__dot-ring-active {
|
||||
background: rgba(var(--button-bg-rgb), 0.16);
|
||||
}
|
||||
|
||||
&__dot {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: rgba(var(--button-bg-rgb), 0.32);
|
||||
border-radius: 6px;
|
||||
|
||||
&.active {
|
||||
background: rgba(var(--button-bg-rgb), 1);
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 2.4rem;
|
||||
|
||||
&-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
}
|
||||
}
|
||||
|
||||
&__opt {
|
||||
align-self: flex-end;
|
||||
margin-top: 1.2rem;
|
||||
font-size: 12px;
|
||||
|
||||
span {
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
&__backdrop {
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
|
||||
&--transparent {
|
||||
background: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adding important as temporary fix, will be removing tippy very soon (WIP)
|
||||
.tippy-box[data-placement^=right] > .tippy-arrow::before {
|
||||
top: -1px !important;
|
||||
border-width: 1px 0 0 1px !important;
|
||||
transform: rotate(-45deg) !important;
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^=left] > .tippy-arrow::before {
|
||||
top: -1px !important;
|
||||
border-width: 1px 1px 0 0 !important;
|
||||
transform: rotate(45deg) !important;
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^=bottom] > .tippy-arrow::before {
|
||||
left: 1px !important;
|
||||
border-width: 1px 0 0 1px !important;
|
||||
transform: rotate(45deg) !important;
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^=top] > .tippy-arrow::before {
|
||||
left: 1px !important;
|
||||
border-width: 0 0 1px 1px !important;
|
||||
transform: rotate(-45deg) !important;
|
||||
}
|
||||
|
||||
// this style is defined outside of the block scope because is intended to affect the tippy element
|
||||
.tippy-blue-style {
|
||||
background: var(--button-bg) !important;
|
||||
color: var(--sidebar-text) !important;
|
||||
|
||||
.tippy-arrow {
|
||||
border-color: var(--button-bg) !important;
|
||||
color: var(--button-bg) !important;
|
||||
|
||||
&::before {
|
||||
border-width: 0 !important;
|
||||
border-color: var(--button-bg) !important;
|
||||
border-left-color: initial;
|
||||
background-color: var(--button-bg) !important;
|
||||
transform-origin: unset !important;
|
||||
}
|
||||
}
|
||||
|
||||
.tour-tip__header {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.icon-close {
|
||||
color: var(--sidebar-text) !important;
|
||||
}
|
||||
|
||||
// style buttons while in the blue style
|
||||
.tour-tip {
|
||||
&__btn {
|
||||
background: var(--button-color);
|
||||
color: var(--button-bg);
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
background: var(--button-color);
|
||||
color: var(--button-bg);
|
||||
}
|
||||
}
|
||||
|
||||
&__dot-ring {
|
||||
.tour-tip__dot {
|
||||
background: var(--offline-indicator);
|
||||
}
|
||||
}
|
||||
|
||||
&__dot-ring-active {
|
||||
.active {
|
||||
background: var(--button-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
247
webapp/platform/components/src/tour_tip/tour_tip.tsx
Обычный файл
247
webapp/platform/components/src/tour_tip/tour_tip.tsx
Обычный файл
@@ -0,0 +1,247 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useRef} from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import Tippy from '@tippyjs/react';
|
||||
import {Placement} from 'tippy.js';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {PunchOutCoordsHeightAndWidth} from '../common/hooks/useMeasurePunchouts';
|
||||
|
||||
import 'tippy.js/dist/tippy.css';
|
||||
import 'tippy.js/themes/light-border.css';
|
||||
import 'tippy.js/animations/scale-subtle.css';
|
||||
import 'tippy.js/animations/perspective-subtle.css';
|
||||
import {PulsatingDot} from '../pulsating_dot';
|
||||
|
||||
import {TourTipBackdrop} from './tour_tip_backdrop';
|
||||
import './tour_tip.scss';
|
||||
|
||||
export type TourTipEventSource = 'next' | 'prev' | 'dismiss' | 'jump' | 'skipped' | 'open' | 'punchOut'
|
||||
|
||||
type Props = {
|
||||
show: boolean;
|
||||
screen: JSX.Element;
|
||||
title: JSX.Element;
|
||||
step: number;
|
||||
|
||||
tourSteps?: Record<string, number>;
|
||||
nextBtn?: JSX.Element;
|
||||
prevBtn?: JSX.Element;
|
||||
imageURL?: string;
|
||||
singleTip?: boolean;
|
||||
showOptOut?: boolean;
|
||||
placement?: Placement;
|
||||
pulsatingDotPlacement?: Omit<Placement, 'auto'| 'auto-end'>;
|
||||
pulsatingDotTranslate?: {x: number; y: number};
|
||||
offset?: [number, number];
|
||||
width?: string | number;
|
||||
zIndex?: number;
|
||||
className?: string;
|
||||
hideBackdrop?: boolean;
|
||||
tippyBlueStyle?: boolean;
|
||||
|
||||
// if you don't want punchOut just assign null, keep null as hook may return null first than actual value
|
||||
overlayPunchOut: PunchOutCoordsHeightAndWidth | null;
|
||||
|
||||
// if we want to interact with element visible via punchOut
|
||||
interactivePunchOut?: boolean;
|
||||
|
||||
handleOpen?: (e: React.MouseEvent) => void;
|
||||
handleNext?: (e: React.MouseEvent) => void;
|
||||
handlePrevious?: (e: React.MouseEvent) => void;
|
||||
handleJump?: (e: React.MouseEvent, jumpToStep: number) => void;
|
||||
handleSkip?: (e: React.MouseEvent) => void;
|
||||
handleDismiss?: (e: React.MouseEvent) => void;
|
||||
handlePunchOut?: (e: React.MouseEvent) => void;
|
||||
}
|
||||
|
||||
export const TourTip = ({
|
||||
title,
|
||||
screen,
|
||||
imageURL,
|
||||
overlayPunchOut,
|
||||
singleTip,
|
||||
step,
|
||||
show,
|
||||
interactivePunchOut,
|
||||
tourSteps,
|
||||
handleOpen,
|
||||
handleDismiss,
|
||||
handleNext,
|
||||
handlePrevious,
|
||||
handleSkip,
|
||||
handleJump,
|
||||
handlePunchOut,
|
||||
pulsatingDotTranslate,
|
||||
pulsatingDotPlacement,
|
||||
nextBtn,
|
||||
prevBtn,
|
||||
className,
|
||||
offset = [-18, 4],
|
||||
placement = 'right-start',
|
||||
showOptOut = true,
|
||||
width = 352,
|
||||
zIndex = 999,
|
||||
hideBackdrop = false,
|
||||
tippyBlueStyle = false,
|
||||
}: Props) => {
|
||||
const FIRST_STEP_INDEX = 0;
|
||||
const triggerRef = useRef(null);
|
||||
const onJump = (event: React.MouseEvent, jumpToStep: number) => {
|
||||
if (handleJump) {
|
||||
handleJump(event, jumpToStep);
|
||||
}
|
||||
};
|
||||
|
||||
// This needs to be changed if root-portal node isn't available to maybe body
|
||||
const rootPortal = document.getElementById('root-portal');
|
||||
|
||||
const dots = [];
|
||||
if (!singleTip && tourSteps) {
|
||||
for (let dot = FIRST_STEP_INDEX; dot < (Object.values(tourSteps).length - 1); dot++) {
|
||||
let className = 'tour-tip__dot';
|
||||
let circularRing = 'tour-tip__dot-ring';
|
||||
|
||||
if (dot === step) {
|
||||
className += ' active';
|
||||
circularRing += ' tour-tip__dot-ring-active';
|
||||
}
|
||||
dots.push(
|
||||
<div className={circularRing}>
|
||||
<a
|
||||
href='#'
|
||||
key={'dotactive' + dot}
|
||||
className={className}
|
||||
data-screen={dot}
|
||||
onClick={(e) => onJump(e, dot)}
|
||||
/>
|
||||
</div>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const content = (
|
||||
<>
|
||||
<div
|
||||
className='tour-tip__header'
|
||||
data-testid={'current_tutorial_tip'}
|
||||
>
|
||||
<h4 className='tour-tip__header__title'>
|
||||
{title}
|
||||
</h4>
|
||||
<button
|
||||
className='tour-tip__header__close'
|
||||
onClick={handleDismiss}
|
||||
data-testid={'close_tutorial_tip'}
|
||||
>
|
||||
<i className='icon icon-close'/>
|
||||
</button>
|
||||
</div>
|
||||
<div className='tour-tip__body'>
|
||||
{screen}
|
||||
</div>
|
||||
{imageURL && (
|
||||
<div className='tour-tip__image'>
|
||||
<img
|
||||
src={imageURL}
|
||||
alt={'tutorial tour tip product image'}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{(nextBtn || prevBtn || showOptOut) && (<div className='tour-tip__footer'>
|
||||
<div className='tour-tip__footer-buttons'>
|
||||
<div className='tour-tip__dot-ctr'>{dots}</div>
|
||||
<div className={'tour-tip__btn-ctr'}>
|
||||
{step !== 0 && prevBtn && (
|
||||
<button
|
||||
id='tipPreviousButton'
|
||||
className='tour-tip__btn tour-tip__cancel-btn'
|
||||
onClick={handlePrevious}
|
||||
>
|
||||
{prevBtn}
|
||||
</button>
|
||||
)}
|
||||
{nextBtn && (
|
||||
<button
|
||||
id='tipNextButton'
|
||||
className='tour-tip__btn tour-tip__confirm-btn'
|
||||
onClick={handleNext}
|
||||
>
|
||||
{nextBtn}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{showOptOut && (
|
||||
<div className='tour-tip__opt'>
|
||||
<FormattedMessage
|
||||
id='tutorial_tip.seen'
|
||||
defaultMessage='Seen this before? '
|
||||
/>
|
||||
<a
|
||||
href='#'
|
||||
onClick={handleSkip}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='tutorial_tip.out'
|
||||
defaultMessage='Opt out of these tips.'
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
id='tipButton'
|
||||
ref={triggerRef}
|
||||
onClick={handleOpen}
|
||||
className='tour-tip__pulsating-dot-ctr'
|
||||
data-pulsating-dot-placement={pulsatingDotPlacement || 'right'}
|
||||
style={{
|
||||
transform: `translate(${pulsatingDotTranslate?.x}px, ${pulsatingDotTranslate?.y}px)`,
|
||||
}}
|
||||
>
|
||||
<PulsatingDot/>
|
||||
</div>
|
||||
<TourTipBackdrop
|
||||
show={show}
|
||||
onDismiss={handleDismiss}
|
||||
onPunchOut={handlePunchOut}
|
||||
interactivePunchOut={interactivePunchOut}
|
||||
overlayPunchOut={overlayPunchOut}
|
||||
appendTo={rootPortal!}
|
||||
transparent={hideBackdrop}
|
||||
/>
|
||||
{show && (
|
||||
<Tippy
|
||||
showOnCreate={show}
|
||||
content={content}
|
||||
animation='scale-subtle'
|
||||
trigger='click'
|
||||
duration={[250, 150]}
|
||||
maxWidth={width}
|
||||
aria={{content: 'labelledby'}}
|
||||
allowHTML={true}
|
||||
zIndex={zIndex}
|
||||
reference={triggerRef}
|
||||
interactive={true}
|
||||
appendTo={rootPortal!}
|
||||
offset={offset}
|
||||
className={classNames(
|
||||
'tour-tip__box',
|
||||
className,
|
||||
{'tippy-blue-style': tippyBlueStyle},
|
||||
)}
|
||||
placement={placement}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
84
webapp/platform/components/src/tour_tip/tour_tip_backdrop.tsx
Обычный файл
84
webapp/platform/components/src/tour_tip/tour_tip_backdrop.tsx
Обычный файл
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import {PunchOutCoordsHeightAndWidth} from '../common/hooks/useMeasurePunchouts';
|
||||
|
||||
type Props = {
|
||||
overlayPunchOut: PunchOutCoordsHeightAndWidth | null;
|
||||
show: boolean;
|
||||
interactivePunchOut?: boolean;
|
||||
onDismiss?: (e: React.MouseEvent) => void;
|
||||
onPunchOut?: (e: React.MouseEvent) => void;
|
||||
appendTo: HTMLElement;
|
||||
transparent?: boolean;
|
||||
}
|
||||
|
||||
const TourTipRootPortal = ({children, show, element}: {children: React.ReactNode ; show: boolean; element: Element}) =>
|
||||
(show ? ReactDOM.createPortal(
|
||||
children,
|
||||
element,
|
||||
) : null);
|
||||
|
||||
export const TourTipBackdrop = ({
|
||||
show,
|
||||
overlayPunchOut,
|
||||
interactivePunchOut,
|
||||
onDismiss,
|
||||
onPunchOut,
|
||||
appendTo,
|
||||
transparent,
|
||||
}: Props) => {
|
||||
const vertices = [];
|
||||
if (overlayPunchOut) {
|
||||
const {x, y, width, height} = overlayPunchOut;
|
||||
|
||||
// draw to top left of punch out
|
||||
vertices.push('0% 0%');
|
||||
vertices.push('0% 100%');
|
||||
vertices.push('100% 100%');
|
||||
vertices.push('100% 0%');
|
||||
vertices.push(`${x} 0%`);
|
||||
vertices.push(`${x} ${y}`);
|
||||
|
||||
// draw punch out
|
||||
vertices.push(`calc(${x} + ${width}) ${y}`);
|
||||
vertices.push(`calc(${x} + ${width}) calc(${y} + ${height})`);
|
||||
vertices.push(`${x} calc(${y} + ${height})`);
|
||||
vertices.push(`${x} ${y}`);
|
||||
|
||||
// close off punch out
|
||||
vertices.push(`${x} 0%`);
|
||||
vertices.push('0% 0%');
|
||||
}
|
||||
const backdrop = (
|
||||
<div
|
||||
onClick={onDismiss}
|
||||
className={`tour-tip__backdrop ${transparent ? 'tour-tip__backdrop--transparent' : ''}`}
|
||||
style={{
|
||||
clipPath: vertices.length ? `polygon(${vertices.join(', ')})` : undefined,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
const overlay = interactivePunchOut ? backdrop : (
|
||||
<>
|
||||
<div
|
||||
className={'tour-tip__overlay'}
|
||||
onClick={onPunchOut || onDismiss}
|
||||
/>
|
||||
{backdrop}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<TourTipRootPortal
|
||||
show={show}
|
||||
element={appendTo}
|
||||
>
|
||||
{overlay}
|
||||
</TourTipRootPortal>
|
||||
);
|
||||
};
|
||||
|
||||
21
webapp/platform/components/tsconfig.json
Обычный файл
21
webapp/platform/components/tsconfig.json
Обычный файл
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"declaration": true,
|
||||
"outDir": "dist",
|
||||
"paths": {
|
||||
"mattermost-redux/*": ["./node_modules/mattermost-redux/src/*"],
|
||||
"@mattermost/types/*": ["./node_modules/@mattermost/types/src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
51
webapp/platform/types/README.md
Обычный файл
51
webapp/platform/types/README.md
Обычный файл
@@ -0,0 +1,51 @@
|
||||
# Mattermost Types
|
||||
|
||||
This package contains shared type definitions used by [the Mattermost web app](https://github.com/mattermost/mattermost-webapp) and related projects.
|
||||
|
||||
## Usage
|
||||
|
||||
For technologies that support [subpath exports](https://nodejs.org/api/packages.html#subpath-exports), such as Node.js, Webpack, and Babel, you can import these types directly from individual files.
|
||||
|
||||
```javascript
|
||||
import {UserProfile} from '@mattermost/types/users';
|
||||
```
|
||||
|
||||
For technologies that don't support that yet, you can add an alias in its package resolution settings to support that.
|
||||
|
||||
### TypeScript
|
||||
|
||||
In the `tsconfig.json`, you can use `compilerOptions.paths` to add that alias. This also requires a `compilerOptions.baseUrl` if you haven't set that already.
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@mattermost/types/*": ["node_modules/@mattermost/types/lib/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Jest
|
||||
|
||||
In your Jest config, you can use the `moduleNameMapper` field to add that alias.
|
||||
|
||||
```json
|
||||
{
|
||||
"moduleNameMapper": {
|
||||
"^@mattermost/types/(.*)$": "<rootDir>/node_modules/@mattermost/types/lib/$1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Compilation and Packaging
|
||||
|
||||
As a member of Mattermost with write access to our NPM organization, you can build and publish this package by running the following commands:
|
||||
|
||||
```bash
|
||||
npm run build --workspace=packages/types
|
||||
npm publish --workspace=packages/types
|
||||
```
|
||||
|
||||
Make sure to increment the version number in `package.json` first! You can add `-0`, `-1`, etc for pre-release versions.
|
||||
35
webapp/platform/types/package.json
Обычный файл
35
webapp/platform/types/package.json
Обычный файл
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "@mattermost/types",
|
||||
"version": "7.9.0",
|
||||
"description": "Shared type definitions used by the Mattermost web app",
|
||||
"keywords": [
|
||||
"mattermost"
|
||||
],
|
||||
"homepage": "https://github.com/mattermost/mattermost-webapp/tree/master/packages/types#readme",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"exports": {
|
||||
"./*": "./lib/*.js"
|
||||
},
|
||||
"types": "./lib/*.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "github:mattermost/mattermost-webapp",
|
||||
"directory": "packages/types"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^4.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc --build --verbose",
|
||||
"run": "tsc --watch --preserveWatchOutput",
|
||||
"clean": "rm -rf tsconfig.tsbuildinfo ./lib"
|
||||
}
|
||||
}
|
||||
93
webapp/platform/types/src/admin.ts
Обычный файл
93
webapp/platform/types/src/admin.ts
Обычный файл
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Audit} from './audits';
|
||||
import {Compliance} from './compliance';
|
||||
import {AdminConfig, ClientLicense, EnvironmentConfig} from './config';
|
||||
import {DataRetentionCustomPolicies} from './data_retention';
|
||||
import {MixedUnlinkedGroupRedux} from './groups';
|
||||
import {PluginRedux, PluginStatusRedux} from './plugins';
|
||||
import {SamlCertificateStatus, SamlMetadataResponse} from './saml';
|
||||
import {Team} from './teams';
|
||||
import {UserAccessToken, UserProfile} from './users';
|
||||
import {RelationOneToOne} from './utilities';
|
||||
|
||||
export enum LogLevelEnum {
|
||||
SILLY = 'silly',
|
||||
DEBUG = 'debug',
|
||||
INFO = 'info',
|
||||
WARN = 'warn',
|
||||
ERROR = 'error',
|
||||
}
|
||||
|
||||
export type LogServerNames = string[];
|
||||
export type LogLevels = LogLevelEnum[];
|
||||
export type LogDateFrom = string; // epoch
|
||||
export type LogDateTo = string; // epoch
|
||||
|
||||
export type LogObject = {
|
||||
caller: string;
|
||||
job_id: string;
|
||||
level: LogLevelEnum;
|
||||
msg: string;
|
||||
timestamp: string;
|
||||
worker: string;
|
||||
}
|
||||
|
||||
export type LogFilter = {
|
||||
serverNames: LogServerNames;
|
||||
logLevels: LogLevels;
|
||||
dateFrom: LogDateFrom;
|
||||
dateTo: LogDateTo;
|
||||
}
|
||||
|
||||
export type AdminState = {
|
||||
logs: LogObject[];
|
||||
audits: Record<string, Audit>;
|
||||
config: Partial<AdminConfig>;
|
||||
environmentConfig: Partial<EnvironmentConfig>;
|
||||
complianceReports: Record<string, Compliance>;
|
||||
ldapGroups: Record<string, MixedUnlinkedGroupRedux>;
|
||||
ldapGroupsCount: number;
|
||||
userAccessTokens: Record<string, UserAccessToken>;
|
||||
clusterInfo: ClusterInfo[];
|
||||
samlCertStatus?: SamlCertificateStatus;
|
||||
analytics?: Record<string, number | AnalyticsRow[]>;
|
||||
teamAnalytics?: RelationOneToOne<Team, Record<string, number | AnalyticsRow[]>>;
|
||||
userAccessTokensByUser?: RelationOneToOne<UserProfile, Record<string, UserAccessToken>>;
|
||||
plugins?: Record<string, PluginRedux>;
|
||||
pluginStatuses?: Record<string, PluginStatusRedux>;
|
||||
samlMetadataResponse?: SamlMetadataResponse;
|
||||
dataRetentionCustomPolicies: DataRetentionCustomPolicies;
|
||||
dataRetentionCustomPoliciesCount: number;
|
||||
prevTrialLicense: ClientLicense;
|
||||
};
|
||||
|
||||
export type ClusterInfo = {
|
||||
id: string;
|
||||
version: string;
|
||||
config_hash: string;
|
||||
ipaddress: string;
|
||||
hostname: string;
|
||||
};
|
||||
|
||||
export type AnalyticsRow = {
|
||||
name: string;
|
||||
value: number;
|
||||
};
|
||||
|
||||
export type IndexedPluginAnalyticsRow = {
|
||||
[key: string]: PluginAnalyticsRow;
|
||||
}
|
||||
|
||||
export type PluginAnalyticsRow = {
|
||||
id: string;
|
||||
name: React.ReactNode;
|
||||
icon: string;
|
||||
value: number;
|
||||
};
|
||||
|
||||
export type SchemaMigration = {
|
||||
version: number;
|
||||
name: string;
|
||||
};
|
||||
258
webapp/platform/types/src/apps.ts
Обычный файл
258
webapp/platform/types/src/apps.ts
Обычный файл
@@ -0,0 +1,258 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {ProductScope} from './products';
|
||||
|
||||
export enum Permission {
|
||||
UserJoinedChannelNotification = 'user_joined_channel_notification',
|
||||
ActAsBot = 'act_as_bot',
|
||||
ActAsUser = 'act_as_user',
|
||||
PermissionActAsAdmin = 'act_as_admin',
|
||||
RemoteOAuth2 = 'remote_oauth2',
|
||||
RemoteWebhooks = 'remote_webhooks',
|
||||
}
|
||||
|
||||
export enum Locations {
|
||||
PostMenu = '/post_menu',
|
||||
ChannelHeader = '/channel_header',
|
||||
Command = '/command',
|
||||
InPost = '/in_post',
|
||||
}
|
||||
|
||||
export type AppManifest = {
|
||||
app_id: string;
|
||||
version?: string;
|
||||
homepage_url?: string;
|
||||
icon?: string;
|
||||
display_name: string;
|
||||
description?: string;
|
||||
requested_permissions?: Permission[];
|
||||
requested_locations?: Locations[];
|
||||
}
|
||||
|
||||
export type AppModalState = {
|
||||
form: AppForm;
|
||||
call: AppCallRequest;
|
||||
}
|
||||
|
||||
export type AppCommandFormMap = { [location: string]: AppForm }
|
||||
|
||||
export type BindingsInfo = {
|
||||
bindings: AppBinding[];
|
||||
forms: AppCommandFormMap;
|
||||
}
|
||||
|
||||
export type AppsState = {
|
||||
main: BindingsInfo;
|
||||
rhs: BindingsInfo;
|
||||
pluginEnabled: boolean;
|
||||
};
|
||||
|
||||
export type AppBinding = {
|
||||
app_id: string;
|
||||
location?: string;
|
||||
supported_product_ids?: ProductScope;
|
||||
icon?: string;
|
||||
|
||||
// Label is the (usually short) primary text to display at the location.
|
||||
// - For LocationPostMenu is the menu item text.
|
||||
// - For LocationChannelHeader is the dropdown text.
|
||||
// - For LocationCommand is the name of the command
|
||||
label: string;
|
||||
|
||||
// Hint is the secondary text to display
|
||||
// - LocationPostMenu: not used
|
||||
// - LocationChannelHeader: tooltip
|
||||
// - LocationCommand: the "Hint" line
|
||||
hint?: string;
|
||||
|
||||
// Description is the (optional) extended help text, used in modals and autocomplete
|
||||
description?: string;
|
||||
|
||||
role_id?: string;
|
||||
depends_on_team?: boolean;
|
||||
depends_on_channel?: boolean;
|
||||
depends_on_user?: boolean;
|
||||
depends_on_post?: boolean;
|
||||
|
||||
// A Binding is either an action (makes a call), a Form, or is a
|
||||
// "container" for other locations - i.e. menu sub-items or subcommands.
|
||||
bindings?: AppBinding[];
|
||||
form?: AppForm;
|
||||
submit?: AppCall;
|
||||
};
|
||||
|
||||
export type AppCallValues = {
|
||||
[name: string]: any;
|
||||
};
|
||||
|
||||
export type AppCall = {
|
||||
path: string;
|
||||
expand?: AppExpand;
|
||||
state?: any;
|
||||
};
|
||||
|
||||
export type AppCallRequest = AppCall & {
|
||||
context: AppContext;
|
||||
values?: AppCallValues;
|
||||
raw_command?: string;
|
||||
selected_field?: string;
|
||||
query?: string;
|
||||
};
|
||||
|
||||
export type AppCallResponseType = string;
|
||||
|
||||
export type AppCallResponse<Res = unknown> = {
|
||||
type: AppCallResponseType;
|
||||
text?: string;
|
||||
data?: Res;
|
||||
navigate_to_url?: string;
|
||||
use_external_browser?: boolean;
|
||||
call?: AppCall;
|
||||
form?: AppForm;
|
||||
app_metadata?: AppMetadataForClient;
|
||||
};
|
||||
|
||||
export type AppMetadataForClient = {
|
||||
bot_user_id: string;
|
||||
bot_username: string;
|
||||
}
|
||||
|
||||
export type AppContext = {
|
||||
app_id: string;
|
||||
location?: string;
|
||||
acting_user_id?: string;
|
||||
user_id?: string;
|
||||
channel_id?: string;
|
||||
team_id?: string;
|
||||
post_id?: string;
|
||||
root_id?: string;
|
||||
props?: AppContextProps;
|
||||
user_agent?: string;
|
||||
track_as_submit?: boolean;
|
||||
};
|
||||
|
||||
export type AppContextProps = {
|
||||
[name: string]: string;
|
||||
};
|
||||
|
||||
export type AppExpandLevel = ''
|
||||
| 'none'
|
||||
| 'summary'
|
||||
| '+summary'
|
||||
| 'all'
|
||||
| '+all';
|
||||
|
||||
export type AppExpand = {
|
||||
app?: AppExpandLevel;
|
||||
acting_user?: AppExpandLevel;
|
||||
channel?: AppExpandLevel;
|
||||
config?: AppExpandLevel;
|
||||
mentioned?: AppExpandLevel;
|
||||
parent_post?: AppExpandLevel;
|
||||
post?: AppExpandLevel;
|
||||
root_post?: AppExpandLevel;
|
||||
team?: AppExpandLevel;
|
||||
user?: AppExpandLevel;
|
||||
locale?: AppExpandLevel;
|
||||
};
|
||||
|
||||
export type AppForm = {
|
||||
title?: string;
|
||||
header?: string;
|
||||
footer?: string;
|
||||
icon?: string;
|
||||
submit_buttons?: string;
|
||||
cancel_button?: boolean;
|
||||
submit_on_cancel?: boolean;
|
||||
fields?: AppField[];
|
||||
|
||||
// source is used in 2 cases:
|
||||
// - if submit is not set, it is used to fetch the submittable form from
|
||||
// the app.
|
||||
// - if a select field change triggers a refresh, the form is refreshed
|
||||
// from source.
|
||||
source?: AppCall;
|
||||
|
||||
// submit is called when one of the submit buttons is pressed, or the
|
||||
// command is executed.
|
||||
submit?: AppCall;
|
||||
|
||||
depends_on?: string[];
|
||||
};
|
||||
|
||||
export type AppFormValue = string | AppSelectOption | boolean | null;
|
||||
export type AppFormValues = { [name: string]: AppFormValue };
|
||||
|
||||
export type AppSelectOption = {
|
||||
label: string;
|
||||
value: string;
|
||||
icon_data?: string;
|
||||
};
|
||||
|
||||
export type AppFieldType = string;
|
||||
|
||||
// This should go in mattermost-redux
|
||||
export type AppField = {
|
||||
|
||||
// Name is the name of the JSON field to use.
|
||||
name: string;
|
||||
type: AppFieldType;
|
||||
is_required?: boolean;
|
||||
readonly?: boolean;
|
||||
|
||||
// Present (default) value of the field
|
||||
value?: AppFormValue;
|
||||
|
||||
description?: string;
|
||||
|
||||
label?: string;
|
||||
hint?: string;
|
||||
position?: number;
|
||||
|
||||
modal_label?: string;
|
||||
|
||||
// Select props
|
||||
refresh?: boolean;
|
||||
options?: AppSelectOption[];
|
||||
multiselect?: boolean;
|
||||
lookup?: AppCall;
|
||||
|
||||
// Text props
|
||||
subtype?: string;
|
||||
min_length?: number;
|
||||
max_length?: number;
|
||||
};
|
||||
|
||||
export type AutocompleteSuggestion = {
|
||||
suggestion: string;
|
||||
complete?: string;
|
||||
description?: string;
|
||||
hint?: string;
|
||||
iconData?: string;
|
||||
}
|
||||
|
||||
export type AutocompleteSuggestionWithComplete = AutocompleteSuggestion & {
|
||||
complete: string;
|
||||
}
|
||||
|
||||
export type AutocompleteElement = AppField;
|
||||
export type AutocompleteStaticSelect = AutocompleteElement & {
|
||||
options: AppSelectOption[];
|
||||
};
|
||||
|
||||
export type AutocompleteDynamicSelect = AutocompleteElement;
|
||||
|
||||
export type AutocompleteUserSelect = AutocompleteElement;
|
||||
|
||||
export type AutocompleteChannelSelect = AutocompleteElement;
|
||||
|
||||
export type FormResponseData = {
|
||||
errors?: {
|
||||
[field: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type AppLookupResponse = {
|
||||
items: AppSelectOption[];
|
||||
}
|
||||
12
webapp/platform/types/src/audits.ts
Обычный файл
12
webapp/platform/types/src/audits.ts
Обычный файл
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type Audit = {
|
||||
id: string;
|
||||
create_at: number;
|
||||
user_id: string;
|
||||
action: string;
|
||||
extra_info: string;
|
||||
ip_address: string;
|
||||
session_id: string;
|
||||
}
|
||||
20
webapp/platform/types/src/autocomplete.ts
Обычный файл
20
webapp/platform/types/src/autocomplete.ts
Обычный файл
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {UserProfile} from './users';
|
||||
|
||||
export type UserAutocomplete = {
|
||||
users: UserProfile[];
|
||||
|
||||
// out_of_channel contains users that aren't in the given channel. It's only populated when autocompleting users in
|
||||
// a given channel ID.
|
||||
out_of_channel?: UserProfile[];
|
||||
};
|
||||
|
||||
export type AutocompleteSuggestion = {
|
||||
Complete: string;
|
||||
Suggestion: string;
|
||||
Hint: string;
|
||||
Description: string;
|
||||
IconData: string;
|
||||
};
|
||||
49
webapp/platform/types/src/boards.ts
Обычный файл
49
webapp/platform/types/src/boards.ts
Обычный файл
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
const BoardTypeOpen = 'O';
|
||||
const BoardTypePrivate = 'P';
|
||||
const boardTypes = [BoardTypeOpen, BoardTypePrivate];
|
||||
type BoardTypes = typeof boardTypes[number];
|
||||
|
||||
type PropertyTypeEnum = 'text' | 'number' | 'select' | 'multiSelect' | 'date' | 'person' | 'file' | 'checkbox' | 'url' | 'email' | 'phone' | 'createdTime' | 'createdBy' | 'updatedTime' | 'updatedBy' | 'unknown';
|
||||
|
||||
interface IPropertyOption {
|
||||
id: string;
|
||||
value: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
// A template for card properties attached to a board
|
||||
interface IPropertyTemplate {
|
||||
id: string;
|
||||
name: string;
|
||||
type: PropertyTypeEnum;
|
||||
options: IPropertyOption[];
|
||||
}
|
||||
export declare type Board = {
|
||||
id: string;
|
||||
teamId: string;
|
||||
channelId?: string;
|
||||
createdBy: string;
|
||||
modifiedBy: string;
|
||||
type: BoardTypes;
|
||||
minimumRole: string;
|
||||
|
||||
title: string;
|
||||
description: string;
|
||||
icon?: string;
|
||||
showDescription: boolean;
|
||||
isTemplate: boolean;
|
||||
templateVersion: number;
|
||||
properties: Record<string, string | string[]>;
|
||||
cardProperties: IPropertyTemplate[];
|
||||
|
||||
createAt: number;
|
||||
updateAt: number;
|
||||
deleteAt: number;
|
||||
}
|
||||
|
||||
export declare type CreateBoardResponse = {
|
||||
boards: Board[];
|
||||
}
|
||||
20
webapp/platform/types/src/bots.ts
Обычный файл
20
webapp/platform/types/src/bots.ts
Обычный файл
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type Bot = {
|
||||
user_id: string ;
|
||||
username: string ;
|
||||
display_name?: string ;
|
||||
description?: string ;
|
||||
owner_id: string ;
|
||||
create_at: number ;
|
||||
update_at: number ;
|
||||
delete_at: number ;
|
||||
}
|
||||
|
||||
// BotPatch is a description of what fields to update on an existing bot.
|
||||
export type BotPatch = {
|
||||
username: string;
|
||||
display_name: string;
|
||||
description: string;
|
||||
}
|
||||
38
webapp/platform/types/src/channel_categories.ts
Обычный файл
38
webapp/platform/types/src/channel_categories.ts
Обычный файл
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Channel} from './channels';
|
||||
import {Team} from './teams';
|
||||
import {UserProfile} from './users';
|
||||
import {IDMappedObjects, RelationOneToOne} from './utilities';
|
||||
|
||||
export type ChannelCategoryType = 'favorites' | 'channels' | 'direct_messages' | 'custom';
|
||||
|
||||
export enum CategorySorting {
|
||||
Alphabetical = 'alpha',
|
||||
Default = '', // behaves the same as manual
|
||||
Recency = 'recent',
|
||||
Manual = 'manual',
|
||||
}
|
||||
|
||||
export type ChannelCategory = {
|
||||
id: string;
|
||||
user_id: UserProfile['id'];
|
||||
team_id: Team['id'];
|
||||
type: ChannelCategoryType;
|
||||
display_name: string;
|
||||
sorting: CategorySorting;
|
||||
channel_ids: Array<Channel['id']>;
|
||||
muted: boolean;
|
||||
collapsed: boolean;
|
||||
};
|
||||
|
||||
export type OrderedChannelCategories = {
|
||||
categories: ChannelCategory[];
|
||||
order: string[];
|
||||
};
|
||||
|
||||
export type ChannelCategoriesState = {
|
||||
byId: IDMappedObjects<ChannelCategory>;
|
||||
orderByTeam: RelationOneToOne<Team, Array<ChannelCategory['id']>>;
|
||||
};
|
||||
211
webapp/platform/types/src/channels.ts
Обычный файл
211
webapp/platform/types/src/channels.ts
Обычный файл
@@ -0,0 +1,211 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {IDMappedObjects, RelationOneToMany, RelationOneToOne} from './utilities';
|
||||
import {Team} from './teams';
|
||||
|
||||
// e.g.
|
||||
// **O**pen channel,
|
||||
// **P**rivate channel,
|
||||
// **D**irect message to one other,
|
||||
// **G**roup direct message to 2+ others
|
||||
export type ChannelType = 'O' | 'P' | 'D' | 'G' | 'threads';
|
||||
|
||||
export type ChannelStats = {
|
||||
channel_id: string;
|
||||
member_count: number;
|
||||
guest_count: number;
|
||||
pinnedpost_count: number;
|
||||
files_count: number;
|
||||
};
|
||||
|
||||
export type ChannelNotifyProps = {
|
||||
desktop: 'default' | 'all' | 'mention' | 'none';
|
||||
email: 'default' | 'all' | 'mention' | 'none';
|
||||
mark_unread: 'all' | 'mention';
|
||||
push: 'default' | 'all' | 'mention' | 'none';
|
||||
ignore_channel_mentions: 'default' | 'off' | 'on';
|
||||
};
|
||||
|
||||
export type Channel = {
|
||||
id: string;
|
||||
create_at: number;
|
||||
update_at: number;
|
||||
delete_at: number;
|
||||
team_id: string;
|
||||
type: ChannelType;
|
||||
display_name: string;
|
||||
name: string;
|
||||
header: string;
|
||||
purpose: string;
|
||||
last_post_at: number;
|
||||
last_root_post_at: number;
|
||||
creator_id: string;
|
||||
scheme_id: string;
|
||||
teammate_id?: string;
|
||||
status?: string;
|
||||
group_constrained: boolean;
|
||||
shared?: boolean;
|
||||
props?: Record<string, any>;
|
||||
policy_id?: string | null;
|
||||
};
|
||||
|
||||
export type ServerChannel = Channel & {
|
||||
|
||||
/**
|
||||
* The total number of posts in this channel, not including join/leave messages
|
||||
*
|
||||
* @remarks This field will be moved to a {@link ChannelMessageCount} object when this channel is stored in Redux.
|
||||
*/
|
||||
total_msg_count: number;
|
||||
|
||||
/**
|
||||
* The number of root posts in this channel, not including join/leave messages
|
||||
*
|
||||
* @remarks This field will be moved to a {@link ChannelMessageCount} object when this channel is stored in Redux.
|
||||
*/
|
||||
total_msg_count_root: number;
|
||||
}
|
||||
|
||||
export type ChannelMessageCount = {
|
||||
|
||||
/** The total number of posts in this channel, not including join/leave messages */
|
||||
total: number;
|
||||
|
||||
/** The number of root posts in this channel, not including join/leave messages */
|
||||
root: number;
|
||||
}
|
||||
|
||||
export type ChannelWithTeamData = Channel & {
|
||||
team_display_name: string;
|
||||
team_name: string;
|
||||
team_update_at: number;
|
||||
};
|
||||
|
||||
export type ChannelsWithTotalCount = {
|
||||
channels: ChannelWithTeamData[];
|
||||
total_count: number;
|
||||
};
|
||||
|
||||
export type ChannelMembership = {
|
||||
channel_id: string;
|
||||
user_id: string;
|
||||
roles: string;
|
||||
last_viewed_at: number;
|
||||
|
||||
/** The number of posts in this channel which have been read by the user */
|
||||
msg_count: number;
|
||||
|
||||
/** The number of root posts in this channel which have been read by the user */
|
||||
msg_count_root: number;
|
||||
|
||||
/** The number of unread mentions in this channel */
|
||||
mention_count: number;
|
||||
|
||||
/** The number of unread mentions in root posts in this channel */
|
||||
mention_count_root: number;
|
||||
|
||||
/** The number of unread urgent mentions in this channel */
|
||||
urgent_mention_count: number;
|
||||
|
||||
notify_props: Partial<ChannelNotifyProps>;
|
||||
last_update_at: number;
|
||||
scheme_user: boolean;
|
||||
scheme_admin: boolean;
|
||||
post_root_id?: string;
|
||||
};
|
||||
|
||||
export type ChannelUnread = {
|
||||
channel_id: string;
|
||||
user_id: string;
|
||||
team_id: string;
|
||||
|
||||
/** The number of posts which have been read by the user */
|
||||
msg_count: number;
|
||||
|
||||
/** The number of root posts which have been read by the user */
|
||||
msg_count_root: number;
|
||||
|
||||
/** The number of unread mentions in this channel */
|
||||
mention_count: number;
|
||||
|
||||
/** The number of unread urgent mentions in this channel */
|
||||
urgent_mention_count: number;
|
||||
|
||||
/** The number of unread mentions in root posts in this channel */
|
||||
mention_count_root: number;
|
||||
|
||||
last_viewed_at: number;
|
||||
deltaMsgs: number;
|
||||
};
|
||||
|
||||
export type ChannelsState = {
|
||||
currentChannelId: string;
|
||||
channels: IDMappedObjects<Channel>;
|
||||
channelsInTeam: RelationOneToMany<Team, Channel>;
|
||||
myMembers: RelationOneToOne<Channel, ChannelMembership>;
|
||||
roles: RelationOneToOne<Channel, Set<string>>;
|
||||
membersInChannel: RelationOneToOne<Channel, Record<string, ChannelMembership>>;
|
||||
stats: RelationOneToOne<Channel, ChannelStats>;
|
||||
groupsAssociatedToChannel: any;
|
||||
totalCount: number;
|
||||
manuallyUnread: RelationOneToOne<Channel, boolean>;
|
||||
channelModerations: RelationOneToOne<Channel, ChannelModeration[]>;
|
||||
channelMemberCountsByGroup: RelationOneToOne<Channel, ChannelMemberCountsByGroup>;
|
||||
messageCounts: RelationOneToOne<Channel, ChannelMessageCount>;
|
||||
};
|
||||
|
||||
export type ChannelModeration = {
|
||||
name: string;
|
||||
roles: {
|
||||
guests?: {
|
||||
value: boolean;
|
||||
enabled: boolean;
|
||||
};
|
||||
members: {
|
||||
value: boolean;
|
||||
enabled: boolean;
|
||||
};
|
||||
admins: {
|
||||
value: boolean;
|
||||
enabled: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type ChannelModerationPatch = {
|
||||
name: string;
|
||||
roles: {
|
||||
guests?: boolean;
|
||||
members?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type ChannelMemberCountByGroup = {
|
||||
group_id: string;
|
||||
channel_member_count: number;
|
||||
channel_member_timezones_count: number;
|
||||
};
|
||||
|
||||
export type ChannelMemberCountsByGroup = Record<string, ChannelMemberCountByGroup>;
|
||||
|
||||
export type ChannelViewResponse = {
|
||||
status: string;
|
||||
last_viewed_at_times: RelationOneToOne<Channel, number>;
|
||||
};
|
||||
|
||||
export type ChannelSearchOpts = {
|
||||
nonAdminSearch?: boolean;
|
||||
exclude_default_channels?: boolean;
|
||||
not_associated_to_group?: string;
|
||||
team_ids?: string[];
|
||||
group_constrained?: boolean;
|
||||
exclude_group_constrained?: boolean;
|
||||
public?: boolean;
|
||||
private?: boolean;
|
||||
include_deleted?: boolean;
|
||||
include_search_by_id?: boolean;
|
||||
deleted?: boolean;
|
||||
page?: number;
|
||||
per_page?: number;
|
||||
};
|
||||
38
webapp/platform/types/src/client4.ts
Обычный файл
38
webapp/platform/types/src/client4.ts
Обычный файл
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export enum LogLevel {
|
||||
Error = 'ERROR',
|
||||
Warning = 'WARNING',
|
||||
Info = 'INFO',
|
||||
Debug = 'DEBUG',
|
||||
}
|
||||
|
||||
export type ClientResponse<T> = {
|
||||
response: Response;
|
||||
headers: Map<string, string>;
|
||||
data: T;
|
||||
};
|
||||
|
||||
export type Options = {
|
||||
headers?: { [x: string]: string };
|
||||
method?: string;
|
||||
url?: string;
|
||||
credentials?: 'omit' | 'same-origin' | 'include';
|
||||
body?: any;
|
||||
};
|
||||
|
||||
export type StatusOK = {
|
||||
status: 'OK';
|
||||
};
|
||||
|
||||
export type FetchPaginatedThreadOptions = {
|
||||
fetchThreads?: boolean;
|
||||
collapsedThreads?: boolean;
|
||||
collapsedThreadsExtended?: boolean;
|
||||
direction?: 'up'|'down';
|
||||
fetchAll?: boolean;
|
||||
perPage?: number;
|
||||
fromCreateAt?: number;
|
||||
fromPost?: string;
|
||||
}
|
||||
237
webapp/platform/types/src/cloud.ts
Обычный файл
237
webapp/platform/types/src/cloud.ts
Обычный файл
@@ -0,0 +1,237 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {ValueOf} from './utilities';
|
||||
|
||||
export type CloudState = {
|
||||
subscription?: Subscription;
|
||||
products?: Record<string, Product>;
|
||||
customer?: CloudCustomer;
|
||||
invoices?: Record<string, Invoice>;
|
||||
subscriptionStats?: LicenseSelfServeStatusReducer;
|
||||
limits: {
|
||||
limitsLoaded: boolean;
|
||||
limits: Limits;
|
||||
};
|
||||
errors: {
|
||||
subscription?: true;
|
||||
products?: true;
|
||||
customer?: true;
|
||||
invoices?: true;
|
||||
limits?: true;
|
||||
trueUpReview?: true;
|
||||
};
|
||||
selfHostedSignup: {
|
||||
progress: ValueOf<typeof SelfHostedSignupProgress>;
|
||||
};
|
||||
}
|
||||
|
||||
export type Subscription = {
|
||||
id: string;
|
||||
customer_id: string;
|
||||
product_id: string;
|
||||
add_ons: string[];
|
||||
start_at: number;
|
||||
end_at: number;
|
||||
create_at: number;
|
||||
seats: number;
|
||||
last_invoice?: Invoice;
|
||||
upcoming_invoice?: Invoice;
|
||||
trial_end_at: number;
|
||||
is_free_trial: string;
|
||||
delinquent_since?: number;
|
||||
compliance_blocked?: string;
|
||||
}
|
||||
|
||||
export type Product = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
price_per_seat: number;
|
||||
add_ons: AddOn[];
|
||||
product_family: string;
|
||||
sku: string;
|
||||
billing_scheme: string;
|
||||
recurring_interval: string;
|
||||
cross_sells_to: string;
|
||||
};
|
||||
|
||||
export type AddOn = {
|
||||
id: string;
|
||||
name: string;
|
||||
display_name: string;
|
||||
price_per_seat: number;
|
||||
};
|
||||
|
||||
export const TypePurchases = {
|
||||
firstSelfHostLicensePurchase: 'first_purchase',
|
||||
renewalSelfHost: 'renewal_self',
|
||||
monthlySubscription: 'monthly_subscription',
|
||||
annualSubscription: 'annual_subscription',
|
||||
} as const;
|
||||
|
||||
export const SelfHostedSignupProgress = {
|
||||
START: 'START',
|
||||
CREATED_CUSTOMER: 'CREATED_CUSTOMER',
|
||||
CREATED_INTENT: 'CREATED_INTENT',
|
||||
CONFIRMED_INTENT: 'CONFIRMED_INTENT',
|
||||
CREATED_SUBSCRIPTION: 'CREATED_SUBSCRIPTION',
|
||||
PAID: 'PAID',
|
||||
CREATED_LICENSE: 'CREATED_LICENSE',
|
||||
} as const;
|
||||
|
||||
export type MetadataGatherWireTransferKeys = `${ValueOf<typeof TypePurchases>}_alt_payment_method`
|
||||
|
||||
export type CustomerMetadataGatherWireTransfer = Partial<Record<MetadataGatherWireTransferKeys, string>>
|
||||
|
||||
// Customer model represents a customer on the system.
|
||||
export type CloudCustomer = {
|
||||
id: string;
|
||||
creator_id: string;
|
||||
create_at: number;
|
||||
email: string;
|
||||
name: string;
|
||||
num_employees: number;
|
||||
contact_first_name: string;
|
||||
contact_last_name: string;
|
||||
billing_address: Address;
|
||||
company_address: Address;
|
||||
payment_method: PaymentMethod;
|
||||
} & CustomerMetadataGatherWireTransfer
|
||||
|
||||
export type LicenseSelfServeStatus = {
|
||||
is_expandable?: boolean;
|
||||
is_renewable?: boolean;
|
||||
}
|
||||
|
||||
type RequestState = 'IDLE' | 'LOADING' | 'ERROR' | 'OK'
|
||||
export interface LicenseSelfServeStatusReducer extends LicenseSelfServeStatus {
|
||||
getRequestState: RequestState;
|
||||
}
|
||||
|
||||
// CustomerPatch model represents a customer patch on the system.
|
||||
export type CloudCustomerPatch = {
|
||||
email?: string;
|
||||
name?: string;
|
||||
num_employees?: number;
|
||||
contact_first_name?: string;
|
||||
contact_last_name?: string;
|
||||
} & CustomerMetadataGatherWireTransfer
|
||||
|
||||
// Address model represents a customer's address.
|
||||
export type Address = {
|
||||
city: string;
|
||||
country: string;
|
||||
line1: string;
|
||||
line2: string;
|
||||
postal_code: string;
|
||||
state: string;
|
||||
}
|
||||
|
||||
// PaymentMethod represents methods of payment for a customer.
|
||||
export type PaymentMethod = {
|
||||
type: string;
|
||||
last_four: string;
|
||||
exp_month: number;
|
||||
exp_year: number;
|
||||
card_brand: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type NotifyAdminRequest = {
|
||||
trial_notification: boolean;
|
||||
required_plan: string;
|
||||
required_feature: string;
|
||||
}
|
||||
|
||||
// Invoice model represents a invoice on the system.
|
||||
export type Invoice = {
|
||||
id: string;
|
||||
number: string;
|
||||
create_at: number;
|
||||
total: number;
|
||||
tax: number;
|
||||
status: string;
|
||||
description: string;
|
||||
period_start: number;
|
||||
period_end: number;
|
||||
subscription_id: string;
|
||||
line_items: InvoiceLineItem[];
|
||||
current_product_name: string;
|
||||
}
|
||||
|
||||
// actual string values come from customer-web-server and should be kept in sync with values seen there
|
||||
export const InvoiceLineItemType = {
|
||||
Full: 'full',
|
||||
Partial: 'partial',
|
||||
OnPremise: 'onpremise',
|
||||
Metered: 'metered',
|
||||
} as const;
|
||||
|
||||
// InvoiceLineItem model represents a invoice lineitem tied to an invoice.
|
||||
export type InvoiceLineItem = {
|
||||
price_id: string;
|
||||
total: number;
|
||||
quantity: number;
|
||||
price_per_unit: number;
|
||||
description: string;
|
||||
type: typeof InvoiceLineItemType[keyof typeof InvoiceLineItemType];
|
||||
metadata: Record<string, string>;
|
||||
}
|
||||
|
||||
export type Limits = {
|
||||
messages?: {
|
||||
history?: number;
|
||||
};
|
||||
files?: {
|
||||
total_storage?: number;
|
||||
};
|
||||
teams?: {
|
||||
active?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CloudUsage {
|
||||
files: {
|
||||
totalStorage: number;
|
||||
totalStorageLoaded: boolean;
|
||||
};
|
||||
messages: {
|
||||
history: number;
|
||||
historyLoaded: boolean;
|
||||
};
|
||||
teams: TeamsUsage;
|
||||
}
|
||||
|
||||
export type TeamsUsage = {
|
||||
active: number;
|
||||
cloudArchived: number;
|
||||
teamsLoaded: boolean;
|
||||
}
|
||||
|
||||
export type ValidBusinessEmail = {
|
||||
is_valid: boolean;
|
||||
}
|
||||
|
||||
export interface CreateSubscriptionRequest {
|
||||
product_id: string;
|
||||
add_ons: string[];
|
||||
seats: number;
|
||||
internal_purchase_order?: string;
|
||||
}
|
||||
|
||||
export const areShippingDetailsValid = (address: Address | null | undefined): boolean => {
|
||||
if (!address) {
|
||||
return false;
|
||||
}
|
||||
return Boolean(address.city && address.country && address.line1 && address.postal_code && address.state);
|
||||
};
|
||||
export type Feedback = {
|
||||
reason: string;
|
||||
comments: string;
|
||||
}
|
||||
|
||||
export type WorkspaceDeletionRequest = {
|
||||
subscription_id: string;
|
||||
delete_feedback: Feedback;
|
||||
}
|
||||
16
webapp/platform/types/src/compliance.ts
Обычный файл
16
webapp/platform/types/src/compliance.ts
Обычный файл
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type Compliance = {
|
||||
id: string;
|
||||
create_at: number;
|
||||
user_id: string;
|
||||
status: string;
|
||||
count: number;
|
||||
desc: string;
|
||||
type: string;
|
||||
start_at: number;
|
||||
end_at: number;
|
||||
keywords: string;
|
||||
emails: string;
|
||||
};
|
||||
932
webapp/platform/types/src/config.ts
Обычный файл
932
webapp/platform/types/src/config.ts
Обычный файл
@@ -0,0 +1,932 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/* eslint-disable max-lines */
|
||||
|
||||
export type ClientConfig = {
|
||||
AboutLink: string;
|
||||
AllowBannerDismissal: string;
|
||||
AllowCustomThemes: string;
|
||||
AllowSyncedDrafts: string;
|
||||
AllowedThemes: string;
|
||||
AndroidAppDownloadLink: string;
|
||||
AndroidLatestVersion: string;
|
||||
AndroidMinVersion: string;
|
||||
AppDownloadLink: string;
|
||||
AsymmetricSigningPublicKey: string;
|
||||
AvailableLocales: string;
|
||||
BannerColor: string;
|
||||
BannerText: string;
|
||||
BannerTextColor: string;
|
||||
BuildBoards: string;
|
||||
BuildDate: string;
|
||||
BuildEnterpriseReady: string;
|
||||
BuildHash: string;
|
||||
BuildHashBoards: string;
|
||||
BuildHashEnterprise: string;
|
||||
BuildHashPlaybooks: string;
|
||||
BuildNumber: string;
|
||||
CollapsedThreads: CollapsedThreads;
|
||||
CustomBrandText: string;
|
||||
CustomDescriptionText: string;
|
||||
CustomTermsOfServiceId: string;
|
||||
CustomTermsOfServiceReAcceptancePeriod: string;
|
||||
CustomUrlSchemes: string;
|
||||
CWSURL: string;
|
||||
DataRetentionEnableFileDeletion: string;
|
||||
DataRetentionEnableMessageDeletion: string;
|
||||
DataRetentionFileRetentionDays: string;
|
||||
DataRetentionMessageRetentionDays: string;
|
||||
DefaultClientLocale: string;
|
||||
DefaultTheme: string;
|
||||
DiagnosticId: string;
|
||||
DiagnosticsEnabled: string;
|
||||
EmailLoginButtonBorderColor: string;
|
||||
EmailLoginButtonColor: string;
|
||||
EmailLoginButtonTextColor: string;
|
||||
EmailNotificationContentsType: string;
|
||||
EnableAskCommunityLink: string;
|
||||
EnableBanner: string;
|
||||
EnableBotAccountCreation: string;
|
||||
EnableChannelViewedMessages: string;
|
||||
EnableClientPerformanceDebugging: string;
|
||||
EnableCluster: string;
|
||||
EnableCommands: string;
|
||||
EnableCompliance: string;
|
||||
EnableConfirmNotificationsToChannel: string;
|
||||
EnableCustomBrand: string;
|
||||
EnableCustomEmoji: string;
|
||||
EnableCustomGroups: string;
|
||||
EnableCustomUserStatuses: string;
|
||||
EnableLastActiveTime: string;
|
||||
EnableTimedDND: string;
|
||||
EnableCustomTermsOfService: string;
|
||||
EnableDeveloper: string;
|
||||
EnableDiagnostics: string;
|
||||
EnableEmailBatching: string;
|
||||
EnableEmailInvitations: string;
|
||||
EnableEmojiPicker: string;
|
||||
EnableFileAttachments: string;
|
||||
EnableFile: string;
|
||||
EnableGifPicker: string;
|
||||
EnableGuestAccounts: string;
|
||||
EnableIncomingWebhooks: string;
|
||||
EnableLatex: string;
|
||||
EnableInlineLatex: string;
|
||||
EnableLdap: string;
|
||||
EnableLinkPreviews: string;
|
||||
EnableMarketplace: string;
|
||||
EnableMetrics: string;
|
||||
EnableMobileFileDownload: string;
|
||||
EnableMobileFileUpload: string;
|
||||
EnableMultifactorAuthentication: string;
|
||||
EnableOAuthServiceProvider: string;
|
||||
EnableOpenServer: string;
|
||||
EnableOutgoingWebhooks: string;
|
||||
EnablePostIconOverride: string;
|
||||
EnablePostUsernameOverride: string;
|
||||
EnablePreviewFeatures: string;
|
||||
EnablePreviewModeBanner: string;
|
||||
EnablePublicLink: string;
|
||||
EnableReliableWebSockets: string;
|
||||
EnableSaml: string;
|
||||
EnableSignInWithEmail: string;
|
||||
EnableSignInWithUsername: string;
|
||||
EnableSignUpWithEmail: string;
|
||||
EnableSignUpWithGitLab: string;
|
||||
EnableSignUpWithGoogle: string;
|
||||
EnableSignUpWithOffice365: string;
|
||||
EnableSignUpWithOpenId: string;
|
||||
EnableSVGs: string;
|
||||
EnableTesting: string;
|
||||
EnableThemeSelection: string;
|
||||
EnableTutorial: string;
|
||||
EnableOnboardingFlow: string;
|
||||
EnableUserAccessTokens: string;
|
||||
EnableUserCreation: string;
|
||||
EnableUserDeactivation: string;
|
||||
EnableUserTypingMessages: string;
|
||||
EnforceMultifactorAuthentication: string;
|
||||
ExperimentalClientSideCertCheck: string;
|
||||
ExperimentalClientSideCertEnable: string;
|
||||
ExperimentalEnableAuthenticationTransfer: string;
|
||||
ExperimentalEnableAutomaticReplies: string;
|
||||
ExperimentalEnableDefaultChannelLeaveJoinMessages: string;
|
||||
ExperimentalEnablePostMetadata: string;
|
||||
ExperimentalGroupUnreadChannels: string;
|
||||
ExperimentalPrimaryTeam: string;
|
||||
ExperimentalTimezone: string;
|
||||
ExperimentalViewArchivedChannels: string;
|
||||
FileLevel: string;
|
||||
FeatureFlagAppsEnabled: string;
|
||||
FeatureFlagBoardsProduct: string;
|
||||
FeatureFlagCallsEnabled: string;
|
||||
FeatureFlagGraphQL: string;
|
||||
GfycatAPIKey: string;
|
||||
GfycatAPISecret: string;
|
||||
GoogleDeveloperKey: string;
|
||||
GuestAccountsEnforceMultifactorAuthentication: string;
|
||||
HasImageProxy: string;
|
||||
HelpLink: string;
|
||||
IosAppDownloadLink: string;
|
||||
IosLatestVersion: string;
|
||||
IosMinVersion: string;
|
||||
InsightsEnabled: string;
|
||||
InstallationDate: string;
|
||||
IsDefaultMarketplace: string;
|
||||
LdapFirstNameAttributeSet: string;
|
||||
LdapLastNameAttributeSet: string;
|
||||
LdapLoginButtonBorderColor: string;
|
||||
LdapLoginButtonColor: string;
|
||||
LdapLoginButtonTextColor: string;
|
||||
LdapLoginFieldName: string;
|
||||
LdapNicknameAttributeSet: string;
|
||||
LdapPositionAttributeSet: string;
|
||||
LdapPictureAttributeSet: string;
|
||||
LockTeammateNameDisplay: string;
|
||||
ManagedResourcePaths: string;
|
||||
MaxFileSize: string;
|
||||
MaxPostSize: string;
|
||||
MaxNotificationsPerChannel: string;
|
||||
MinimumHashtagLength: string;
|
||||
NoAccounts: string;
|
||||
GitLabButtonText: string;
|
||||
GitLabButtonColor: string;
|
||||
OpenIdButtonText: string;
|
||||
OpenIdButtonColor: string;
|
||||
PasswordMinimumLength: string;
|
||||
PasswordRequireLowercase: string;
|
||||
PasswordRequireNumber: string;
|
||||
PasswordRequireSymbol: string;
|
||||
PasswordRequireUppercase: string;
|
||||
PluginsEnabled: string;
|
||||
PostEditTimeLimit: string;
|
||||
PrivacyPolicyLink: string;
|
||||
ReportAProblemLink: string;
|
||||
RequireEmailVerification: string;
|
||||
RestrictDirectMessage: string;
|
||||
RunJobs: string;
|
||||
SamlFirstNameAttributeSet: string;
|
||||
SamlLastNameAttributeSet: string;
|
||||
SamlLoginButtonBorderColor: string;
|
||||
SamlLoginButtonColor: string;
|
||||
SamlLoginButtonText: string;
|
||||
SamlLoginButtonTextColor: string;
|
||||
SamlNicknameAttributeSet: string;
|
||||
SamlPositionAttributeSet: string;
|
||||
SchemaVersion: string;
|
||||
SendEmailNotifications: string;
|
||||
SendPushNotifications: string;
|
||||
ShowEmailAddress: string;
|
||||
SiteName: string;
|
||||
SiteURL: string;
|
||||
SQLDriverName: string;
|
||||
SupportEmail: string;
|
||||
TelemetryId: string;
|
||||
TeammateNameDisplay: string;
|
||||
TermsOfServiceLink: string;
|
||||
TimeBetweenUserTypingUpdatesMilliseconds: string;
|
||||
UpgradedFromTE: string;
|
||||
Version: string;
|
||||
WebsocketPort: string;
|
||||
WebsocketSecurePort: string;
|
||||
WebsocketURL: string;
|
||||
ExperimentalSharedChannels: string;
|
||||
EnableAppBar: string;
|
||||
EnableComplianceExport: string;
|
||||
PostPriority: string;
|
||||
ReduceOnBoardingTaskList: string;
|
||||
PostAcknowledgements: string;
|
||||
};
|
||||
|
||||
export type License = {
|
||||
id: string;
|
||||
issued_at: number;
|
||||
starts_at: number;
|
||||
expires_at: string;
|
||||
customer: LicenseCustomer;
|
||||
features: LicenseFeatures;
|
||||
sku_name: string;
|
||||
short_sku_name: string;
|
||||
};
|
||||
|
||||
export type LicenseCustomer = {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
company: string;
|
||||
};
|
||||
|
||||
export type LicenseFeatures = {
|
||||
users?: number;
|
||||
ldap?: boolean;
|
||||
ldap_groups?: boolean;
|
||||
mfa?: boolean;
|
||||
google_oauth?: boolean;
|
||||
office365_oauth?: boolean;
|
||||
compliance?: boolean;
|
||||
cluster?: boolean;
|
||||
metrics?: boolean;
|
||||
mhpns?: boolean;
|
||||
saml?: boolean;
|
||||
elastic_search?: boolean;
|
||||
announcement?: boolean;
|
||||
theme_management?: boolean;
|
||||
email_notification_contents?: boolean;
|
||||
data_retention?: boolean;
|
||||
message_export?: boolean;
|
||||
custom_permissions_schemes?: boolean;
|
||||
custom_terms_of_service?: boolean;
|
||||
guest_accounts?: boolean;
|
||||
guest_accounts_permissions?: boolean;
|
||||
id_loaded?: boolean;
|
||||
lock_teammate_name_display?: boolean;
|
||||
cloud?: boolean;
|
||||
future_features?: boolean;
|
||||
};
|
||||
|
||||
export type ClientLicense = Record<string, string>;
|
||||
|
||||
export type RequestLicenseBody = {
|
||||
users: number;
|
||||
terms_accepted: boolean;
|
||||
receive_emails_accepted: boolean;
|
||||
}
|
||||
|
||||
export type DataRetentionPolicy = {
|
||||
message_deletion_enabled: boolean;
|
||||
file_deletion_enabled: boolean;
|
||||
message_retention_cutoff: number;
|
||||
file_retention_cutoff: number;
|
||||
};
|
||||
|
||||
export type ServiceSettings = {
|
||||
SiteURL: string;
|
||||
WebsocketURL: string;
|
||||
LicenseFileLocation: string;
|
||||
ListenAddress: string;
|
||||
ConnectionSecurity: string;
|
||||
TLSCertFile: string;
|
||||
TLSKeyFile: string;
|
||||
TLSMinVer: string;
|
||||
TLSStrictTransport: boolean;
|
||||
TLSStrictTransportMaxAge: number;
|
||||
TLSOverwriteCiphers: string[];
|
||||
UseLetsEncrypt: boolean;
|
||||
LetsEncryptCertificateCacheFile: string;
|
||||
Forward80To443: boolean;
|
||||
TrustedProxyIPHeader: string[];
|
||||
ReadTimeout: number;
|
||||
WriteTimeout: number;
|
||||
IdleTimeout: number;
|
||||
MaximumLoginAttempts: number;
|
||||
GoroutineHealthThreshold: number;
|
||||
GoogleDeveloperKey: string;
|
||||
EnableOAuthServiceProvider: boolean;
|
||||
EnableIncomingWebhooks: boolean;
|
||||
EnableOutgoingWebhooks: boolean;
|
||||
EnableCommands: boolean;
|
||||
EnablePostUsernameOverride: boolean;
|
||||
EnablePostIconOverride: boolean;
|
||||
EnableLinkPreviews: boolean;
|
||||
EnablePermalinkPreviews: boolean;
|
||||
RestrictLinkPreviews: string;
|
||||
EnableTesting: boolean;
|
||||
EnableDeveloper: boolean;
|
||||
DeveloperFlags: string;
|
||||
EnableClientPerformanceDebugging: boolean;
|
||||
EnableOpenTracing: boolean;
|
||||
EnableSecurityFixAlert: boolean;
|
||||
EnableInsecureOutgoingConnections: boolean;
|
||||
AllowedUntrustedInternalConnections: string;
|
||||
EnableMultifactorAuthentication: boolean;
|
||||
EnforceMultifactorAuthentication: boolean;
|
||||
EnableUserAccessTokens: boolean;
|
||||
AllowCorsFrom: string;
|
||||
CorsExposedHeaders: string;
|
||||
CorsAllowCredentials: boolean;
|
||||
CorsDebug: boolean;
|
||||
AllowCookiesForSubdomains: boolean;
|
||||
ExtendSessionLengthWithActivity: boolean;
|
||||
SessionLengthWebInDays: number;
|
||||
SessionLengthWebInHours: number;
|
||||
SessionLengthMobileInDays: number;
|
||||
SessionLengthMobileInHours: number;
|
||||
SessionLengthSSOInDays: number;
|
||||
SessionLengthSSOInHours: number;
|
||||
SessionCacheInMinutes: number;
|
||||
SessionIdleTimeoutInMinutes: number;
|
||||
WebsocketSecurePort: number;
|
||||
WebsocketPort: number;
|
||||
WebserverMode: string;
|
||||
EnableCustomEmoji: boolean;
|
||||
EnableEmojiPicker: boolean;
|
||||
EnableGifPicker: boolean;
|
||||
GfycatAPIKey: string;
|
||||
GfycatAPISecret: string;
|
||||
PostEditTimeLimit: number;
|
||||
TimeBetweenUserTypingUpdatesMilliseconds: number;
|
||||
EnablePostSearch: boolean;
|
||||
EnableFileSearch: boolean;
|
||||
MinimumHashtagLength: number;
|
||||
EnableUserTypingMessages: boolean;
|
||||
EnableChannelViewedMessages: boolean;
|
||||
EnableUserStatuses: boolean;
|
||||
ExperimentalEnableAuthenticationTransfer: boolean;
|
||||
ClusterLogTimeoutMilliseconds: number;
|
||||
EnablePreviewFeatures: boolean;
|
||||
EnableTutorial: boolean;
|
||||
EnableOnboardingFlow: boolean;
|
||||
ExperimentalEnableDefaultChannelLeaveJoinMessages: boolean;
|
||||
ExperimentalGroupUnreadChannels: string;
|
||||
EnableAPITeamDeletion: boolean;
|
||||
EnableAPITriggerAdminNotifications: boolean;
|
||||
EnableAPIUserDeletion: boolean;
|
||||
ExperimentalEnableHardenedMode: boolean;
|
||||
ExperimentalStrictCSRFEnforcement: boolean;
|
||||
EnableEmailInvitations: boolean;
|
||||
DisableBotsWhenOwnerIsDeactivated: boolean;
|
||||
EnableBotAccountCreation: boolean;
|
||||
EnableSVGs: boolean;
|
||||
EnableLatex: boolean;
|
||||
EnableInlineLatex: boolean;
|
||||
EnableLocalMode: boolean;
|
||||
LocalModeSocketLocation: string;
|
||||
CollapsedThreads: CollapsedThreads;
|
||||
ThreadAutoFollow: boolean;
|
||||
PostPriority: boolean;
|
||||
EnableAPIChannelDeletion: boolean;
|
||||
EnableAWSMetering: boolean;
|
||||
SplitKey: string;
|
||||
FeatureFlagSyncIntervalSeconds: number;
|
||||
DebugSplit: boolean;
|
||||
ManagedResourcePaths: string;
|
||||
EnableCustomGroups: boolean;
|
||||
SelfHostedPurchase: boolean;
|
||||
AllowSyncedDrafts: boolean;
|
||||
SelfHostedExpansion: boolean;
|
||||
};
|
||||
|
||||
export type TeamSettings = {
|
||||
SiteName: string;
|
||||
MaxUsersPerTeam: number;
|
||||
EnableCustomUserStatuses: boolean;
|
||||
EnableUserCreation: boolean;
|
||||
EnableOpenServer: boolean;
|
||||
EnableUserDeactivation: boolean;
|
||||
RestrictCreationToDomains: string;
|
||||
EnableCustomBrand: boolean;
|
||||
CustomBrandText: string;
|
||||
CustomDescriptionText: string;
|
||||
RestrictDirectMessage: string;
|
||||
UserStatusAwayTimeout: number;
|
||||
MaxChannelsPerTeam: number;
|
||||
MaxNotificationsPerChannel: number;
|
||||
EnableConfirmNotificationsToChannel: boolean;
|
||||
TeammateNameDisplay: string;
|
||||
ExperimentalViewArchivedChannels: boolean;
|
||||
ExperimentalEnableAutomaticReplies: boolean;
|
||||
LockTeammateNameDisplay: boolean;
|
||||
ExperimentalPrimaryTeam: string;
|
||||
ExperimentalDefaultChannels: string[];
|
||||
EnableLastActiveTime: boolean;
|
||||
};
|
||||
|
||||
export type ClientRequirements = {
|
||||
AndroidLatestVersion: string;
|
||||
AndroidMinVersion: string;
|
||||
IosLatestVersion: string;
|
||||
IosMinVersion: string;
|
||||
};
|
||||
|
||||
export type SqlSettings = {
|
||||
DriverName: string;
|
||||
DataSource: string;
|
||||
DataSourceReplicas: string[];
|
||||
DataSourceSearchReplicas: string[];
|
||||
MaxIdleConns: number;
|
||||
ConnMaxLifetimeMilliseconds: number;
|
||||
ConnMaxIdleTimeMilliseconds: number;
|
||||
MaxOpenConns: number;
|
||||
Trace: boolean;
|
||||
AtRestEncryptKey: string;
|
||||
QueryTimeout: number;
|
||||
DisableDatabaseSearch: boolean;
|
||||
MigrationsStatementTimeoutSeconds: number;
|
||||
ReplicaLagSettings: ReplicaLagSetting[];
|
||||
};
|
||||
|
||||
export type LogSettings = {
|
||||
EnableConsole: boolean;
|
||||
ConsoleLevel: string;
|
||||
ConsoleJson: boolean;
|
||||
EnableColor: boolean;
|
||||
EnableFile: boolean;
|
||||
FileLevel: string;
|
||||
FileJson: boolean;
|
||||
FileLocation: string;
|
||||
EnableWebhookDebugging: boolean;
|
||||
EnableDiagnostics: boolean;
|
||||
VerboseDiagnostics: boolean;
|
||||
EnableSentry: boolean;
|
||||
AdvancedLoggingConfig: string;
|
||||
};
|
||||
|
||||
export type ExperimentalAuditSettings = {
|
||||
FileEnabled: boolean;
|
||||
FileName: string;
|
||||
FileMaxSizeMB: number;
|
||||
FileMaxAgeDays: number;
|
||||
FileMaxBackups: number;
|
||||
FileCompress: boolean;
|
||||
FileMaxQueueSize: number;
|
||||
AdvancedLoggingConfig: string;
|
||||
};
|
||||
|
||||
export type NotificationLogSettings = {
|
||||
EnableConsole: boolean;
|
||||
ConsoleLevel: string;
|
||||
ConsoleJson: boolean;
|
||||
EnableColor: boolean;
|
||||
EnableFile: boolean;
|
||||
FileLevel: string;
|
||||
FileJson: boolean;
|
||||
FileLocation: string;
|
||||
AdvancedLoggingConfig: string;
|
||||
};
|
||||
|
||||
export type PasswordSettings = {
|
||||
MinimumLength: number;
|
||||
Lowercase: boolean;
|
||||
Number: boolean;
|
||||
Uppercase: boolean;
|
||||
Symbol: boolean;
|
||||
};
|
||||
|
||||
export type FileSettings = {
|
||||
EnableFileAttachments: boolean;
|
||||
EnableMobileUpload: boolean;
|
||||
EnableMobileDownload: boolean;
|
||||
MaxFileSize: number;
|
||||
MaxImageResolution: number;
|
||||
MaxImageDecoderConcurrency: number;
|
||||
DriverName: string;
|
||||
Directory: string;
|
||||
EnablePublicLink: boolean;
|
||||
ExtractContent: boolean;
|
||||
ArchiveRecursion: boolean;
|
||||
PublicLinkSalt: string;
|
||||
InitialFont: string;
|
||||
AmazonS3AccessKeyId: string;
|
||||
AmazonS3SecretAccessKey: string;
|
||||
AmazonS3Bucket: string;
|
||||
AmazonS3PathPrefix: string;
|
||||
AmazonS3Region: string;
|
||||
AmazonS3Endpoint: string;
|
||||
AmazonS3SSL: boolean;
|
||||
AmazonS3SignV2: boolean;
|
||||
AmazonS3SSE: boolean;
|
||||
AmazonS3Trace: boolean;
|
||||
AmazonS3RequestTimeoutMilliseconds: number;
|
||||
};
|
||||
|
||||
export type EmailSettings = {
|
||||
EnableSignUpWithEmail: boolean;
|
||||
EnableSignInWithEmail: boolean;
|
||||
EnableSignInWithUsername: boolean;
|
||||
SendEmailNotifications: boolean;
|
||||
UseChannelInEmailNotifications: boolean;
|
||||
RequireEmailVerification: boolean;
|
||||
FeedbackName: string;
|
||||
FeedbackEmail: string;
|
||||
ReplyToAddress: string;
|
||||
FeedbackOrganization: string;
|
||||
EnableSMTPAuth: boolean;
|
||||
SMTPUsername: string;
|
||||
SMTPPassword: string;
|
||||
SMTPServer: string;
|
||||
SMTPPort: string;
|
||||
SMTPServerTimeout: number;
|
||||
ConnectionSecurity: string;
|
||||
SendPushNotifications: boolean;
|
||||
PushNotificationServer: string;
|
||||
PushNotificationContents: string;
|
||||
PushNotificationBuffer: number;
|
||||
EnableEmailBatching: boolean;
|
||||
EmailBatchingBufferSize: number;
|
||||
EmailBatchingInterval: number;
|
||||
EnablePreviewModeBanner: boolean;
|
||||
SkipServerCertificateVerification: boolean;
|
||||
EmailNotificationContentsType: string;
|
||||
LoginButtonColor: string;
|
||||
LoginButtonBorderColor: string;
|
||||
LoginButtonTextColor: string;
|
||||
EnableInactivityEmail: boolean;
|
||||
};
|
||||
|
||||
export type RateLimitSettings = {
|
||||
Enable: boolean;
|
||||
PerSec: number;
|
||||
MaxBurst: number;
|
||||
MemoryStoreSize: number;
|
||||
VaryByRemoteAddr: boolean;
|
||||
VaryByUser: boolean;
|
||||
VaryByHeader: string;
|
||||
};
|
||||
|
||||
export type PrivacySettings = {
|
||||
ShowEmailAddress: boolean;
|
||||
ShowFullName: boolean;
|
||||
};
|
||||
|
||||
export type SupportSettings = {
|
||||
TermsOfServiceLink: string;
|
||||
PrivacyPolicyLink: string;
|
||||
AboutLink: string;
|
||||
HelpLink: string;
|
||||
ReportAProblemLink: string;
|
||||
SupportEmail: string;
|
||||
CustomTermsOfServiceEnabled: boolean;
|
||||
CustomTermsOfServiceReAcceptancePeriod: number;
|
||||
EnableAskCommunityLink: boolean;
|
||||
};
|
||||
|
||||
export type AnnouncementSettings = {
|
||||
EnableBanner: boolean;
|
||||
BannerText: string;
|
||||
BannerColor: string;
|
||||
BannerTextColor: string;
|
||||
AllowBannerDismissal: boolean;
|
||||
AdminNoticesEnabled: boolean;
|
||||
UserNoticesEnabled: boolean;
|
||||
NoticesURL: string;
|
||||
NoticesFetchFrequency: number;
|
||||
NoticesSkipCache: boolean;
|
||||
};
|
||||
|
||||
export type ThemeSettings = {
|
||||
EnableThemeSelection: boolean;
|
||||
DefaultTheme: string;
|
||||
AllowCustomThemes: boolean;
|
||||
AllowedThemes: string[];
|
||||
};
|
||||
|
||||
export type SSOSettings = {
|
||||
Enable: boolean;
|
||||
Secret: string;
|
||||
Id: string;
|
||||
Scope: string;
|
||||
AuthEndpoint: string;
|
||||
TokenEndpoint: string;
|
||||
UserAPIEndpoint: string;
|
||||
DiscoveryEndpoint: string;
|
||||
ButtonText: string;
|
||||
ButtonColor: string;
|
||||
};
|
||||
|
||||
export type Office365Settings = {
|
||||
Enable: boolean;
|
||||
Secret: string;
|
||||
Id: string;
|
||||
Scope: string;
|
||||
AuthEndpoint: string;
|
||||
TokenEndpoint: string;
|
||||
UserAPIEndpoint: string;
|
||||
DiscoveryEndpoint: string;
|
||||
DirectoryId: string;
|
||||
};
|
||||
|
||||
export type LdapSettings = {
|
||||
Enable: boolean;
|
||||
EnableSync: boolean;
|
||||
LdapServer: string;
|
||||
LdapPort: number;
|
||||
ConnectionSecurity: string;
|
||||
BaseDN: string;
|
||||
BindUsername: string;
|
||||
BindPassword: string;
|
||||
UserFilter: string;
|
||||
GroupFilter: string;
|
||||
GuestFilter: string;
|
||||
EnableAdminFilter: boolean;
|
||||
AdminFilter: string;
|
||||
GroupDisplayNameAttribute: string;
|
||||
GroupIdAttribute: string;
|
||||
FirstNameAttribute: string;
|
||||
LastNameAttribute: string;
|
||||
EmailAttribute: string;
|
||||
UsernameAttribute: string;
|
||||
NicknameAttribute: string;
|
||||
IdAttribute: string;
|
||||
PositionAttribute: string;
|
||||
LoginIdAttribute: string;
|
||||
PictureAttribute: string;
|
||||
SyncIntervalMinutes: number;
|
||||
SkipCertificateVerification: boolean;
|
||||
PublicCertificateFile: string;
|
||||
PrivateKeyFile: string;
|
||||
QueryTimeout: number;
|
||||
MaxPageSize: number;
|
||||
LoginFieldName: string;
|
||||
LoginButtonColor: string;
|
||||
LoginButtonBorderColor: string;
|
||||
LoginButtonTextColor: string;
|
||||
Trace: boolean;
|
||||
};
|
||||
|
||||
export type ComplianceSettings = {
|
||||
Enable: boolean;
|
||||
Directory: string;
|
||||
EnableDaily: boolean;
|
||||
BatchSize: number;
|
||||
};
|
||||
|
||||
export type LocalizationSettings = {
|
||||
DefaultServerLocale: string;
|
||||
DefaultClientLocale: string;
|
||||
AvailableLocales: string;
|
||||
};
|
||||
|
||||
export type SamlSettings = {
|
||||
Enable: boolean;
|
||||
EnableSyncWithLdap: boolean;
|
||||
EnableSyncWithLdapIncludeAuth: boolean;
|
||||
IgnoreGuestsLdapSync: boolean;
|
||||
Verify: boolean;
|
||||
Encrypt: boolean;
|
||||
SignRequest: boolean;
|
||||
IdpURL: string;
|
||||
IdpDescriptorURL: string;
|
||||
IdpMetadataURL: string;
|
||||
ServiceProviderIdentifier: string;
|
||||
AssertionConsumerServiceURL: string;
|
||||
SignatureAlgorithm: string;
|
||||
CanonicalAlgorithm: string;
|
||||
ScopingIDPProviderId: string;
|
||||
ScopingIDPName: string;
|
||||
IdpCertificateFile: string;
|
||||
PublicCertificateFile: string;
|
||||
PrivateKeyFile: string;
|
||||
IdAttribute: string;
|
||||
GuestAttribute: string;
|
||||
EnableAdminAttribute: boolean;
|
||||
AdminAttribute: string;
|
||||
FirstNameAttribute: string;
|
||||
LastNameAttribute: string;
|
||||
EmailAttribute: string;
|
||||
UsernameAttribute: string;
|
||||
NicknameAttribute: string;
|
||||
LocaleAttribute: string;
|
||||
PositionAttribute: string;
|
||||
LoginButtonText: string;
|
||||
LoginButtonColor: string;
|
||||
LoginButtonBorderColor: string;
|
||||
LoginButtonTextColor: string;
|
||||
};
|
||||
|
||||
export type NativeAppSettings = {
|
||||
AppCustomURLSchemes: string[];
|
||||
AppDownloadLink: string;
|
||||
AndroidAppDownloadLink: string;
|
||||
IosAppDownloadLink: string;
|
||||
};
|
||||
|
||||
export type ClusterSettings = {
|
||||
Enable: boolean;
|
||||
ClusterName: string;
|
||||
OverrideHostname: string;
|
||||
NetworkInterface: string;
|
||||
BindAddress: string;
|
||||
AdvertiseAddress: string;
|
||||
UseIPAddress: boolean;
|
||||
EnableGossipCompression: boolean;
|
||||
EnableExperimentalGossipEncryption: boolean;
|
||||
ReadOnlyConfig: boolean;
|
||||
GossipPort: number;
|
||||
StreamingPort: number;
|
||||
MaxIdleConns: number;
|
||||
MaxIdleConnsPerHost: number;
|
||||
IdleConnTimeoutMilliseconds: number;
|
||||
};
|
||||
|
||||
export type MetricsSettings = {
|
||||
Enable: boolean;
|
||||
BlockProfileRate: number;
|
||||
ListenAddress: string;
|
||||
};
|
||||
|
||||
export type ExperimentalSettings = {
|
||||
ClientSideCertEnable: boolean;
|
||||
ClientSideCertCheck: string;
|
||||
LinkMetadataTimeoutMilliseconds: number;
|
||||
RestrictSystemAdmin: boolean;
|
||||
UseNewSAMLLibrary: boolean;
|
||||
EnableSharedChannels: boolean;
|
||||
EnableRemoteClusterService: boolean;
|
||||
EnableAppBar: boolean;
|
||||
PatchPluginsReactDOM: boolean;
|
||||
};
|
||||
|
||||
export type AnalyticsSettings = {
|
||||
MaxUsersForStatistics: number;
|
||||
};
|
||||
|
||||
export type ElasticsearchSettings = {
|
||||
ConnectionURL: string;
|
||||
Username: string;
|
||||
Password: string;
|
||||
EnableIndexing: boolean;
|
||||
EnableSearching: boolean;
|
||||
EnableAutocomplete: boolean;
|
||||
Sniff: boolean;
|
||||
PostIndexReplicas: number;
|
||||
PostIndexShards: number;
|
||||
ChannelIndexReplicas: number;
|
||||
ChannelIndexShards: number;
|
||||
UserIndexReplicas: number;
|
||||
UserIndexShards: number;
|
||||
AggregatePostsAfterDays: number;
|
||||
PostsAggregatorJobStartTime: string;
|
||||
IndexPrefix: string;
|
||||
LiveIndexingBatchSize: number;
|
||||
BatchSize: number;
|
||||
RequestTimeoutSeconds: number;
|
||||
SkipTLSVerification: boolean;
|
||||
CA: string;
|
||||
ClientCert: string;
|
||||
ClientKey: string;
|
||||
Trace: string;
|
||||
};
|
||||
|
||||
export type BleveSettings = {
|
||||
IndexDir: string;
|
||||
EnableIndexing: boolean;
|
||||
EnableSearching: boolean;
|
||||
EnableAutocomplete: boolean;
|
||||
BatchSize: number;
|
||||
};
|
||||
|
||||
export type DataRetentionSettings = {
|
||||
EnableMessageDeletion: boolean;
|
||||
EnableFileDeletion: boolean;
|
||||
EnableBoardsDeletion: boolean;
|
||||
MessageRetentionDays: number;
|
||||
FileRetentionDays: number;
|
||||
BoardsRetentionDays: number;
|
||||
DeletionJobStartTime: string;
|
||||
BatchSize: number;
|
||||
};
|
||||
|
||||
export type MessageExportSettings = {
|
||||
EnableExport: boolean;
|
||||
DownloadExportResults: boolean;
|
||||
ExportFormat: string;
|
||||
DailyRunTime: string;
|
||||
ExportFromTimestamp: number;
|
||||
BatchSize: number;
|
||||
GlobalRelaySettings: {
|
||||
CustomerType: string;
|
||||
SMTPUsername: string;
|
||||
SMTPPassword: string;
|
||||
EmailAddress: string;
|
||||
SMTPServerTimeout: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type JobSettings = {
|
||||
RunJobs: boolean;
|
||||
RunScheduler: boolean;
|
||||
CleanupJobsThresholdDays: number;
|
||||
CleanupConfigThresholdDays: number;
|
||||
};
|
||||
|
||||
export type ProductSettings = {
|
||||
EnablePublicSharedBoards: boolean;
|
||||
};
|
||||
|
||||
export type PluginSettings = {
|
||||
Enable: boolean;
|
||||
EnableUploads: boolean;
|
||||
AllowInsecureDownloadURL: boolean;
|
||||
EnableHealthCheck: boolean;
|
||||
Directory: string;
|
||||
ClientDirectory: string;
|
||||
Plugins: Record<string, any>;
|
||||
PluginStates: Record<string, { Enable: boolean }>;
|
||||
EnableMarketplace: boolean;
|
||||
EnableRemoteMarketplace: boolean;
|
||||
AutomaticPrepackagedPlugins: boolean;
|
||||
RequirePluginSignature: boolean;
|
||||
MarketplaceURL: string;
|
||||
SignaturePublicKeyFiles: string[];
|
||||
ChimeraOAuthProxyURL: string;
|
||||
};
|
||||
|
||||
export type DisplaySettings = {
|
||||
CustomURLSchemes: string[];
|
||||
ExperimentalTimezone: boolean;
|
||||
};
|
||||
|
||||
export type GuestAccountsSettings = {
|
||||
Enable: boolean;
|
||||
AllowEmailAccounts: boolean;
|
||||
EnforceMultifactorAuthentication: boolean;
|
||||
RestrictCreationToDomains: string;
|
||||
};
|
||||
|
||||
export type ImageProxySettings = {
|
||||
Enable: boolean;
|
||||
ImageProxyType: string;
|
||||
RemoteImageProxyURL: string;
|
||||
RemoteImageProxyOptions: string;
|
||||
};
|
||||
|
||||
export type CloudSettings = {
|
||||
CWSURL: string;
|
||||
CWSAPIURL: string;
|
||||
};
|
||||
|
||||
export type FeatureFlags = Record<string, string | boolean>;
|
||||
|
||||
export type ImportSettings = {
|
||||
Directory: string;
|
||||
RetentionDays: number;
|
||||
};
|
||||
|
||||
export type ExportSettings = {
|
||||
Directory: string;
|
||||
RetentionDays: number;
|
||||
};
|
||||
|
||||
export type AdminConfig = {
|
||||
ServiceSettings: ServiceSettings;
|
||||
TeamSettings: TeamSettings;
|
||||
ClientRequirements: ClientRequirements;
|
||||
SqlSettings: SqlSettings;
|
||||
LogSettings: LogSettings;
|
||||
ExperimentalAuditSettings: ExperimentalAuditSettings;
|
||||
NotificationLogSettings: NotificationLogSettings;
|
||||
PasswordSettings: PasswordSettings;
|
||||
FileSettings: FileSettings;
|
||||
EmailSettings: EmailSettings;
|
||||
RateLimitSettings: RateLimitSettings;
|
||||
PrivacySettings: PrivacySettings;
|
||||
SupportSettings: SupportSettings;
|
||||
AnnouncementSettings: AnnouncementSettings;
|
||||
ThemeSettings: ThemeSettings;
|
||||
GitLabSettings: SSOSettings;
|
||||
GoogleSettings: SSOSettings;
|
||||
Office365Settings: Office365Settings;
|
||||
OpenIdSettings: SSOSettings;
|
||||
LdapSettings: LdapSettings;
|
||||
ComplianceSettings: ComplianceSettings;
|
||||
LocalizationSettings: LocalizationSettings;
|
||||
SamlSettings: SamlSettings;
|
||||
NativeAppSettings: NativeAppSettings;
|
||||
ClusterSettings: ClusterSettings;
|
||||
MetricsSettings: MetricsSettings;
|
||||
ExperimentalSettings: ExperimentalSettings;
|
||||
AnalyticsSettings: AnalyticsSettings;
|
||||
ElasticsearchSettings: ElasticsearchSettings;
|
||||
BleveSettings: BleveSettings;
|
||||
DataRetentionSettings: DataRetentionSettings;
|
||||
MessageExportSettings: MessageExportSettings;
|
||||
JobSettings: JobSettings;
|
||||
ProductSettings: ProductSettings;
|
||||
PluginSettings: PluginSettings;
|
||||
DisplaySettings: DisplaySettings;
|
||||
GuestAccountsSettings: GuestAccountsSettings;
|
||||
ImageProxySettings: ImageProxySettings;
|
||||
CloudSettings: CloudSettings;
|
||||
FeatureFlags: FeatureFlags;
|
||||
ImportSettings: ImportSettings;
|
||||
ExportSettings: ExportSettings;
|
||||
};
|
||||
|
||||
export type ReplicaLagSetting = {
|
||||
DataSource: string;
|
||||
QueryAbsoluteLag: string;
|
||||
QueryTimeLag: string;
|
||||
}
|
||||
|
||||
export type EnvironmentConfigSettings<T> = {
|
||||
[P in keyof T]: boolean;
|
||||
}
|
||||
|
||||
export type EnvironmentConfig = {
|
||||
[P in keyof AdminConfig]: EnvironmentConfigSettings<AdminConfig[P]>;
|
||||
}
|
||||
|
||||
export type WarnMetricStatus = {
|
||||
id: string;
|
||||
limit: number;
|
||||
acked: boolean;
|
||||
store_status: string;
|
||||
};
|
||||
|
||||
export enum CollapsedThreads {
|
||||
DISABLED = 'disabled',
|
||||
DEFAULT_ON = 'default_on',
|
||||
DEFAULT_OFF = 'default_off',
|
||||
ALWAYS_ON = 'always_on',
|
||||
}
|
||||
39
webapp/platform/types/src/data_retention.ts
Обычный файл
39
webapp/platform/types/src/data_retention.ts
Обычный файл
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type DataRetentionCustomPolicy = {
|
||||
id: string;
|
||||
display_name: string;
|
||||
post_duration: number;
|
||||
team_count: number;
|
||||
channel_count: number;
|
||||
};
|
||||
|
||||
export type CreateDataRetentionCustomPolicy = {
|
||||
display_name: string;
|
||||
post_duration: number;
|
||||
channel_ids: string[];
|
||||
team_ids: string[];
|
||||
};
|
||||
|
||||
export type PatchDataRetentionCustomPolicy = {
|
||||
display_name: string;
|
||||
post_duration: number;
|
||||
}
|
||||
|
||||
export type PatchDataRetentionCustomPolicyTeams = {
|
||||
team_ids: string[];
|
||||
}
|
||||
|
||||
export type PatchDataRetentionCustomPolicyChannels = {
|
||||
channel_ids: string[];
|
||||
}
|
||||
|
||||
export type DataRetentionCustomPolicies = {
|
||||
[x: string]: DataRetentionCustomPolicy;
|
||||
};
|
||||
|
||||
export type GetDataRetentionCustomPoliciesRequest = {
|
||||
policies: DataRetentionCustomPolicy[];
|
||||
total_count: number;
|
||||
};
|
||||
18
webapp/platform/types/src/drafts.ts
Обычный файл
18
webapp/platform/types/src/drafts.ts
Обычный файл
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {PostMetadata, PostPriorityMetadata} from './posts';
|
||||
|
||||
export type Draft = {
|
||||
create_at: number;
|
||||
update_at: number;
|
||||
delete_at: number;
|
||||
user_id: string;
|
||||
channel_id: string;
|
||||
root_id: string;
|
||||
message: string;
|
||||
props: Record<string, any>;
|
||||
file_ids?: string[];
|
||||
metadata?: PostMetadata;
|
||||
priority?: PostPriorityMetadata;
|
||||
};
|
||||
65
webapp/platform/types/src/emojis.ts
Обычный файл
65
webapp/platform/types/src/emojis.ts
Обычный файл
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type EmojiCategory =
|
||||
| 'recent'
|
||||
| 'searchResults'
|
||||
| 'smileys-emotion'
|
||||
| 'people-body'
|
||||
| 'animals-nature'
|
||||
| 'food-drink'
|
||||
| 'activities'
|
||||
| 'travel-places'
|
||||
| 'objects'
|
||||
| 'symbols'
|
||||
| 'flags'
|
||||
| 'custom';
|
||||
|
||||
export type CustomEmoji = {
|
||||
id: string;
|
||||
name: string;
|
||||
category: 'custom';
|
||||
create_at: number;
|
||||
update_at: number;
|
||||
delete_at: number;
|
||||
creator_id: string;
|
||||
};
|
||||
|
||||
export type SystemEmoji = {
|
||||
name: string;
|
||||
category: EmojiCategory;
|
||||
image: string;
|
||||
short_name: string;
|
||||
short_names: string[];
|
||||
batch: number;
|
||||
skins?: string[];
|
||||
skin_variations?: Record<string, SystemEmojiVariation>;
|
||||
unified: string;
|
||||
};
|
||||
|
||||
export type SystemEmojiVariation = {
|
||||
unified: string;
|
||||
non_qualified: null;
|
||||
image: string;
|
||||
sheet_x: number;
|
||||
sheet_y: number;
|
||||
added_in: string;
|
||||
has_img_apple: boolean;
|
||||
has_img_google: boolean;
|
||||
has_img_twitter: boolean;
|
||||
has_img_facebook: boolean;
|
||||
}
|
||||
|
||||
export type Emoji = SystemEmoji | CustomEmoji;
|
||||
|
||||
export type EmojisState = {
|
||||
customEmoji: {
|
||||
[x: string]: CustomEmoji;
|
||||
};
|
||||
nonExistentEmoji: Set<string>;
|
||||
};
|
||||
|
||||
export type RecentEmojiData = {
|
||||
name: string;
|
||||
usageCount: number;
|
||||
};
|
||||
11
webapp/platform/types/src/errors.ts
Обычный файл
11
webapp/platform/types/src/errors.ts
Обычный файл
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type ServerError = {
|
||||
type?: string;
|
||||
server_error_id?: string;
|
||||
stack?: string;
|
||||
message: string;
|
||||
status_code?: number;
|
||||
url?: string;
|
||||
};
|
||||
44
webapp/platform/types/src/files.ts
Обычный файл
44
webapp/platform/types/src/files.ts
Обычный файл
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type FileInfo = {
|
||||
id: string;
|
||||
user_id: string;
|
||||
create_at: number;
|
||||
update_at: number;
|
||||
delete_at: number;
|
||||
name: string;
|
||||
extension: string;
|
||||
size: number;
|
||||
mime_type: string;
|
||||
width: number;
|
||||
height: number;
|
||||
has_preview_image: boolean;
|
||||
clientId: string;
|
||||
post_id?: string;
|
||||
mini_preview?: string;
|
||||
archived: boolean;
|
||||
link?: string;
|
||||
};
|
||||
export type FilesState = {
|
||||
files: Record<string, FileInfo>;
|
||||
filesFromSearch: Record<string, FileSearchResultItem>;
|
||||
fileIdsByPostId: Record<string, string[]>;
|
||||
filePublicLink?: {link: string};
|
||||
};
|
||||
|
||||
export type FileUploadResponse = {
|
||||
file_infos: FileInfo[];
|
||||
client_ids: string[];
|
||||
}
|
||||
|
||||
export type FileSearchResultItem = FileInfo & {
|
||||
channel_id: string;
|
||||
}
|
||||
|
||||
export type FileSearchResults = {
|
||||
order: Array<FileSearchResultItem['id']>;
|
||||
file_infos: Map<string, FileSearchResultItem>;
|
||||
next_file_info_id: string;
|
||||
prev_file_info_id: string;
|
||||
};
|
||||
19
webapp/platform/types/src/general.ts
Обычный файл
19
webapp/platform/types/src/general.ts
Обычный файл
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {ClientConfig, ClientLicense, WarnMetricStatus} from './config';
|
||||
|
||||
export type GeneralState = {
|
||||
config: Partial<ClientConfig>;
|
||||
dataRetentionPolicy: any;
|
||||
firstAdminVisitMarketplaceStatus: boolean;
|
||||
firstAdminCompleteSetup: boolean;
|
||||
license: ClientLicense;
|
||||
serverVersion: string;
|
||||
warnMetricsStatus: Record<string, WarnMetricStatus>;
|
||||
};
|
||||
|
||||
export type SystemSetting = {
|
||||
name: string;
|
||||
value: string;
|
||||
};
|
||||
164
webapp/platform/types/src/gifs.ts
Обычный файл
164
webapp/platform/types/src/gifs.ts
Обычный файл
@@ -0,0 +1,164 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type GifsState = {
|
||||
app: GifsAppState;
|
||||
cache: GifsCacheState;
|
||||
categories: GifsCategoriesState;
|
||||
search: GifsSearchState;
|
||||
};
|
||||
|
||||
export type GifsAppState = {
|
||||
appClassName: string;
|
||||
appId: string;
|
||||
appName: string;
|
||||
basePath: string;
|
||||
enableHistory: boolean;
|
||||
header: {
|
||||
tabs: number[];
|
||||
displayText: boolean;
|
||||
};
|
||||
itemTapType: number;
|
||||
shareEvent: string;
|
||||
}
|
||||
|
||||
type GifsCacheState = {
|
||||
gifs: Record<string, GfycatAPIItem>;
|
||||
updating: boolean;
|
||||
}
|
||||
|
||||
type GifsCategoriesState = {
|
||||
cursor: string;
|
||||
hasMore: boolean;
|
||||
isFetching: boolean;
|
||||
tagsDict: Record<string, boolean>;
|
||||
tagsList: GfycatAPITag[];
|
||||
}
|
||||
|
||||
type GifsSearchState = {
|
||||
priorLocation: string | null;
|
||||
resultsByTerm: Record<string, GifsResult>;
|
||||
scrollPosition: number;
|
||||
searchBarText: string;
|
||||
searchText: string;
|
||||
}
|
||||
|
||||
export type GifsResult = GfycatAPIPaginatedResponse & {
|
||||
count: number;
|
||||
currentPage: number;
|
||||
didInvalidate: boolean;
|
||||
found: number;
|
||||
isFetching: boolean;
|
||||
items: string[];
|
||||
moreRemaining: boolean;
|
||||
pages: Record<number, string[]>;
|
||||
start: number;
|
||||
}
|
||||
|
||||
export interface GfycatAPIPaginatedResponse {
|
||||
cursor?: string;
|
||||
gfycats: GfycatAPIItem[];
|
||||
totalCount?: number;
|
||||
}
|
||||
|
||||
export interface GfycatAPIItemResponse {
|
||||
gfyItem: GfycatAPIItem;
|
||||
}
|
||||
|
||||
export interface GfycatAPIItem {
|
||||
anonymous?: boolean;
|
||||
avgColor: string;
|
||||
captionsUrl?: null;
|
||||
content_urls: { [key: string]: GfycatAPIContent };
|
||||
createDate: number;
|
||||
description?: string;
|
||||
dislikes?: number;
|
||||
domainWhitelist?: any[];
|
||||
duration?: number;
|
||||
encoding?: boolean;
|
||||
extraLemmas?: string;
|
||||
finished?: boolean;
|
||||
frameRate: number;
|
||||
gatekeeper: number;
|
||||
geoWhitelist?: any[];
|
||||
gfyId: string;
|
||||
gfyName: string;
|
||||
gfyNumber?: string;
|
||||
gfySlug?: string;
|
||||
gif100px?: string;
|
||||
gifSize?: number;
|
||||
gifUrl: string;
|
||||
hasAudio: boolean;
|
||||
hasTransparency: boolean;
|
||||
height: number;
|
||||
languageCategories: string[];
|
||||
languageText?: string;
|
||||
likes: number;
|
||||
max1mbGif?: string;
|
||||
max2mbGif: string;
|
||||
max5mbGif: string;
|
||||
md5?: string;
|
||||
miniPosterUrl: string;
|
||||
miniUrl?: string;
|
||||
mobileHeight?: number;
|
||||
mobilePosterUrl?: string;
|
||||
mobileUrl: string;
|
||||
mobileWidth?: number;
|
||||
mp4Size?: number;
|
||||
mp4Url: string;
|
||||
nsfw: boolean | number;
|
||||
numFrames: number;
|
||||
posterUrl: string;
|
||||
published: number;
|
||||
rating?: string;
|
||||
ratio?: null;
|
||||
sitename?: string;
|
||||
source?: number;
|
||||
tags: string[];
|
||||
thumb100PosterUrl: string;
|
||||
title?: string;
|
||||
type?: number;
|
||||
url?: string;
|
||||
userData?: GfycatAPIUser | [];
|
||||
userDisplayName?: string;
|
||||
userName?: string;
|
||||
username?: string;
|
||||
userProfileImageUrl?: string;
|
||||
views: number;
|
||||
views5?: number;
|
||||
webmSize?: number;
|
||||
webmUrl?: string;
|
||||
webpUrl?: string;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export interface GfycatAPIContent {
|
||||
width: number;
|
||||
size: number;
|
||||
url: string;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export interface GfycatAPIUser {
|
||||
createDate?: number;
|
||||
description?: string;
|
||||
followers: number;
|
||||
following: number;
|
||||
iframeProfileImageVisible?: boolean;
|
||||
name: string;
|
||||
profileImageUrl: string;
|
||||
profileUrl?: string;
|
||||
publishedGfycats?: number;
|
||||
subscription?: number;
|
||||
url?: string;
|
||||
userid?: string;
|
||||
username: string;
|
||||
verified: boolean;
|
||||
views: number;
|
||||
}
|
||||
|
||||
export interface GfycatAPITag {
|
||||
tagName: string;
|
||||
gfyId: string;
|
||||
}
|
||||
|
||||
172
webapp/platform/types/src/groups.ts
Обычный файл
172
webapp/platform/types/src/groups.ts
Обычный файл
@@ -0,0 +1,172 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {UserProfile} from './users';
|
||||
|
||||
import {RelationOneToOne} from './utilities';
|
||||
|
||||
export enum SyncableType {
|
||||
Team = 'team',
|
||||
Channel = 'channel'
|
||||
}
|
||||
|
||||
export type SyncablePatch = {
|
||||
scheme_admin: boolean;
|
||||
auto_add: boolean;
|
||||
};
|
||||
|
||||
export type GroupPatch = {
|
||||
allow_reference: boolean;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
export type CustomGroupPatch = {
|
||||
name: string;
|
||||
display_name: string;
|
||||
};
|
||||
|
||||
export type Group = {
|
||||
id: string;
|
||||
name: string;
|
||||
display_name: string;
|
||||
description: string;
|
||||
source: string;
|
||||
remote_id: string | null;
|
||||
create_at: number;
|
||||
update_at: number;
|
||||
delete_at: number;
|
||||
has_syncables: boolean;
|
||||
member_count: number;
|
||||
scheme_admin: boolean;
|
||||
allow_reference: boolean;
|
||||
channel_member_count?: number;
|
||||
channel_member_timezones_count?: number;
|
||||
};
|
||||
|
||||
export enum GroupSource {
|
||||
Ldap = 'ldap',
|
||||
Custom = 'custom',
|
||||
}
|
||||
|
||||
export type GroupTeam = {
|
||||
team_id: string;
|
||||
team_display_name: string;
|
||||
team_type?: string;
|
||||
group_id?: string;
|
||||
auto_add?: boolean;
|
||||
scheme_admin?: boolean;
|
||||
create_at?: number;
|
||||
delete_at?: number;
|
||||
update_at?: number;
|
||||
};
|
||||
|
||||
export type GroupChannel = {
|
||||
channel_id: string;
|
||||
channel_display_name: string;
|
||||
channel_type?: string;
|
||||
team_id: string;
|
||||
team_display_name: string;
|
||||
team_type?: string;
|
||||
group_id?: string;
|
||||
auto_add?: boolean;
|
||||
scheme_admin?: boolean;
|
||||
create_at?: number;
|
||||
delete_at?: number;
|
||||
update_at?: number;
|
||||
};
|
||||
|
||||
export type GroupSyncable = {
|
||||
group_id: string;
|
||||
|
||||
auto_add: boolean;
|
||||
scheme_admin: boolean;
|
||||
create_at: number;
|
||||
delete_at: number;
|
||||
update_at: number;
|
||||
type: 'Team' | 'Channel';
|
||||
};
|
||||
|
||||
export type GroupSyncablesState = {
|
||||
teams: GroupTeam[];
|
||||
channels: GroupChannel[];
|
||||
};
|
||||
|
||||
export type GroupsState = {
|
||||
syncables: Record<string, GroupSyncablesState>;
|
||||
stats: RelationOneToOne<Group, GroupStats>;
|
||||
groups: Record<string, Group>;
|
||||
myGroups: string[];
|
||||
};
|
||||
|
||||
export type GroupStats = {
|
||||
group_id: string;
|
||||
total_member_count: number;
|
||||
};
|
||||
|
||||
export type GroupSearchOpts = {
|
||||
q: string;
|
||||
is_linked?: boolean;
|
||||
is_configured?: boolean;
|
||||
};
|
||||
|
||||
export type MixedUnlinkedGroup = {
|
||||
mattermost_group_id?: string;
|
||||
name: string;
|
||||
primary_key: string;
|
||||
has_syncables?: boolean;
|
||||
};
|
||||
|
||||
export type MixedUnlinkedGroupRedux = MixedUnlinkedGroup & {
|
||||
failed?: boolean;
|
||||
};
|
||||
|
||||
export type UserWithGroup = UserProfile & {
|
||||
groups: Group[];
|
||||
scheme_guest: boolean;
|
||||
scheme_user: boolean;
|
||||
scheme_admin: boolean;
|
||||
};
|
||||
|
||||
export type GroupsWithCount = {
|
||||
groups: Group[];
|
||||
total_group_count: number;
|
||||
|
||||
// These fields are added by the client after the groups are returned by the server
|
||||
channelID?: string;
|
||||
teamID?: string;
|
||||
}
|
||||
|
||||
export type UsersWithGroupsAndCount = {
|
||||
users: UserWithGroup[];
|
||||
total_count: number;
|
||||
};
|
||||
|
||||
export type GroupCreateWithUserIds = {
|
||||
name: string;
|
||||
allow_reference: boolean;
|
||||
display_name: string;
|
||||
source: string;
|
||||
user_ids: string[];
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export type GroupSearachParams = {
|
||||
q: string;
|
||||
filter_allow_reference: boolean;
|
||||
page: number;
|
||||
per_page: number;
|
||||
include_member_count: boolean;
|
||||
user_id?: string;
|
||||
include_timezones?: string;
|
||||
include_channel_member_count?: string;
|
||||
}
|
||||
|
||||
export type GroupMembership = {
|
||||
user_id: string;
|
||||
roles: string;
|
||||
}
|
||||
|
||||
export type GroupPermissions = {
|
||||
can_delete: boolean;
|
||||
can_manage_members: boolean;
|
||||
}
|
||||
76
webapp/platform/types/src/hosted_customer.ts
Обычный файл
76
webapp/platform/types/src/hosted_customer.ts
Обычный файл
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {Address, Product, Invoice} from './cloud';
|
||||
import {ValueOf} from './utilities';
|
||||
|
||||
export const SelfHostedSignupProgress = {
|
||||
START: 'START',
|
||||
CREATED_CUSTOMER: 'CREATED_CUSTOMER',
|
||||
CREATED_INTENT: 'CREATED_INTENT',
|
||||
CONFIRMED_INTENT: 'CONFIRMED_INTENT',
|
||||
CREATED_SUBSCRIPTION: 'CREATED_SUBSCRIPTION',
|
||||
PAID: 'PAID',
|
||||
CREATED_LICENSE: 'CREATED_LICENSE',
|
||||
} as const;
|
||||
|
||||
export interface SelfHostedSignupForm {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
billing_address: Address;
|
||||
organization: string;
|
||||
}
|
||||
|
||||
export interface SelfHostedSignupBootstrapResponse {
|
||||
progress: ValueOf<typeof SelfHostedSignupProgress>;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface SelfHostedSignupCustomerResponse {
|
||||
customer_id: string;
|
||||
setup_intent_id: string;
|
||||
setup_intent_secret: string;
|
||||
progress: ValueOf<typeof SelfHostedSignupProgress>;
|
||||
}
|
||||
|
||||
export interface SelfHostedSignupSuccessResponse {
|
||||
progress: ValueOf<typeof SelfHostedSignupProgress>;
|
||||
license: Record<string, string>;
|
||||
}
|
||||
|
||||
export type HostedCustomerState = {
|
||||
products: {
|
||||
products: Record<string, Product>;
|
||||
productsLoaded: boolean;
|
||||
};
|
||||
invoices: {
|
||||
invoices: Record<string, Invoice>;
|
||||
invoicesLoaded: boolean;
|
||||
};
|
||||
errors: {
|
||||
products?: true;
|
||||
invoices?: true;
|
||||
trueUpReview?: true;
|
||||
};
|
||||
signupProgress: ValueOf<typeof SelfHostedSignupProgress>;
|
||||
trueUpReviewStatus: TrueUpReviewStatusReducer;
|
||||
trueUpReviewProfile: TrueUpReviewProfileReducer;
|
||||
}
|
||||
|
||||
export type TrueUpReviewProfile = {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export type TrueUpReviewStatus = {
|
||||
due_date: number;
|
||||
complete: boolean;
|
||||
}
|
||||
|
||||
type RequestState = 'IDLE' | 'LOADING' | 'OK'
|
||||
export interface TrueUpReviewProfileReducer extends TrueUpReviewProfile {
|
||||
getRequestState: RequestState;
|
||||
}
|
||||
|
||||
export interface TrueUpReviewStatusReducer extends TrueUpReviewStatus {
|
||||
getRequestState: RequestState;
|
||||
}
|
||||
183
webapp/platform/types/src/insights.ts
Обычный файл
183
webapp/platform/types/src/insights.ts
Обычный файл
@@ -0,0 +1,183 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {ChannelType} from './channels';
|
||||
import {Post} from './posts';
|
||||
import {UserProfile} from './users';
|
||||
|
||||
export enum InsightsWidgetTypes {
|
||||
TOP_CHANNELS = 'TOP_CHANNELS',
|
||||
TOP_REACTIONS = 'TOP_REACTIONS',
|
||||
TOP_THREADS = 'TOP_THREADS',
|
||||
TOP_BOARDS = 'TOP_BOARDS',
|
||||
LEAST_ACTIVE_CHANNELS = 'LEAST_ACTIVE_CHANNELS',
|
||||
TOP_PLAYBOOKS = 'TOP_PLAYBOOKS',
|
||||
TOP_DMS = 'TOP_DMS',
|
||||
NEW_TEAM_MEMBERS = 'NEW_TEAM_MEMBERS',
|
||||
}
|
||||
|
||||
export enum CardSizes {
|
||||
large = 'lg',
|
||||
medium = 'md',
|
||||
small = 'sm',
|
||||
}
|
||||
export type CardSize = CardSizes;
|
||||
|
||||
export enum TimeFrames {
|
||||
INSIGHTS_1_DAY = 'today',
|
||||
INSIGHTS_7_DAYS = '7_day',
|
||||
INSIGHTS_28_DAYS = '28_day',
|
||||
}
|
||||
|
||||
export type TimeFrame = TimeFrames;
|
||||
|
||||
export type TopReaction = {
|
||||
emoji_name: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export type TopReactionResponse = {
|
||||
has_next: boolean;
|
||||
items: TopReaction[];
|
||||
timeFrame?: TimeFrame;
|
||||
}
|
||||
|
||||
export type TopChannel = {
|
||||
id: string;
|
||||
type: ChannelType;
|
||||
display_name: string;
|
||||
name: string;
|
||||
team_id: string;
|
||||
message_count: number;
|
||||
}
|
||||
|
||||
export type TopChannelGraphData = Record<string, Record<string, number>>;
|
||||
|
||||
export type TopChannelResponse = {
|
||||
has_next: boolean;
|
||||
items: TopChannel[];
|
||||
daily_channel_post_counts: TopChannelGraphData;
|
||||
};
|
||||
|
||||
export type InsightsState = {
|
||||
topReactions: Record<string, Record<TimeFrame, Record<string, TopReaction>>>;
|
||||
myTopReactions: Record<string, Record<TimeFrame, Record<string, TopReaction>>>;
|
||||
}
|
||||
|
||||
export type TopChannelActionResult = {
|
||||
data?: TopChannelResponse;
|
||||
error?: any;
|
||||
};
|
||||
|
||||
export type TopThread = {
|
||||
channel_id: string;
|
||||
channel_display_name: string;
|
||||
channel_name: string;
|
||||
participants: string[];
|
||||
user_information: {
|
||||
id: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
last_picture_update: number;
|
||||
};
|
||||
post: Post;
|
||||
};
|
||||
|
||||
export type TopThreadResponse = {
|
||||
has_next: boolean;
|
||||
items: TopThread[];
|
||||
};
|
||||
|
||||
export type TopThreadActionResult = {
|
||||
data?: TopThreadResponse;
|
||||
error?: any;
|
||||
};
|
||||
|
||||
export type TopBoard = {
|
||||
boardID: string;
|
||||
icon: string;
|
||||
title: string;
|
||||
activityCount: number;
|
||||
|
||||
// MM-49023: community bugfix to maintain backwards compatibility
|
||||
activeUsers: Array<UserProfile['id']> | string;
|
||||
createdBy: string;
|
||||
};
|
||||
|
||||
export type TopBoardResponse = {
|
||||
has_next: boolean;
|
||||
items: TopBoard[];
|
||||
};
|
||||
|
||||
export type LeastActiveChannel = {
|
||||
id: string;
|
||||
display_name: string;
|
||||
name: string;
|
||||
participants: string[];
|
||||
last_activity_at: number;
|
||||
type: ChannelType;
|
||||
team_id: string;
|
||||
message_count: number;
|
||||
};
|
||||
|
||||
export type LeastActiveChannelsResponse = {
|
||||
has_next: boolean;
|
||||
items: LeastActiveChannel[];
|
||||
};
|
||||
|
||||
export type LeastActiveChannelsActionResult = {
|
||||
data?: LeastActiveChannelsResponse;
|
||||
error?: any;
|
||||
};
|
||||
export type TopPlaybook = {
|
||||
playbook_id: string;
|
||||
num_runs: number;
|
||||
title: string;
|
||||
last_run_at: number;
|
||||
};
|
||||
|
||||
export type TopPlaybookResponse = {
|
||||
has_next: boolean;
|
||||
items: TopPlaybook[];
|
||||
};
|
||||
|
||||
type MinUserProfile = {
|
||||
id: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
last_picture_update: number;
|
||||
nickname: string;
|
||||
position: string;
|
||||
username: string;
|
||||
};
|
||||
|
||||
export type TopDM = {
|
||||
outgoing_message_count: number;
|
||||
post_count: number;
|
||||
second_participant: MinUserProfile;
|
||||
};
|
||||
|
||||
export type TopDMsResponse = {
|
||||
has_next: boolean;
|
||||
items: TopDM[];
|
||||
};
|
||||
|
||||
export type TopDMsActionResult = {
|
||||
data?: TopDMsResponse;
|
||||
error?: any;
|
||||
};
|
||||
|
||||
export type NewMember = MinUserProfile & {
|
||||
create_at: number;
|
||||
};
|
||||
|
||||
export type NewMembersResponse = {
|
||||
has_next: boolean;
|
||||
items: NewMember[];
|
||||
total_count: number;
|
||||
};
|
||||
|
||||
export type NewMembersActionResult = {
|
||||
data?: NewMembersResponse;
|
||||
error?: any;
|
||||
};
|
||||
30
webapp/platform/types/src/integration_actions.ts
Обычный файл
30
webapp/platform/types/src/integration_actions.ts
Обычный файл
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type PostAction = {
|
||||
id?: string;
|
||||
type?: string;
|
||||
name?: string;
|
||||
disabled?: boolean;
|
||||
style?: string;
|
||||
data_source?: string;
|
||||
options?: PostActionOption[];
|
||||
default_option?: string;
|
||||
integration?: PostActionIntegration;
|
||||
cookie?: string;
|
||||
};
|
||||
|
||||
export type PostActionOption = {
|
||||
text: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type PostActionIntegration = {
|
||||
url?: string;
|
||||
context?: Record<string, any>;
|
||||
}
|
||||
|
||||
export type PostActionResponse = {
|
||||
status: string;
|
||||
trigger_id: string;
|
||||
};
|
||||
151
webapp/platform/types/src/integrations.ts
Обычный файл
151
webapp/platform/types/src/integrations.ts
Обычный файл
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {MessageAttachment} from './message_attachments';
|
||||
import {IDMappedObjects} from './utilities';
|
||||
|
||||
export type IncomingWebhook = {
|
||||
id: string;
|
||||
create_at: number;
|
||||
update_at: number;
|
||||
delete_at: number;
|
||||
user_id: string;
|
||||
channel_id: string;
|
||||
team_id: string;
|
||||
display_name: string;
|
||||
description: string;
|
||||
username: string;
|
||||
icon_url: string;
|
||||
channel_locked: boolean;
|
||||
};
|
||||
|
||||
export type OutgoingWebhook = {
|
||||
id: string;
|
||||
token: string;
|
||||
create_at: number;
|
||||
update_at: number;
|
||||
delete_at: number;
|
||||
creator_id: string;
|
||||
channel_id: string;
|
||||
team_id: string;
|
||||
trigger_words: string[];
|
||||
trigger_when: number;
|
||||
callback_urls: string[];
|
||||
display_name: string;
|
||||
description: string;
|
||||
content_type: string;
|
||||
username: string;
|
||||
icon_url: string;
|
||||
};
|
||||
|
||||
export type Command = {
|
||||
'id': string;
|
||||
'token': string;
|
||||
'create_at': number;
|
||||
'update_at': number;
|
||||
'delete_at': number;
|
||||
'creator_id': string;
|
||||
'team_id': string;
|
||||
'trigger': string;
|
||||
'method': 'P' | 'G' | '';
|
||||
'username': string;
|
||||
'icon_url': string;
|
||||
'auto_complete': boolean;
|
||||
'auto_complete_desc': string;
|
||||
'auto_complete_hint': string;
|
||||
'display_name': string;
|
||||
'description': string;
|
||||
'url': string;
|
||||
};
|
||||
|
||||
export type CommandArgs = {
|
||||
channel_id: string;
|
||||
team_id?: string;
|
||||
root_id?: string;
|
||||
}
|
||||
|
||||
export type CommandResponse = {
|
||||
response_type: string;
|
||||
text: string;
|
||||
username: string;
|
||||
channel_id: SVGAnimatedString;
|
||||
icon_url: string;
|
||||
type: string;
|
||||
props: Record<string, any>;
|
||||
goto_location: string;
|
||||
trigger_id: string;
|
||||
skip_slack_parsing: boolean;
|
||||
attachments: MessageAttachment[];
|
||||
extra_responses: CommandResponse[];
|
||||
};
|
||||
|
||||
export type AutocompleteSuggestion = {
|
||||
Complete: string;
|
||||
Suggestion: string;
|
||||
Hint: string;
|
||||
Description: string;
|
||||
IconData: string;
|
||||
type?: string;
|
||||
};
|
||||
|
||||
export type CommandAutocompleteSuggestion = AutocompleteSuggestion; // TODO remove this alias after the mattermost-redux migration
|
||||
|
||||
export type OAuthApp = {
|
||||
'id': string;
|
||||
'creator_id': string;
|
||||
'create_at': number;
|
||||
'update_at': number;
|
||||
'client_secret': string;
|
||||
'name': string;
|
||||
'description': string;
|
||||
'icon_url': string;
|
||||
'callback_urls': string[];
|
||||
'homepage': string;
|
||||
'is_trusted': boolean;
|
||||
};
|
||||
|
||||
export type IntegrationsState = {
|
||||
incomingHooks: IDMappedObjects<IncomingWebhook>;
|
||||
outgoingHooks: IDMappedObjects<OutgoingWebhook>;
|
||||
oauthApps: IDMappedObjects<OAuthApp>;
|
||||
appsOAuthAppIDs: string[];
|
||||
appsBotIDs: string[];
|
||||
systemCommands: IDMappedObjects<Command>;
|
||||
commands: IDMappedObjects<Command>;
|
||||
};
|
||||
|
||||
export type DialogSubmission = {
|
||||
url: string;
|
||||
callback_id: string;
|
||||
state: string;
|
||||
user_id: string;
|
||||
channel_id: string;
|
||||
team_id: string;
|
||||
submission: {
|
||||
[x: string]: string;
|
||||
};
|
||||
cancelled: boolean;
|
||||
};
|
||||
|
||||
export type DialogElement = {
|
||||
display_name: string;
|
||||
name: string;
|
||||
type: string;
|
||||
subtype: string;
|
||||
default: string;
|
||||
placeholder: string;
|
||||
help_text: string;
|
||||
optional: boolean;
|
||||
min_length: number;
|
||||
max_length: number;
|
||||
data_source: string;
|
||||
options: Array<{
|
||||
text: string;
|
||||
value: any;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type SubmitDialogResponse = {
|
||||
error?: string;
|
||||
errors?: Record<string, string>;
|
||||
};
|
||||
27
webapp/platform/types/src/jobs.ts
Обычный файл
27
webapp/platform/types/src/jobs.ts
Обычный файл
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {IDMappedObjects} from './utilities';
|
||||
|
||||
export type JobType = 'data_retention' | 'elasticsearch_post_indexing' | 'ldap_sync' | 'message_export';
|
||||
export type JobStatus = 'pending' | 'in_progress' | 'success' | 'error' | 'cancel_requested' | 'canceled' | 'warning';
|
||||
export type Job = JobTypeBase & {
|
||||
id: string;
|
||||
priority: number;
|
||||
create_at: number;
|
||||
start_at: number;
|
||||
last_activity_at: number;
|
||||
status: JobStatus;
|
||||
progress: number;
|
||||
data: any;
|
||||
};
|
||||
export type JobsByType = {
|
||||
[x in JobType]?: Job[];
|
||||
};
|
||||
export type JobsState = {
|
||||
jobs: IDMappedObjects<Job>;
|
||||
jobsByTypeList: JobsByType;
|
||||
};
|
||||
export type JobTypeBase = {
|
||||
type: JobType;
|
||||
}
|
||||
51
webapp/platform/types/src/marketplace.ts
Обычный файл
51
webapp/platform/types/src/marketplace.ts
Обычный файл
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {PluginManifest} from './plugins';
|
||||
import {AppManifest} from './apps';
|
||||
|
||||
export type MarketplaceLabel = {
|
||||
name: string;
|
||||
description?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export enum HostingType {
|
||||
OnPrem = 'on-prem',
|
||||
Cloud = 'cloud',
|
||||
}
|
||||
|
||||
export enum AuthorType {
|
||||
Mattermost = 'mattermost',
|
||||
Partner = 'partner',
|
||||
Community = 'community',
|
||||
}
|
||||
|
||||
export enum ReleaseStage {
|
||||
Production = 'production',
|
||||
Beta = 'beta',
|
||||
Experimental = 'experimental',
|
||||
}
|
||||
|
||||
interface MarketplaceBaseItem {
|
||||
labels?: MarketplaceLabel[];
|
||||
hosting?: HostingType;
|
||||
author_type: AuthorType;
|
||||
release_stage: ReleaseStage;
|
||||
enterprise: boolean;
|
||||
}
|
||||
|
||||
export interface MarketplacePlugin extends MarketplaceBaseItem {
|
||||
manifest: PluginManifest;
|
||||
icon_data?: string;
|
||||
homepage_url?: string;
|
||||
download_url?: string;
|
||||
release_notes_url?: string;
|
||||
installed_version?: string;
|
||||
}
|
||||
|
||||
export interface MarketplaceApp extends MarketplaceBaseItem {
|
||||
manifest: AppManifest;
|
||||
installed: boolean;
|
||||
icon_url?: string;
|
||||
}
|
||||
30
webapp/platform/types/src/message_attachments.ts
Обычный файл
30
webapp/platform/types/src/message_attachments.ts
Обычный файл
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {PostAction} from './integration_actions';
|
||||
|
||||
export type MessageAttachment = {
|
||||
id: number;
|
||||
fallback: string;
|
||||
color: string;
|
||||
pretext: string;
|
||||
author_name: string;
|
||||
author_link: string;
|
||||
author_icon: string;
|
||||
title: string;
|
||||
title_link: string;
|
||||
text: string;
|
||||
fields: MessageAttachmentField[];
|
||||
image_url: string;
|
||||
thumb_url: string;
|
||||
footer: string;
|
||||
footer_icon: string;
|
||||
timestamp: number | string;
|
||||
actions?: PostAction[];
|
||||
};
|
||||
|
||||
export type MessageAttachmentField = {
|
||||
title: string;
|
||||
value: any;
|
||||
short: boolean;
|
||||
}
|
||||
7
webapp/platform/types/src/mfa.ts
Обычный файл
7
webapp/platform/types/src/mfa.ts
Обычный файл
@@ -0,0 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type MfaSecret = {
|
||||
secret: string;
|
||||
qr_code: string;
|
||||
};
|
||||
137
webapp/platform/types/src/plugins.ts
Обычный файл
137
webapp/platform/types/src/plugins.ts
Обычный файл
@@ -0,0 +1,137 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type PluginManifest = {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
homepage_url?: string;
|
||||
support_url?: string;
|
||||
release_notes_url?: string;
|
||||
icon_path?: string;
|
||||
version: string;
|
||||
min_server_version?: string;
|
||||
translate?: boolean;
|
||||
server?: PluginManifestServer;
|
||||
backend?: PluginManifestServer;
|
||||
webapp?: PluginManifestWebapp;
|
||||
settings_schema?: PluginSettingsSchema;
|
||||
props?: Record<string, any>;
|
||||
};
|
||||
|
||||
export type PluginRedux = PluginManifest & {active: boolean};
|
||||
|
||||
export type PluginManifestServer = {
|
||||
executables?: {
|
||||
'linux-amd64'?: string;
|
||||
'darwin-amd64'?: string;
|
||||
'windows-amd64'?: string;
|
||||
};
|
||||
executable: string;
|
||||
};
|
||||
|
||||
export type PluginManifestWebapp = {
|
||||
bundle_path: string;
|
||||
};
|
||||
|
||||
export type PluginSettingsSchema = {
|
||||
header: string;
|
||||
footer: string;
|
||||
settings: PluginSetting[];
|
||||
};
|
||||
|
||||
export type PluginSetting = {
|
||||
key: string;
|
||||
display_name: string;
|
||||
type: string;
|
||||
help_text: string;
|
||||
regenerate_help_text?: string;
|
||||
placeholder: string;
|
||||
default: any;
|
||||
options?: PluginSettingOption[];
|
||||
hosting?: 'on-prem' | 'cloud';
|
||||
};
|
||||
|
||||
export type PluginSettingOption = {
|
||||
display_name: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type PluginsResponse = {
|
||||
active: PluginManifest[];
|
||||
inactive: PluginManifest[];
|
||||
};
|
||||
|
||||
export type PluginStatus = {
|
||||
plugin_id: string;
|
||||
cluster_id: string;
|
||||
plugin_path: string;
|
||||
state: number;
|
||||
name: string;
|
||||
description: string;
|
||||
version: string;
|
||||
};
|
||||
|
||||
type PluginInstance = {
|
||||
cluster_id: string;
|
||||
version: string;
|
||||
state: number;
|
||||
}
|
||||
|
||||
export type PluginStatusRedux = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
version: string;
|
||||
active: boolean;
|
||||
state: number;
|
||||
error?: string;
|
||||
instances: PluginInstance[];
|
||||
}
|
||||
|
||||
export type ClientPluginManifest = {
|
||||
id: string;
|
||||
min_server_version?: string;
|
||||
version: string;
|
||||
webapp: {
|
||||
bundle_path: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type MarketplaceLabel = { // TODO remove this in favour of the definition in types/marketplace after the mattermost-redux migration
|
||||
name: string;
|
||||
description?: string;
|
||||
url?: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export enum HostingType { // TODO remove this in favour of the definition in types/marketplace after the mattermost-redux migration
|
||||
OnPrem = 'on-prem',
|
||||
Cloud = 'cloud',
|
||||
}
|
||||
|
||||
export enum AuthorType { // TODO remove this in favour of the definition in types/marketplace after the mattermost-redux migration
|
||||
Mattermost = 'mattermost',
|
||||
Partner = 'partner',
|
||||
Community = 'community',
|
||||
}
|
||||
|
||||
export enum ReleaseStage { // TODO remove this in favour of the definition in types/marketplace after the mattermost-redux migration
|
||||
Production = 'production',
|
||||
Beta = 'beta',
|
||||
Experimental = 'experimental',
|
||||
}
|
||||
|
||||
export type MarketplacePlugin = { // TODO remove this in favour of the definition in types/marketplace after the mattermost-redux migration
|
||||
homepage_url?: string;
|
||||
icon_data?: string;
|
||||
download_url?: string;
|
||||
release_notes_url?: string;
|
||||
labels?: MarketplaceLabel[];
|
||||
hosting?: HostingType;
|
||||
author_type: AuthorType;
|
||||
release_stage: ReleaseStage;
|
||||
enterprise: boolean;
|
||||
manifest: PluginManifest;
|
||||
installed_version?: string;
|
||||
}
|
||||
206
webapp/platform/types/src/posts.ts
Обычный файл
206
webapp/platform/types/src/posts.ts
Обычный файл
@@ -0,0 +1,206 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Channel, ChannelType} from './channels';
|
||||
import {CustomEmoji} from './emojis';
|
||||
import {FileInfo} from './files';
|
||||
import {Reaction} from './reactions';
|
||||
import {UserProfile} from './users';
|
||||
import {
|
||||
RelationOneToOne,
|
||||
RelationOneToMany,
|
||||
IDMappedObjects,
|
||||
} from './utilities';
|
||||
|
||||
export type PostType = 'system_add_remove' |
|
||||
'system_add_to_channel' |
|
||||
'system_add_to_team' |
|
||||
'system_channel_deleted' |
|
||||
'system_channel_restored' |
|
||||
'system_displayname_change' |
|
||||
'system_convert_channel' |
|
||||
'system_ephemeral' |
|
||||
'system_header_change' |
|
||||
'system_join_channel' |
|
||||
'system_join_leave' |
|
||||
'system_leave_channel' |
|
||||
'system_purpose_change' |
|
||||
'system_remove_from_channel' |
|
||||
'system_combined_user_activity' |
|
||||
'system_fake_parent_deleted' |
|
||||
'system_generic' |
|
||||
'reminder' |
|
||||
'';
|
||||
|
||||
export type PostEmbedType = 'image' | 'link' | 'message_attachment' | 'opengraph' | 'permalink';
|
||||
|
||||
export type PostEmbed = {
|
||||
type: PostEmbedType;
|
||||
url: string;
|
||||
data?: OpenGraphMetadata | PostPreviewMetadata;
|
||||
};
|
||||
|
||||
export type PostImage = {
|
||||
format: string;
|
||||
frameCount: number;
|
||||
height: number;
|
||||
width: number;
|
||||
};
|
||||
|
||||
export type PostAcknowledgement = {
|
||||
post_id: Post['id'];
|
||||
user_id: UserProfile['id'];
|
||||
acknowledged_at: number;
|
||||
}
|
||||
|
||||
export type PostPriorityMetadata = {
|
||||
priority: PostPriority|'';
|
||||
requested_ack?: boolean;
|
||||
persistent_notifications?: boolean;
|
||||
}
|
||||
|
||||
export type PostMetadata = {
|
||||
embeds: PostEmbed[];
|
||||
emojis: CustomEmoji[];
|
||||
files: FileInfo[];
|
||||
images: Record<string, PostImage>;
|
||||
reactions: Reaction[];
|
||||
priority?: PostPriorityMetadata;
|
||||
acknowledgements?: PostAcknowledgement[];
|
||||
};
|
||||
|
||||
export type Post = {
|
||||
id: string;
|
||||
create_at: number;
|
||||
update_at: number;
|
||||
edit_at: number;
|
||||
delete_at: number;
|
||||
is_pinned: boolean;
|
||||
user_id: string;
|
||||
channel_id: string;
|
||||
root_id: string;
|
||||
original_id: string;
|
||||
message: string;
|
||||
type: PostType;
|
||||
props: Record<string, any>;
|
||||
hashtags: string;
|
||||
pending_post_id: string;
|
||||
reply_count: number;
|
||||
file_ids?: string[];
|
||||
metadata: PostMetadata;
|
||||
failed?: boolean;
|
||||
user_activity_posts?: Post[];
|
||||
state?: PostState;
|
||||
filenames?: string[];
|
||||
last_reply_at?: number;
|
||||
participants?: any; //Array<UserProfile | UserProfile['id']>;
|
||||
message_source?: string;
|
||||
is_following?: boolean;
|
||||
exists?: boolean;
|
||||
};
|
||||
|
||||
export type PostState = 'DELETED';
|
||||
|
||||
export enum PostPriority {
|
||||
URGENT = 'urgent',
|
||||
IMPORTANT = 'important',
|
||||
}
|
||||
|
||||
export type PostList = {
|
||||
order: Array<Post['id']>;
|
||||
posts: Record<string, Post>;
|
||||
next_post_id: string;
|
||||
prev_post_id: string;
|
||||
first_inaccessible_post_time: number;
|
||||
};
|
||||
|
||||
export type PaginatedPostList = PostList & {
|
||||
has_next: boolean;
|
||||
}
|
||||
|
||||
export type PostSearchResults = PostList & {
|
||||
matches: RelationOneToOne<Post, string[]>;
|
||||
};
|
||||
|
||||
export type PostOrderBlock = {
|
||||
order: string[];
|
||||
recent?: boolean;
|
||||
oldest?: boolean;
|
||||
};
|
||||
|
||||
export type MessageHistory = {
|
||||
messages: string[];
|
||||
index: {
|
||||
post: number;
|
||||
comment: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type PostsState = {
|
||||
posts: IDMappedObjects<Post>;
|
||||
postsReplies: {[x in Post['id']]: number};
|
||||
postsInChannel: Record<string, PostOrderBlock[]>;
|
||||
postsInThread: RelationOneToMany<Post, Post>;
|
||||
reactions: RelationOneToOne<Post, Record<string, Reaction>>;
|
||||
openGraph: RelationOneToOne<Post, Record<string, OpenGraphMetadata>>;
|
||||
pendingPostIds: string[];
|
||||
selectedPostId: string;
|
||||
postEditHistory: Post[];
|
||||
currentFocusedPostId: string;
|
||||
messagesHistory: MessageHistory;
|
||||
expandedURLs: Record<string, string>;
|
||||
limitedViews: {
|
||||
channels: Record<Channel['id'], number>;
|
||||
threads: Record<Post['root_id'], number>;
|
||||
};
|
||||
acknowledgements: RelationOneToOne<Post, Record<UserProfile['id'], number>>;
|
||||
};
|
||||
|
||||
export declare type OpenGraphMetadataImage = {
|
||||
secure_url?: string;
|
||||
url: string;
|
||||
type?: string;
|
||||
height?: number;
|
||||
width?: number;
|
||||
}
|
||||
|
||||
export declare type OpenGraphMetadata = {
|
||||
type?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
site_name?: string;
|
||||
url?: string;
|
||||
images: OpenGraphMetadataImage[];
|
||||
};
|
||||
|
||||
export declare type PostPreviewMetadata = {
|
||||
post_id: string;
|
||||
post?: Post;
|
||||
channel_display_name: string;
|
||||
team_name: string;
|
||||
channel_type: ChannelType;
|
||||
channel_id: string;
|
||||
};
|
||||
|
||||
export declare type PostsUsageResponse = {
|
||||
count: number;
|
||||
};
|
||||
|
||||
export declare type FilesUsageResponse = {
|
||||
bytes: number;
|
||||
};
|
||||
|
||||
export declare type TeamsUsageResponse = {
|
||||
active: number;
|
||||
cloud_archived: number;
|
||||
};
|
||||
|
||||
export type PostAnalytics = {
|
||||
channel_id: string;
|
||||
post_id: string;
|
||||
user_actual_id: string;
|
||||
root_id: string;
|
||||
priority?: PostPriority|'';
|
||||
requested_ack?: boolean;
|
||||
persistent_notifications?: boolean;
|
||||
}
|
||||
13
webapp/platform/types/src/preferences.ts
Обычный файл
13
webapp/platform/types/src/preferences.ts
Обычный файл
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type PreferenceType = {
|
||||
category: string;
|
||||
name: string;
|
||||
user_id: string;
|
||||
value?: string;
|
||||
};
|
||||
|
||||
export type PreferencesType = {
|
||||
[x: string]: PreferenceType;
|
||||
};
|
||||
38
webapp/platform/types/src/product_notices.ts
Обычный файл
38
webapp/platform/types/src/product_notices.ts
Обычный файл
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export enum Action {
|
||||
URL = 'url',
|
||||
}
|
||||
|
||||
export type ProductNotice = {
|
||||
|
||||
/** Unique identifier for this notice. Can be a running number. Used for storing 'viewed' state on the server. */
|
||||
id: string;
|
||||
|
||||
/** Notice title. Use {{Mattermost}} instead of plain text to support white-labeling. Text supports Markdown. */
|
||||
title: string;
|
||||
|
||||
/** Notice content. Use {{Mattermost}} instead of plain text to support white-labeling. Text supports Markdown. */
|
||||
description: string;
|
||||
image?: string;
|
||||
|
||||
/** Optional override for the action button text (defaults to OK) */
|
||||
actionText?: string;
|
||||
|
||||
/** Optional action to perform on action button click. (defaults to closing the notice) */
|
||||
action?: Action;
|
||||
|
||||
/** Optional action parameter.
|
||||
* Example: {"action": "url", actionParam: "/console/some-page"}
|
||||
*/
|
||||
actionParam?: string;
|
||||
|
||||
sysAdminOnly: boolean;
|
||||
teamAdminOnly: boolean;
|
||||
}
|
||||
|
||||
/** List of product notices. Order is important and is used to resolve priorities.
|
||||
* Each notice will only be show if conditions are met.
|
||||
*/
|
||||
export type ProductNotices = ProductNotice[];
|
||||
11
webapp/platform/types/src/products.ts
Обычный файл
11
webapp/platform/types/src/products.ts
Обычный файл
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/**
|
||||
* - `null` - explicitly Channels
|
||||
* - `string` - uuid - any other product
|
||||
*/
|
||||
export type ProductIdentifier = null | string;
|
||||
|
||||
/** @see {@link ProductIdentifier} */
|
||||
export type ProductScope = ProductIdentifier | ProductIdentifier[];
|
||||
9
webapp/platform/types/src/reactions.ts
Обычный файл
9
webapp/platform/types/src/reactions.ts
Обычный файл
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type Reaction = {
|
||||
user_id: string;
|
||||
post_id: string;
|
||||
emoji_name: string;
|
||||
create_at: number;
|
||||
};
|
||||
70
webapp/platform/types/src/requests.ts
Обычный файл
70
webapp/platform/types/src/requests.ts
Обычный файл
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type RequestStatusOption = 'not_started' | 'started' | 'success' | 'failure' | 'cancelled';
|
||||
export type RequestStatusType = {
|
||||
status: RequestStatusOption;
|
||||
error: null | Record<string, any>;
|
||||
};
|
||||
|
||||
export type ChannelsRequestsStatuses = {
|
||||
getChannels: RequestStatusType;
|
||||
getAllChannels: RequestStatusType;
|
||||
myChannels: RequestStatusType;
|
||||
createChannel: RequestStatusType;
|
||||
updateChannel: RequestStatusType;
|
||||
};
|
||||
|
||||
export type GeneralRequestsStatuses = {
|
||||
websocket: RequestStatusType;
|
||||
};
|
||||
|
||||
export type PostsRequestsStatuses = {
|
||||
createPost: RequestStatusType;
|
||||
editPost: RequestStatusType;
|
||||
getPostThread: RequestStatusType;
|
||||
};
|
||||
|
||||
export type ThreadsRequestStatuses = {
|
||||
getThreads: RequestStatusType;
|
||||
};
|
||||
|
||||
export type TeamsRequestsStatuses = {
|
||||
getMyTeams: RequestStatusType;
|
||||
getTeams: RequestStatusType;
|
||||
joinTeam: RequestStatusType;
|
||||
};
|
||||
|
||||
export type UsersRequestsStatuses = {
|
||||
login: RequestStatusType;
|
||||
logout: RequestStatusType;
|
||||
autocompleteUsers: RequestStatusType;
|
||||
updateMe: RequestStatusType;
|
||||
};
|
||||
|
||||
export type AdminRequestsStatuses = {
|
||||
createCompliance: RequestStatusType;
|
||||
};
|
||||
|
||||
export type EmojisRequestsStatuses = {
|
||||
createCustomEmoji: RequestStatusType;
|
||||
getCustomEmojis: RequestStatusType;
|
||||
deleteCustomEmoji: RequestStatusType;
|
||||
getCustomEmoji: RequestStatusType;
|
||||
};
|
||||
|
||||
export type FilesRequestsStatuses = {
|
||||
uploadFiles: RequestStatusType;
|
||||
};
|
||||
|
||||
export type RolesRequestsStatuses = {
|
||||
getRolesByNames: RequestStatusType;
|
||||
getRoleByName: RequestStatusType;
|
||||
getRole: RequestStatusType;
|
||||
editRole: RequestStatusType;
|
||||
};
|
||||
|
||||
export type SearchRequestsStatuses = {
|
||||
flaggedPosts: RequestStatusType;
|
||||
pinnedPosts: RequestStatusType;
|
||||
};
|
||||
15
webapp/platform/types/src/roles.ts
Обычный файл
15
webapp/platform/types/src/roles.ts
Обычный файл
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type Role = {
|
||||
id: string;
|
||||
name: string;
|
||||
display_name: string;
|
||||
description: string;
|
||||
create_at: number;
|
||||
update_at: number;
|
||||
delete_at: number;
|
||||
permissions: string[];
|
||||
scheme_managed: boolean;
|
||||
built_in: boolean;
|
||||
};
|
||||
14
webapp/platform/types/src/saml.ts
Обычный файл
14
webapp/platform/types/src/saml.ts
Обычный файл
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type SamlCertificateStatus = {
|
||||
idp_certificate_file: string;
|
||||
private_key_file: string;
|
||||
public_certificate_file: string;
|
||||
};
|
||||
|
||||
export type SamlMetadataResponse = {
|
||||
idp_descriptor_url: string;
|
||||
idp_url: string;
|
||||
idp_public_certificate: string;
|
||||
};
|
||||
32
webapp/platform/types/src/schemes.ts
Обычный файл
32
webapp/platform/types/src/schemes.ts
Обычный файл
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type SchemeScope = 'team' | 'channel';
|
||||
export type Scheme = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
display_name: string;
|
||||
create_at: number;
|
||||
update_at: number;
|
||||
delete_at: number;
|
||||
scope: SchemeScope;
|
||||
default_team_admin_role: string;
|
||||
default_team_user_role: string;
|
||||
default_team_guest_role: string;
|
||||
default_channel_admin_role: string;
|
||||
default_channel_user_role: string;
|
||||
default_channel_guest_role: string;
|
||||
default_playbook_admin_role: string;
|
||||
default_playbook_member_role: string;
|
||||
default_run_member_role: string;
|
||||
};
|
||||
export type SchemesState = {
|
||||
schemes: {
|
||||
[x: string]: Scheme;
|
||||
};
|
||||
};
|
||||
export type SchemePatch = {
|
||||
name?: string;
|
||||
description?: string;
|
||||
};
|
||||
33
webapp/platform/types/src/search.ts
Обычный файл
33
webapp/platform/types/src/search.ts
Обычный файл
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type Search = {
|
||||
terms: string;
|
||||
isOrSearch: boolean;
|
||||
};
|
||||
|
||||
export type SearchState = {
|
||||
current: any;
|
||||
results: string[];
|
||||
fileResults: string[];
|
||||
flagged: string[];
|
||||
pinned: Record<string, string[]>;
|
||||
isSearchingTerm: boolean;
|
||||
isSearchGettingMore: boolean;
|
||||
isLimitedResults: number;
|
||||
recent: {
|
||||
[x: string]: Search[];
|
||||
};
|
||||
matches: {
|
||||
[x: string]: string[];
|
||||
};
|
||||
};
|
||||
|
||||
export type SearchParameter = {
|
||||
terms: string;
|
||||
is_or_search: boolean;
|
||||
time_zone_offset?: number;
|
||||
page: number;
|
||||
per_page: number;
|
||||
include_deleted_channels: boolean;
|
||||
}
|
||||
19
webapp/platform/types/src/sessions.ts
Обычный файл
19
webapp/platform/types/src/sessions.ts
Обычный файл
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {TeamMembership} from './teams';
|
||||
|
||||
export type Session = {
|
||||
id: string;
|
||||
token: string;
|
||||
create_at: number;
|
||||
expires_at: number;
|
||||
last_activity_at: number;
|
||||
user_id: string;
|
||||
device_id: string;
|
||||
roles: string;
|
||||
is_oauth: boolean;
|
||||
props: Record<string, any>;
|
||||
team_members: TeamMembership[];
|
||||
local: boolean;
|
||||
}
|
||||
6
webapp/platform/types/src/setup.ts
Обычный файл
6
webapp/platform/types/src/setup.ts
Обычный файл
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type CompleteOnboardingRequest = {
|
||||
install_plugins: string[];
|
||||
}
|
||||
93
webapp/platform/types/src/store.ts
Обычный файл
93
webapp/platform/types/src/store.ts
Обычный файл
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {AdminState} from './admin';
|
||||
import {Bot} from './bots';
|
||||
import {ChannelsState} from './channels';
|
||||
import {ChannelCategoriesState} from './channel_categories';
|
||||
import {CloudState, CloudUsage} from './cloud';
|
||||
import {HostedCustomerState} from './hosted_customer';
|
||||
import {EmojisState} from './emojis';
|
||||
import {FilesState} from './files';
|
||||
import {GeneralState} from './general';
|
||||
import {GroupsState} from './groups';
|
||||
import {IntegrationsState} from './integrations';
|
||||
import {JobsState} from './jobs';
|
||||
import {PostsState} from './posts';
|
||||
import {PreferenceType} from './preferences';
|
||||
import {
|
||||
AdminRequestsStatuses, ChannelsRequestsStatuses,
|
||||
FilesRequestsStatuses, GeneralRequestsStatuses,
|
||||
PostsRequestsStatuses, RolesRequestsStatuses,
|
||||
TeamsRequestsStatuses, UsersRequestsStatuses,
|
||||
} from './requests';
|
||||
import {Role} from './roles';
|
||||
import {SchemesState} from './schemes';
|
||||
import {SearchState} from './search';
|
||||
import {TeamsState} from './teams';
|
||||
import {ThreadsState} from './threads';
|
||||
import {Typing} from './typing';
|
||||
import {UsersState} from './users';
|
||||
import {AppsState} from './apps';
|
||||
import {InsightsState} from './insights';
|
||||
import {GifsState} from './gifs';
|
||||
import {WorkTemplatesState} from './work_templates';
|
||||
|
||||
export type GlobalState = {
|
||||
entities: {
|
||||
general: GeneralState;
|
||||
users: UsersState;
|
||||
teams: TeamsState;
|
||||
channels: ChannelsState;
|
||||
posts: PostsState;
|
||||
threads: ThreadsState;
|
||||
bots: {
|
||||
accounts: Record<string, Bot>;
|
||||
};
|
||||
preferences: {
|
||||
myPreferences: {
|
||||
[x: string]: PreferenceType;
|
||||
};
|
||||
};
|
||||
admin: AdminState;
|
||||
jobs: JobsState;
|
||||
search: SearchState;
|
||||
integrations: IntegrationsState;
|
||||
files: FilesState;
|
||||
emojis: EmojisState;
|
||||
typing: Typing;
|
||||
roles: {
|
||||
roles: {
|
||||
[x: string]: Role;
|
||||
};
|
||||
pending: Set<string>;
|
||||
};
|
||||
schemes: SchemesState;
|
||||
gifs: GifsState;
|
||||
groups: GroupsState;
|
||||
channelCategories: ChannelCategoriesState;
|
||||
apps: AppsState;
|
||||
cloud: CloudState;
|
||||
hostedCustomer: HostedCustomerState;
|
||||
usage: CloudUsage;
|
||||
insights: InsightsState;
|
||||
worktemplates: WorkTemplatesState;
|
||||
};
|
||||
errors: any[];
|
||||
requests: {
|
||||
channels: ChannelsRequestsStatuses;
|
||||
general: GeneralRequestsStatuses;
|
||||
posts: PostsRequestsStatuses;
|
||||
teams: TeamsRequestsStatuses;
|
||||
users: UsersRequestsStatuses;
|
||||
admin: AdminRequestsStatuses;
|
||||
files: FilesRequestsStatuses;
|
||||
roles: RolesRequestsStatuses;
|
||||
};
|
||||
websocket: {
|
||||
connected: boolean;
|
||||
lastConnectAt: number;
|
||||
lastDisconnectAt: number;
|
||||
connectionId: string;
|
||||
};
|
||||
};
|
||||
110
webapp/platform/types/src/teams.ts
Обычный файл
110
webapp/platform/types/src/teams.ts
Обычный файл
@@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {ServerError} from './errors';
|
||||
import {UserProfile} from './users';
|
||||
import {RelationOneToOne} from './utilities';
|
||||
|
||||
export type TeamMembership = TeamUnread & {
|
||||
user_id: string;
|
||||
roles: string;
|
||||
delete_at: number;
|
||||
scheme_admin: boolean;
|
||||
scheme_guest: boolean;
|
||||
scheme_user: boolean;
|
||||
};
|
||||
|
||||
export type TeamMemberWithError = {
|
||||
member: TeamMembership;
|
||||
user_id: string;
|
||||
error: ServerError;
|
||||
}
|
||||
|
||||
export type TeamType = 'O' | 'I';
|
||||
|
||||
export type Team = {
|
||||
id: string;
|
||||
create_at: number;
|
||||
update_at: number;
|
||||
delete_at: number;
|
||||
display_name: string;
|
||||
name: string;
|
||||
description: string;
|
||||
email: string;
|
||||
type: TeamType;
|
||||
company_name: string;
|
||||
allowed_domains: string;
|
||||
invite_id: string;
|
||||
allow_open_invite: boolean;
|
||||
scheme_id: string;
|
||||
group_constrained: boolean;
|
||||
policy_id?: string | null;
|
||||
};
|
||||
|
||||
export type TeamsState = {
|
||||
currentTeamId: string;
|
||||
teams: Record<string, Team>;
|
||||
myMembers: Record<string, TeamMembership>;
|
||||
membersInTeam: RelationOneToOne<Team, RelationOneToOne<UserProfile, TeamMembership>>;
|
||||
stats: RelationOneToOne<Team, TeamStats>;
|
||||
groupsAssociatedToTeam: any;
|
||||
totalCount: number;
|
||||
};
|
||||
|
||||
export type TeamUnread = {
|
||||
team_id: string;
|
||||
|
||||
/** The number of unread mentions in channels on this team, not including DMs and GMs */
|
||||
mention_count: number;
|
||||
|
||||
/** The number of unread mentions in root posts in channels on this team, not including DMs and GMs */
|
||||
mention_count_root: number;
|
||||
|
||||
/**
|
||||
* The number of unread posts in channels on this team, not including DMs and GMs
|
||||
*
|
||||
* @remarks Note that this differs from ChannelMembership.msg_count and ChannelUnread.msg_count since it tracks
|
||||
* unread posts instead of read posts.
|
||||
*/
|
||||
msg_count: number;
|
||||
|
||||
/**
|
||||
* The number of unread root posts in channels on this team, not including DMs and GMs
|
||||
*
|
||||
* @remarks Note that this differs from ChannelMember.msg_count_root and ChannelUnread.msg_count_root since it
|
||||
* tracks unread posts instead of read posts.
|
||||
*/
|
||||
msg_count_root: number;
|
||||
|
||||
thread_count?: number;
|
||||
thread_mention_count?: number;
|
||||
thread_urgent_mention_count?: number;
|
||||
};
|
||||
|
||||
export type GetTeamMembersOpts = {
|
||||
sort?: 'Username';
|
||||
exclude_deleted_users?: boolean;
|
||||
};
|
||||
|
||||
export type TeamsWithCount = {
|
||||
teams: Team[];
|
||||
total_count: number;
|
||||
};
|
||||
|
||||
export type TeamStats = {
|
||||
team_id: string;
|
||||
total_member_count: number;
|
||||
active_member_count: number;
|
||||
};
|
||||
|
||||
export type TeamSearchOpts = {
|
||||
page?: number;
|
||||
per_page?: number;
|
||||
allow_open_invite?: boolean;
|
||||
group_constrained?: boolean;
|
||||
}
|
||||
|
||||
export type TeamInviteWithError = {
|
||||
email: string;
|
||||
error: ServerError;
|
||||
};
|
||||
9
webapp/platform/types/src/terms_of_service.ts
Обычный файл
9
webapp/platform/types/src/terms_of_service.ts
Обычный файл
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type TermsOfService = {
|
||||
id: string;
|
||||
create_at: number;
|
||||
user_id: string;
|
||||
text: string;
|
||||
}
|
||||
72
webapp/platform/types/src/threads.ts
Обычный файл
72
webapp/platform/types/src/threads.ts
Обычный файл
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {Post} from './posts';
|
||||
import type {Team} from './teams';
|
||||
import type {Channel} from './channels';
|
||||
import type {UserProfile} from './users';
|
||||
import type {IDMappedObjects, RelationOneToMany, RelationOneToOne} from './utilities';
|
||||
|
||||
export enum UserThreadType {
|
||||
Synthetic = 'S' // derived from post
|
||||
}
|
||||
|
||||
export type UserThread = {
|
||||
id: string;
|
||||
reply_count: number;
|
||||
last_reply_at: number;
|
||||
last_viewed_at: number;
|
||||
participants: Array<{id: UserProfile['id']} | UserProfile>;
|
||||
unread_replies: number;
|
||||
unread_mentions: number;
|
||||
is_following: boolean;
|
||||
is_urgent?: boolean;
|
||||
type?: UserThreadType;
|
||||
|
||||
// TODO consider flattening, removing post from UserThreads in-store
|
||||
/**
|
||||
* only depend on channel_id and user_id for UserThread<>Channel/User mapping,
|
||||
* use normalized post store/selectors as those are kept up-to-date in the store
|
||||
*/
|
||||
post: {
|
||||
channel_id: Channel['id'];
|
||||
user_id: UserProfile['id'];
|
||||
};
|
||||
};
|
||||
|
||||
type SyntheticMissingKeys = 'unread_replies' | 'unread_mentions' | 'last_viewed_at';
|
||||
export type UserThreadSynthetic = Omit<UserThread, SyntheticMissingKeys> & {
|
||||
type: UserThreadType.Synthetic;
|
||||
}
|
||||
|
||||
export function threadIsSynthetic(thread: UserThread | UserThreadSynthetic): thread is UserThreadSynthetic {
|
||||
return thread.type === UserThreadType.Synthetic;
|
||||
}
|
||||
|
||||
export type UserThreadWithPost = UserThread & {post: Post};
|
||||
|
||||
export type UserThreadList = {
|
||||
total: number;
|
||||
total_unread_threads: number;
|
||||
total_unread_mentions: number;
|
||||
total_unread_urgent_mentions?: number;
|
||||
threads: UserThreadWithPost[];
|
||||
}
|
||||
|
||||
export type ThreadsState = {
|
||||
threadsInTeam: RelationOneToMany<Team, UserThread>;
|
||||
unreadThreadsInTeam: RelationOneToMany<Team, UserThread>;
|
||||
threads: IDMappedObjects<UserThread>;
|
||||
counts: RelationOneToOne<Team, {
|
||||
total: number;
|
||||
total_unread_threads: number;
|
||||
total_unread_mentions: number;
|
||||
total_unread_urgent_mentions?: number;
|
||||
}>;
|
||||
countsIncludingDirect: RelationOneToOne<Team, {
|
||||
total: number;
|
||||
total_unread_threads: number;
|
||||
total_unread_mentions: number;
|
||||
total_unread_urgent_mentions?: number;
|
||||
}>;
|
||||
};
|
||||
8
webapp/platform/types/src/typing.ts
Обычный файл
8
webapp/platform/types/src/typing.ts
Обычный файл
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type Typing = {
|
||||
[x: string]: {
|
||||
[x: string]: number;
|
||||
};
|
||||
};
|
||||
141
webapp/platform/types/src/users.ts
Обычный файл
141
webapp/platform/types/src/users.ts
Обычный файл
@@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Audit} from './audits';
|
||||
import {Channel} from './channels';
|
||||
import {Group} from './groups';
|
||||
import {Session} from './sessions';
|
||||
import {Team} from './teams';
|
||||
import {IDMappedObjects, RelationOneToMany, RelationOneToManyUnique, RelationOneToOne} from './utilities';
|
||||
|
||||
export type UserNotifyProps = {
|
||||
desktop: 'default' | 'all' | 'mention' | 'none';
|
||||
desktop_sound: 'true' | 'false';
|
||||
email: 'true' | 'false';
|
||||
mark_unread: 'all' | 'mention';
|
||||
push: 'default' | 'all' | 'mention' | 'none';
|
||||
push_status: 'ooo' | 'offline' | 'away' | 'dnd' | 'online';
|
||||
comments: 'never' | 'root' | 'any';
|
||||
first_name: 'true' | 'false';
|
||||
channel: 'true' | 'false';
|
||||
mention_keys: string;
|
||||
desktop_notification_sound?: 'Bing' | 'Crackle' | 'Down' | 'Hello' | 'Ripple' | 'Upstairs';
|
||||
desktop_threads?: 'default' | 'all' | 'mention' | 'none';
|
||||
email_threads?: 'default' | 'all' | 'mention' | 'none';
|
||||
push_threads?: 'default' | 'all' | 'mention' | 'none';
|
||||
auto_responder_active?: 'true' | 'false';
|
||||
auto_responder_message?: string;
|
||||
};
|
||||
|
||||
export type UserProfile = {
|
||||
id: string;
|
||||
create_at: number;
|
||||
update_at: number;
|
||||
delete_at: number;
|
||||
username: string;
|
||||
password: string;
|
||||
auth_service: string;
|
||||
email: string;
|
||||
nickname: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
position: string;
|
||||
roles: string;
|
||||
props: Record<string, string>;
|
||||
notify_props: UserNotifyProps;
|
||||
last_password_update: number;
|
||||
last_picture_update: number;
|
||||
locale: string;
|
||||
timezone?: UserTimezone;
|
||||
mfa_active: boolean;
|
||||
last_activity_at: number;
|
||||
is_bot: boolean;
|
||||
bot_description: string;
|
||||
terms_of_service_id: string;
|
||||
terms_of_service_create_at: number;
|
||||
remote_id?: string;
|
||||
status?: string;
|
||||
};
|
||||
|
||||
export type UserProfileWithLastViewAt = UserProfile & {
|
||||
last_viewed_at: number;
|
||||
};
|
||||
|
||||
export type UsersState = {
|
||||
currentUserId: string;
|
||||
isManualStatus: RelationOneToOne<UserProfile, boolean>;
|
||||
mySessions: Session[];
|
||||
myAudits: Audit[];
|
||||
profiles: IDMappedObjects<UserProfile>;
|
||||
profilesInTeam: RelationOneToMany<Team, UserProfile>;
|
||||
profilesNotInTeam: RelationOneToMany<Team, UserProfile>;
|
||||
profilesWithoutTeam: Set<string>;
|
||||
profilesInChannel: RelationOneToManyUnique<Channel, UserProfile>;
|
||||
profilesNotInChannel: RelationOneToManyUnique<Channel, UserProfile>;
|
||||
profilesInGroup: RelationOneToMany<Group, UserProfile>;
|
||||
profilesNotInGroup: RelationOneToMany<Group, UserProfile>;
|
||||
statuses: RelationOneToOne<UserProfile, string>;
|
||||
stats: RelationOneToOne<UserProfile, UsersStats>;
|
||||
filteredStats?: UsersStats;
|
||||
myUserAccessTokens: Record<string, UserAccessToken>;
|
||||
lastActivity: RelationOneToOne<UserProfile, number>;
|
||||
};
|
||||
|
||||
export type UserTimezone = {
|
||||
useAutomaticTimezone: boolean | string;
|
||||
automaticTimezone: string;
|
||||
manualTimezone: string;
|
||||
};
|
||||
|
||||
export type UserStatus = {
|
||||
user_id: string;
|
||||
status: string;
|
||||
manual?: boolean;
|
||||
last_activity_at?: number;
|
||||
active_channel?: string;
|
||||
dnd_end_time?: number;
|
||||
};
|
||||
|
||||
export enum CustomStatusDuration {
|
||||
DONT_CLEAR = '',
|
||||
THIRTY_MINUTES = 'thirty_minutes',
|
||||
ONE_HOUR = 'one_hour',
|
||||
FOUR_HOURS = 'four_hours',
|
||||
TODAY = 'today',
|
||||
THIS_WEEK = 'this_week',
|
||||
DATE_AND_TIME = 'date_and_time',
|
||||
CUSTOM_DATE_TIME = 'custom_date_time',
|
||||
}
|
||||
|
||||
export type UserCustomStatus = {
|
||||
emoji: string;
|
||||
text: string;
|
||||
duration: CustomStatusDuration;
|
||||
expires_at?: string;
|
||||
};
|
||||
|
||||
export type UserAccessToken = {
|
||||
id: string;
|
||||
token?: string;
|
||||
user_id: string;
|
||||
description: string;
|
||||
is_active: boolean;
|
||||
};
|
||||
|
||||
export type UsersStats = {
|
||||
total_users_count: number;
|
||||
};
|
||||
|
||||
export type GetFilteredUsersStatsOpts = {
|
||||
in_team?: string;
|
||||
in_channel?: string;
|
||||
include_deleted?: boolean;
|
||||
include_bots?: boolean;
|
||||
roles?: string[];
|
||||
channel_roles?: string[];
|
||||
team_roles?: string[];
|
||||
};
|
||||
|
||||
export type AuthChangeResponse = {
|
||||
follow_link: string;
|
||||
};
|
||||
26
webapp/platform/types/src/utilities.ts
Обычный файл
26
webapp/platform/types/src/utilities.ts
Обычный файл
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type RelationOneToOne<E extends {id: string}, T> = {
|
||||
[x in E['id']]: T;
|
||||
};
|
||||
export type RelationOneToMany<E1 extends {id: string}, E2 extends {id: string}> = {
|
||||
[x in E1['id']]: Array<E2['id']>;
|
||||
};
|
||||
export type RelationOneToManyUnique<E1 extends {id: string}, E2 extends {id: string}> = {
|
||||
[x in E1['id']]: Set<E2['id']>;
|
||||
};
|
||||
|
||||
export type IDMappedObjects<E extends {id: string}> = RelationOneToOne<E, E>;
|
||||
|
||||
export type DeepPartial<T> = {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
}
|
||||
|
||||
export type ValueOf<T> = T[keyof T];
|
||||
|
||||
/**
|
||||
* Based on https://stackoverflow.com/a/49725198
|
||||
*/
|
||||
export type RequireOnlyOne<T, Keys extends keyof T = keyof T> =
|
||||
Pick<T, Exclude<keyof T, Keys>> & {[K in Keys]-?: Required<Pick<T, K>> & Partial<Record<Exclude<Keys, K>, undefined>>}[Keys];
|
||||
97
webapp/platform/types/src/work_templates.ts
Обычный файл
97
webapp/platform/types/src/work_templates.ts
Обычный файл
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {RequireOnlyOne} from './utilities';
|
||||
|
||||
export type WorkTemplatesState = {
|
||||
categories: Category[];
|
||||
templatesInCategory: Record<string, WorkTemplate[]>;
|
||||
playbookTemplates: PlaybookTemplateType[];
|
||||
linkedProducts: Record<string, number>;
|
||||
}
|
||||
|
||||
export interface PlaybookTemplateType {
|
||||
title: string;
|
||||
template: any;
|
||||
}
|
||||
|
||||
export interface ExecuteWorkTemplateRequest {
|
||||
team_id: string;
|
||||
name: string;
|
||||
visibility: Visibility;
|
||||
work_template: WorkTemplate;
|
||||
playbook_templates?: PlaybookTemplateType[];
|
||||
}
|
||||
|
||||
export interface ExecuteWorkTemplateResponse {
|
||||
channel_with_playbook_ids: string[];
|
||||
channel_ids: string[];
|
||||
}
|
||||
|
||||
export interface WorkTemplate {
|
||||
id: string;
|
||||
category: string;
|
||||
useCase: string;
|
||||
description: Description;
|
||||
illustration: string;
|
||||
visibility: Visibility;
|
||||
content: ValidContent[];
|
||||
}
|
||||
|
||||
export const categories = ['product', 'devops', 'company_wide', 'leadership', 'design'];
|
||||
export interface Category {
|
||||
id: typeof categories[number];
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface Channel {
|
||||
id: string;
|
||||
name: string;
|
||||
illustration: string;
|
||||
playbook?: string;
|
||||
}
|
||||
export interface Board {
|
||||
id: string;
|
||||
name: string;
|
||||
illustration: string;
|
||||
channel?: string;
|
||||
}
|
||||
export interface Playbook {
|
||||
id: string;
|
||||
name: string;
|
||||
illustration: string;
|
||||
template: string;
|
||||
}
|
||||
export interface Integration {
|
||||
id: string;
|
||||
name?: string;
|
||||
icon?: string;
|
||||
installed?: boolean;
|
||||
}
|
||||
|
||||
interface Content {
|
||||
channel?: Channel;
|
||||
board?: Board;
|
||||
playbook?: Playbook;
|
||||
integration?: Integration;
|
||||
}
|
||||
|
||||
type ValidContent = RequireOnlyOne<Content, 'channel' | 'board' | 'playbook' | 'integration'>;
|
||||
|
||||
export interface MessageWithIllustration {
|
||||
message: string;
|
||||
illustration?: string;
|
||||
}
|
||||
type MessageWithMandatoryIllustration = Partial<MessageWithIllustration> & Required<Pick<MessageWithIllustration, 'illustration'>>;
|
||||
|
||||
interface Description {
|
||||
channel: MessageWithIllustration;
|
||||
board: MessageWithIllustration;
|
||||
playbook: MessageWithIllustration;
|
||||
integration: MessageWithMandatoryIllustration;
|
||||
}
|
||||
|
||||
export enum Visibility {
|
||||
Public = 'public',
|
||||
Private = 'private',
|
||||
}
|
||||
18
webapp/platform/types/tsconfig.json
Обычный файл
18
webapp/platform/types/tsconfig.json
Обычный файл
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"target": "es6",
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"jsx": "react",
|
||||
"outDir": "./lib",
|
||||
"rootDir": "./src",
|
||||
"composite": true
|
||||
},
|
||||
"exclude": ["**/node_modules", "**/lib", "**/*.test.js", "**/*.test.ts"]
|
||||
}
|
||||
Ссылка в новой задаче
Block a user