MM-58349: Shared Channels in System Console (#28116)
Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Miguel de la Cruz <miguel@mcrx.me>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
02e6851603
Коммит
0f200adbe0
@@ -26,7 +26,7 @@ import type {
|
||||
ChannelSearchOpts,
|
||||
ServerChannel,
|
||||
} from '@mattermost/types/channels';
|
||||
import type {Options, StatusOK, ClientResponse, FetchPaginatedThreadOptions} from '@mattermost/types/client4';
|
||||
import type {Options, StatusOK, ClientResponse, FetchPaginatedThreadOptions, OptsSignalExt} from '@mattermost/types/client4';
|
||||
import {LogLevel} from '@mattermost/types/client4';
|
||||
import type {
|
||||
Address,
|
||||
@@ -110,12 +110,14 @@ import type {Post, PostList, PostSearchResults, PostsUsageResponse, TeamsUsageRe
|
||||
import type {PreferenceType} from '@mattermost/types/preferences';
|
||||
import type {ProductNotices} from '@mattermost/types/product_notices';
|
||||
import type {Reaction} from '@mattermost/types/reactions';
|
||||
import type {RemoteCluster, RemoteClusterAcceptInvite, RemoteClusterPatch, RemoteClusterWithPassword} from '@mattermost/types/remote_clusters';
|
||||
import type {UserReport, UserReportFilter, UserReportOptions} from '@mattermost/types/reports';
|
||||
import type {Role} from '@mattermost/types/roles';
|
||||
import type {SamlCertificateStatus, SamlMetadataResponse} from '@mattermost/types/saml';
|
||||
import type {Scheme} from '@mattermost/types/schemes';
|
||||
import type {Session} from '@mattermost/types/sessions';
|
||||
import type {CompleteOnboardingRequest} from '@mattermost/types/setup';
|
||||
import type {SharedChannelRemote} from '@mattermost/types/shared_channels';
|
||||
import type {
|
||||
GetTeamMembersOpts,
|
||||
Team,
|
||||
@@ -140,7 +142,7 @@ import type {
|
||||
GetFilteredUsersStatsOpts,
|
||||
UserCustomStatus,
|
||||
} from '@mattermost/types/users';
|
||||
import type {DeepPartial, RelationOneToOne} from '@mattermost/types/utilities';
|
||||
import type {DeepPartial, PartialExcept, RelationOneToOne} from '@mattermost/types/utilities';
|
||||
|
||||
import {cleanUrlForLogging} from './errors';
|
||||
import {buildQueryString} from './helpers';
|
||||
@@ -327,6 +329,14 @@ export default class Client4 {
|
||||
return `${this.getBaseRoute()}/users/${userId}/teams/${teamId}/channels/categories`;
|
||||
}
|
||||
|
||||
getRemoteClustersRoute() {
|
||||
return `${this.getBaseRoute()}/remotecluster`;
|
||||
}
|
||||
|
||||
getRemoteClusterRoute(remoteId: string) {
|
||||
return `${this.getRemoteClustersRoute()}/${remoteId}`;
|
||||
}
|
||||
|
||||
getPostsRoute() {
|
||||
return `${this.getBaseRoute()}/posts`;
|
||||
}
|
||||
@@ -1942,9 +1952,9 @@ export default class Client4 {
|
||||
);
|
||||
};
|
||||
|
||||
searchAllChannels(term: string, opts: {page: number; per_page: number} & ChannelSearchOpts): Promise<ChannelsWithTotalCount>;
|
||||
searchAllChannels(term: string, opts: Omit<ChannelSearchOpts, 'page' | 'per_page'> | undefined): Promise<ChannelWithTeamData[]>;
|
||||
searchAllChannels(term: string, opts: ChannelSearchOpts = {}) {
|
||||
searchAllChannels(term: string, opts: {page: number; per_page: number} & ChannelSearchOpts & OptsSignalExt): Promise<ChannelsWithTotalCount>;
|
||||
searchAllChannels(term: string, opts: Omit<ChannelSearchOpts, 'page' | 'per_page'> & OptsSignalExt | undefined): Promise<ChannelWithTeamData[]>;
|
||||
searchAllChannels(term: string, opts: ChannelSearchOpts & OptsSignalExt = {}) {
|
||||
const body = {
|
||||
term,
|
||||
...opts,
|
||||
@@ -1958,7 +1968,7 @@ export default class Client4 {
|
||||
}
|
||||
return this.doFetch<ChannelWithTeamData[] | ChannelsWithTotalCount>(
|
||||
`${this.getChannelsRoute()}/search${buildQueryString(queryParams)}`,
|
||||
{method: 'post', body: JSON.stringify(body)},
|
||||
{method: 'post', body: JSON.stringify(body), signal: opts.signal},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2072,6 +2082,89 @@ export default class Client4 {
|
||||
);
|
||||
};
|
||||
|
||||
// Remote Clusters Routes
|
||||
|
||||
getRemoteClusters = (options: {excludePlugins: boolean}) => {
|
||||
return this.doFetch<RemoteCluster[]>(
|
||||
`${this.getRemoteClustersRoute()}${buildQueryString({exclude_plugins: options.excludePlugins})}`,
|
||||
{method: 'GET'},
|
||||
);
|
||||
};
|
||||
|
||||
getRemoteCluster = (remoteId: string) => {
|
||||
return this.doFetch<RemoteCluster>(
|
||||
`${this.getRemoteClusterRoute(remoteId)}`,
|
||||
{method: 'GET'},
|
||||
);
|
||||
};
|
||||
|
||||
createRemoteCluster = (remoteCluster: PartialExcept<RemoteClusterWithPassword, 'name' | 'display_name'>) => {
|
||||
return this.doFetch<{invite: string; password: string; remote_cluster: RemoteCluster}>(
|
||||
`${this.getRemoteClustersRoute()}`,
|
||||
{method: 'POST', body: JSON.stringify(remoteCluster)},
|
||||
);
|
||||
};
|
||||
|
||||
patchRemoteCluster = (remoteId: string, patch: Partial<RemoteClusterPatch>) => {
|
||||
return this.doFetch<RemoteCluster>(
|
||||
`${this.getRemoteClusterRoute(remoteId)}`,
|
||||
{method: 'PATCH', body: JSON.stringify(patch)},
|
||||
);
|
||||
};
|
||||
|
||||
deleteRemoteCluster = (remoteId: string) => {
|
||||
return this.doFetch(
|
||||
`${this.getRemoteClusterRoute(remoteId)}`,
|
||||
{method: 'DELETE'},
|
||||
);
|
||||
};
|
||||
|
||||
acceptInviteRemoteCluster = (remoteClusterAcceptInvite: RemoteClusterAcceptInvite) => {
|
||||
return this.doFetch<RemoteCluster>(
|
||||
`${this.getRemoteClustersRoute()}/accept_invite`,
|
||||
{method: 'POST', body: JSON.stringify(remoteClusterAcceptInvite)},
|
||||
);
|
||||
};
|
||||
|
||||
generateInviteRemoteCluster = (remoteId: string, remoteCluster: Partial<Pick<RemoteClusterWithPassword, 'password'>>) => {
|
||||
return this.doFetch<string>(
|
||||
`${this.getRemoteClusterRoute(remoteId)}/generate_invite`,
|
||||
{method: 'POST', body: JSON.stringify(remoteCluster)},
|
||||
);
|
||||
};
|
||||
|
||||
// Shared Channels Routes
|
||||
|
||||
getSharedChannelRemotes = (
|
||||
remoteId: string,
|
||||
filters: {
|
||||
exclude_remote?: boolean;
|
||||
exclude_home?: boolean;
|
||||
include_deleted?: boolean;
|
||||
include_unconfirmed?: boolean;
|
||||
exclude_confirmed?: boolean;
|
||||
},
|
||||
) => {
|
||||
return this.doFetch<SharedChannelRemote[]>(
|
||||
`${this.getRemoteClusterRoute(remoteId)}/sharedchannelremotes${buildQueryString(filters)}`,
|
||||
{method: 'GET'},
|
||||
);
|
||||
};
|
||||
|
||||
sharedChannelRemoteInvite = (remoteId: string, channelId: string) => {
|
||||
return this.doFetch<StatusOK>(
|
||||
`${this.getRemoteClusterRoute(remoteId)}/channels/${channelId}/invite`,
|
||||
{method: 'POST'},
|
||||
);
|
||||
};
|
||||
|
||||
sharedChannelRemoteUninvite = (remoteId: string, channelId: string) => {
|
||||
return this.doFetch<StatusOK>(
|
||||
`${this.getRemoteClusterRoute(remoteId)}/channels/${channelId}/uninvite`,
|
||||
{method: 'POST'},
|
||||
);
|
||||
};
|
||||
|
||||
// Post Routes
|
||||
|
||||
createPost = async (post: Post) => {
|
||||
|
||||
@@ -31,7 +31,7 @@ export type Props = {
|
||||
ariaLabel?: string;
|
||||
errorText?: string | React.ReactNode;
|
||||
compassDesign?: boolean;
|
||||
backdrop?: boolean;
|
||||
backdrop?: boolean | 'static';
|
||||
backdropClassName?: string;
|
||||
tabIndex?: number;
|
||||
children: React.ReactNode;
|
||||
@@ -40,6 +40,7 @@ export type Props = {
|
||||
headerInput?: React.ReactNode;
|
||||
bodyPadding?: boolean;
|
||||
bodyDivider?: boolean;
|
||||
bodyOverflowVisible?: boolean;
|
||||
footerContent?: React.ReactNode;
|
||||
footerDivider?: boolean;
|
||||
appendedContent?: React.ReactNode;
|
||||
@@ -180,7 +181,14 @@ export class GenericModal extends React.PureComponent<Props, State> {
|
||||
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)}
|
||||
dialogClassName={classNames(
|
||||
'a11y__modal GenericModal',
|
||||
{
|
||||
GenericModal__compassDesign: this.props.compassDesign,
|
||||
'modal--overflow': this.props.bodyOverflowVisible,
|
||||
},
|
||||
this.props.className,
|
||||
)}
|
||||
show={this.state.show}
|
||||
restoreFocus={true}
|
||||
enforceFocus={this.props.enforceFocus}
|
||||
@@ -204,7 +212,7 @@ export class GenericModal extends React.PureComponent<Props, State> {
|
||||
</>
|
||||
)}
|
||||
</Modal.Header>
|
||||
<Modal.Body className={classNames({divider: this.props.bodyDivider})}>
|
||||
<Modal.Body className={classNames({divider: this.props.bodyDivider, 'overflow-visible': this.props.bodyOverflowVisible})}>
|
||||
{this.props.compassDesign ? (
|
||||
this.props.errorText && (
|
||||
<div className='genericModalError'>
|
||||
|
||||
@@ -25,6 +25,8 @@ export type Options = {
|
||||
duplex?: 'half'; /** Optional, but required for node clients. Must be 'half' for half-duplex fetch; 'full' is reserved for future use. See https://fetch.spec.whatwg.org/#dom-requestinit-duplex */
|
||||
};
|
||||
|
||||
export type OptsSignalExt = {signal?: AbortSignal};
|
||||
|
||||
export type StatusOK = {
|
||||
status: 'OK';
|
||||
};
|
||||
|
||||
@@ -509,6 +509,13 @@ export type WranglerSettings = {
|
||||
MoveThreadFromGroupMessageChannelEnable: boolean;
|
||||
};
|
||||
|
||||
export type ConnectedWorkspacesSettings = {
|
||||
EnableSharedChannels: boolean;
|
||||
EnableRemoteClusterService: boolean;
|
||||
DisableSharedChannelsStatusSync: boolean;
|
||||
MaxPostsPerSync: number;
|
||||
}
|
||||
|
||||
export type FileSettings = {
|
||||
EnableFileAttachments: boolean;
|
||||
EnableMobileUpload: boolean;
|
||||
@@ -981,6 +988,7 @@ export type AdminConfig = {
|
||||
ImportSettings: ImportSettings;
|
||||
ExportSettings: ExportSettings;
|
||||
WranglerSettings: WranglerSettings;
|
||||
ConnectedWorkspacesSettings: ConnectedWorkspacesSettings;
|
||||
};
|
||||
|
||||
export type ReplicaLagSetting = {
|
||||
|
||||
47
webapp/platform/types/src/remote_clusters.ts
Обычный файл
47
webapp/platform/types/src/remote_clusters.ts
Обычный файл
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type RemoteClusterInvite = {
|
||||
remote_id: string;
|
||||
remote_team_id: string;
|
||||
site_url: string;
|
||||
token: string;
|
||||
};
|
||||
|
||||
export type RemoteClusterAcceptInvite = {
|
||||
name: string;
|
||||
display_name: string;
|
||||
invite: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export type RemoteClusterPatch = Pick<RemoteCluster, 'display_name' | 'default_team_id'>
|
||||
|
||||
export function isRemoteClusterPatch(x: Partial<RemoteClusterPatch>): x is RemoteClusterPatch {
|
||||
return (
|
||||
(x.display_name !== undefined && x.display_name !== '') ||
|
||||
x.default_team_id !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
export type RemoteCluster = {
|
||||
remote_id: string;
|
||||
remote_team_id: string;
|
||||
name: string;
|
||||
display_name: string;
|
||||
site_url: string;
|
||||
create_at: number;
|
||||
delete_at: number;
|
||||
last_ping_at: number;
|
||||
token?: string;
|
||||
remote_token?: string;
|
||||
topics: string;
|
||||
creator_id: string;
|
||||
plugin_id: string;
|
||||
options: number;
|
||||
default_team_id: string;
|
||||
}
|
||||
|
||||
export type RemoteClusterWithPassword = RemoteCluster & {
|
||||
password: string;
|
||||
}
|
||||
18
webapp/platform/types/src/shared_channels.ts
Обычный файл
18
webapp/platform/types/src/shared_channels.ts
Обычный файл
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type SharedChannelRemote = {
|
||||
id: string;
|
||||
channel_id: string;
|
||||
creator_id: string;
|
||||
create_at: number;
|
||||
update_at: number;
|
||||
delete_at: number;
|
||||
is_invite_accepted: boolean;
|
||||
is_invite_confirmed: boolean;
|
||||
remote_id: string;
|
||||
last_post_update_at: number;
|
||||
last_post_id: string;
|
||||
last_post_create_at: number;
|
||||
last_post_create_id: string;
|
||||
}
|
||||
@@ -43,3 +43,5 @@ Pick<T, Exclude<keyof T, Keys>> & {[K in Keys]-?: Required<Pick<T, K>> & Partial
|
||||
|
||||
export type Intersection<T1, T2> =
|
||||
Omit<Omit<T1&T2, keyof(Omit<T1, keyof(T2)>)>, keyof(Omit<T2, keyof(T1)>)>;
|
||||
|
||||
export type PartialExcept<T extends Record<string, unknown>, TKeysNotPartial extends keyof T> = Partial<T> & Pick<T, TKeysNotPartial>;
|
||||
|
||||
Ссылка в новой задаче
Block a user