[CLD-8377] Add ability to open invitation modal from a query parameter (#28315)
* Add query_param_action_controller, allowing actions to be performed based on query parameters when webapp is loaded * Actually git add the new component * undo changes to package-lock * Change render to renderWithContext * Move into channel_controller * Fix tests * Updates to RootRedirect component and redirectUserToDefaultTeam * Grab another entry point * Adjust font for title of no permissions modal, fix default text * Fix linter --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
@@ -329,7 +329,18 @@ export async function getTeamRedirectChannelIfIsAccesible(user: UserProfile, tea
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function redirectUserToDefaultTeam() {
|
function historyPushWithQueryParams(path: string, queryParams?: URLSearchParams) {
|
||||||
|
if (queryParams) {
|
||||||
|
getHistory().push({
|
||||||
|
pathname: path,
|
||||||
|
search: queryParams.toString(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
getHistory().push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function redirectUserToDefaultTeam(searchParams?: URLSearchParams) {
|
||||||
let state = getState();
|
let state = getState();
|
||||||
|
|
||||||
// Assume we need to load the user if they don't have any team memberships loaded or the user loaded
|
// Assume we need to load the user if they don't have any team memberships loaded or the user loaded
|
||||||
@@ -356,11 +367,11 @@ export async function redirectUserToDefaultTeam() {
|
|||||||
const teams = getActiveTeamsList(state);
|
const teams = getActiveTeamsList(state);
|
||||||
if (teams.length === 0) {
|
if (teams.length === 0) {
|
||||||
if (isUserFirstAdmin && onboardingFlowEnabled) {
|
if (isUserFirstAdmin && onboardingFlowEnabled) {
|
||||||
getHistory().push('/preparing-workspace');
|
historyPushWithQueryParams('/preparing-workspace', searchParams);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
getHistory().push('/select_team');
|
historyPushWithQueryParams('/select_team', searchParams);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,7 +384,7 @@ export async function redirectUserToDefaultTeam() {
|
|||||||
const channel = await getTeamRedirectChannelIfIsAccesible(user, team);
|
const channel = await getTeamRedirectChannelIfIsAccesible(user, team);
|
||||||
if (channel) {
|
if (channel) {
|
||||||
dispatch(selectChannel(channel.id));
|
dispatch(selectChannel(channel.id));
|
||||||
getHistory().push(`/${team.name}/channels/${channel.name}`);
|
historyPushWithQueryParams(`/${team.name}/channels/${channel.name}`, searchParams);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -385,10 +396,10 @@ export async function redirectUserToDefaultTeam() {
|
|||||||
const channel = await getTeamRedirectChannelIfIsAccesible(user, myTeam); // eslint-disable-line no-await-in-loop
|
const channel = await getTeamRedirectChannelIfIsAccesible(user, myTeam); // eslint-disable-line no-await-in-loop
|
||||||
if (channel) {
|
if (channel) {
|
||||||
dispatch(selectChannel(channel.id));
|
dispatch(selectChannel(channel.id));
|
||||||
getHistory().push(`/${myTeam.name}/channels/${channel.name}`);
|
historyPushWithQueryParams(`/${myTeam.name}/channels/${channel.name}`, searchParams);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getHistory().push('/select_team');
|
historyPushWithQueryParams('/select_team', searchParams);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import {render, act} from '@testing-library/react';
|
import {act} from '@testing-library/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Provider} from 'react-redux';
|
import {Provider} from 'react-redux';
|
||||||
|
|
||||||
import * as actions from 'actions/status_actions';
|
import * as actions from 'actions/status_actions';
|
||||||
|
|
||||||
|
import {renderWithContext} from 'tests/react_testing_utils';
|
||||||
import mockStore from 'tests/test_store';
|
import mockStore from 'tests/test_store';
|
||||||
import Constants from 'utils/constants';
|
import Constants from 'utils/constants';
|
||||||
import {TestHelper} from 'utils/test_helper';
|
import {TestHelper} from 'utils/test_helper';
|
||||||
@@ -54,7 +55,7 @@ describe('ChannelController', () => {
|
|||||||
mockState.entities.general.config.EnableUserStatuses = 'true';
|
mockState.entities.general.config.EnableUserStatuses = 'true';
|
||||||
const store = mockStore(mockState);
|
const store = mockStore(mockState);
|
||||||
|
|
||||||
render(
|
renderWithContext(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<ChannelController shouldRenderCenterChannel={true}/>
|
<ChannelController shouldRenderCenterChannel={true}/>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
@@ -71,7 +72,7 @@ describe('ChannelController', () => {
|
|||||||
const store = mockStore(mockState);
|
const store = mockStore(mockState);
|
||||||
mockState.entities.general.config.EnableUserStatuses = 'false';
|
mockState.entities.general.config.EnableUserStatuses = 'false';
|
||||||
|
|
||||||
render(
|
renderWithContext(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<ChannelController shouldRenderCenterChannel={true}/>
|
<ChannelController shouldRenderCenterChannel={true}/>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {addVisibleUsersInCurrentChannelAndSelfToStatusPoll} from 'actions/status
|
|||||||
import {makeAsyncComponent} from 'components/async_load';
|
import {makeAsyncComponent} from 'components/async_load';
|
||||||
import CenterChannel from 'components/channel_layout/center_channel';
|
import CenterChannel from 'components/channel_layout/center_channel';
|
||||||
import LoadingScreen from 'components/loading_screen';
|
import LoadingScreen from 'components/loading_screen';
|
||||||
|
import QueryParamActionController from 'components/query_param_actions/query_param_action_controller';
|
||||||
import Sidebar from 'components/sidebar';
|
import Sidebar from 'components/sidebar';
|
||||||
import CRTPostsChannelResetWatcher from 'components/threading/channel_threads/posts_channel_reset_watcher';
|
import CRTPostsChannelResetWatcher from 'components/threading/channel_threads/posts_channel_reset_watcher';
|
||||||
import UnreadsStatusHandler from 'components/unreads_status_handler';
|
import UnreadsStatusHandler from 'components/unreads_status_handler';
|
||||||
@@ -69,6 +70,7 @@ export default function ChannelController(props: Props) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CRTPostsChannelResetWatcher/>
|
<CRTPostsChannelResetWatcher/>
|
||||||
|
<QueryParamActionController/>
|
||||||
<Sidebar/>
|
<Sidebar/>
|
||||||
<div
|
<div
|
||||||
id='channel_view'
|
id='channel_view'
|
||||||
|
|||||||
@@ -1,37 +1,30 @@
|
|||||||
.NoPermissionsView {
|
.NoPermissionsView {
|
||||||
|
padding-top: 20px;
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
align-self: stretch;
|
||||||
|
padding: var(--spacing-xxl, 24px) 32px;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
&__body {
|
&__body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
margin-bottom: var(--spacing-l, 12px);
|
||||||
.icon-close {
|
|
||||||
position: absolute;
|
|
||||||
top: 20px;
|
|
||||||
right: 15px;
|
|
||||||
padding: 0;
|
|
||||||
border: none;
|
|
||||||
-moz-appearance: none;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
appearance: none;
|
|
||||||
background: none;
|
|
||||||
color: inherit;
|
|
||||||
color: rgba(var(--center-channel-color-rgb), 0.64);
|
|
||||||
cursor: pointer;
|
|
||||||
font: inherit;
|
|
||||||
font-size: 24px;
|
|
||||||
outline: inherit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
margin: 10px 0 22px;
|
font-family: 'Metropolis', sans-serif;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__description {
|
&__description {
|
||||||
|
margin-bottom: 32px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,35 +12,36 @@ import './no_permissions_view.scss';
|
|||||||
type Props = {
|
type Props = {
|
||||||
footerClass: string;
|
footerClass: string;
|
||||||
onDone: () => void;
|
onDone: () => void;
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NoPermissionsView(props: Props) {
|
export default function NoPermissionsView(props: Props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Modal.Header
|
||||||
|
className='NoPermissionsView__header'
|
||||||
|
closeButton={true}
|
||||||
|
>
|
||||||
|
<Modal.Title className='NoPermissionsView__title'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='invite_modal.no_permissions.title'
|
||||||
|
defaultMessage='Unable to invite people'
|
||||||
|
/>
|
||||||
|
</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
<Modal.Body>
|
<Modal.Body>
|
||||||
<div className='NoPermissionsView__body'>
|
<div className='NoPermissionsView__body'>
|
||||||
<button
|
|
||||||
className='icon icon-close'
|
|
||||||
aria-label='Close'
|
|
||||||
title='Close'
|
|
||||||
onClick={props.onDone}
|
|
||||||
/>
|
|
||||||
<AccessDeniedSvg
|
|
||||||
width={120}
|
|
||||||
height={100}
|
|
||||||
/>
|
|
||||||
<div className='NoPermissionsView__title'>
|
|
||||||
<FormattedMessage
|
|
||||||
id='invite_modal.no_permissions.title'
|
|
||||||
defaultMessage='Unable to continue'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='NoPermissionsView__description'>
|
<div className='NoPermissionsView__description'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='invite_modal.no_permissions.description'
|
id='invite_modal.no_permissions.description'
|
||||||
defaultMessage='You do not have permissions to add users or guests. If this seems like an error, please reach out to your system administrator.'
|
defaultMessage='You do not have permissions to add users or guests. If this seems like an error, please reach out to your system administrator.'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<AccessDeniedSvg
|
||||||
|
width={211}
|
||||||
|
height={156}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
<Modal.Footer className={props.footerClass}>
|
<Modal.Footer className={props.footerClass}>
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {useDispatch} from 'react-redux';
|
||||||
|
import {MemoryRouter, Route, useHistory} from 'react-router-dom';
|
||||||
|
|
||||||
|
import {openModal} from 'actions/views/modals';
|
||||||
|
|
||||||
|
import {renderWithContext} from 'tests/react_testing_utils';
|
||||||
|
|
||||||
|
import QueryParamActionController from './query_param_action_controller';
|
||||||
|
|
||||||
|
// Mock react-redux since we just care about calling logic
|
||||||
|
jest.mock('react-redux', () => ({
|
||||||
|
...jest.requireActual('react-redux'),
|
||||||
|
useDispatch: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('actions/views/modals', () => ({
|
||||||
|
openModal: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useHistory: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('QueryParamActionController', () => {
|
||||||
|
let mockDispatch: jest.Mock;
|
||||||
|
|
||||||
|
// Define a custom type for mockHistory that includes the replace method
|
||||||
|
interface MockHistory extends jest.Mock<History, [any]> {
|
||||||
|
replace: jest.Mock;
|
||||||
|
}
|
||||||
|
let mockHistory: MockHistory;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockDispatch = jest.fn();
|
||||||
|
(useDispatch as jest.Mock).mockReturnValue(mockDispatch);
|
||||||
|
mockHistory = {
|
||||||
|
replace: jest.fn(),
|
||||||
|
} as MockHistory;
|
||||||
|
(useHistory as jest.Mock).mockReturnValue(mockHistory);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should dispatch openModal for INVITATION modal ID when passed valid open_invitation_modal action', () => {
|
||||||
|
renderWithContext(
|
||||||
|
<MemoryRouter initialEntries={['/?action=open_invitation_modal']}>
|
||||||
|
<Route
|
||||||
|
path='/'
|
||||||
|
component={QueryParamActionController}
|
||||||
|
/>
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockDispatch).toHaveBeenCalledWith(
|
||||||
|
openModal({
|
||||||
|
modalId: 'INVITATION',
|
||||||
|
dialogType: expect.any(Function),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not dispatch any action when action query parameter is not present', () => {
|
||||||
|
renderWithContext(
|
||||||
|
<MemoryRouter initialEntries={['/']}>
|
||||||
|
<Route
|
||||||
|
path='/'
|
||||||
|
component={QueryParamActionController}
|
||||||
|
/>
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockDispatch).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not dispatch any action when action query parameter is not in list', () => {
|
||||||
|
renderWithContext(
|
||||||
|
<MemoryRouter initialEntries={['/?action=invalid_action']}>
|
||||||
|
<Route
|
||||||
|
path='/'
|
||||||
|
component={QueryParamActionController}
|
||||||
|
/>
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockDispatch).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove the action query parameter after dispatching the action', () => {
|
||||||
|
renderWithContext(
|
||||||
|
<MemoryRouter initialEntries={['/?action=open_invitation_modal']}>
|
||||||
|
<Route
|
||||||
|
path='/'
|
||||||
|
component={QueryParamActionController}
|
||||||
|
/>
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockDispatch).toHaveBeenCalledWith(
|
||||||
|
openModal({
|
||||||
|
modalId: 'INVITATION',
|
||||||
|
dialogType: expect.any(Function),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockHistory.replace).toHaveBeenCalledWith({
|
||||||
|
search: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import {useEffect} from 'react';
|
||||||
|
import {useDispatch} from 'react-redux';
|
||||||
|
import {useHistory, useLocation} from 'react-router-dom';
|
||||||
|
|
||||||
|
import {openModal} from 'actions/views/modals';
|
||||||
|
|
||||||
|
import InvitationModal from 'components/invitation_modal';
|
||||||
|
|
||||||
|
import {ModalIdentifiers} from 'utils/constants';
|
||||||
|
|
||||||
|
import type {ModalData} from 'types/actions';
|
||||||
|
|
||||||
|
interface ActionMap {
|
||||||
|
[key: string]: ModalData<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function QueryParamActionController() {
|
||||||
|
const location = useLocation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const actionMap: ActionMap = {
|
||||||
|
open_invitation_modal: {
|
||||||
|
modalId: ModalIdentifiers.INVITATION,
|
||||||
|
dialogType: InvitationModal,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const searchParams = new URLSearchParams(location.search);
|
||||||
|
const action = searchParams.get('action');
|
||||||
|
|
||||||
|
if (action && actionMap[action]) {
|
||||||
|
dispatch(openModal(actionMap[action]));
|
||||||
|
|
||||||
|
// Delete the action after it's been invoked so that it's not locked for subsequent refreshes
|
||||||
|
searchParams.delete('action');
|
||||||
|
history.replace({
|
||||||
|
search: searchParams.toString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [location, actionMap]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default QueryParamActionController;
|
||||||
@@ -99,12 +99,12 @@ export function registerCustomPostRenderer(type: string, component: any, id: str
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function redirectToOnboardingOrDefaultTeam(history: History): ThunkActionFunc<void> {
|
export function redirectToOnboardingOrDefaultTeam(history: History, searchParams?: URLSearchParams): ThunkActionFunc<void> {
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const isUserAdmin = isCurrentUserSystemAdmin(state);
|
const isUserAdmin = isCurrentUserSystemAdmin(state);
|
||||||
if (!isUserAdmin) {
|
if (!isUserAdmin) {
|
||||||
redirectUserToDefaultTeam();
|
redirectUserToDefaultTeam(searchParams);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,19 +113,19 @@ export function redirectToOnboardingOrDefaultTeam(history: History): ThunkAction
|
|||||||
const onboardingFlowEnabled = getIsOnboardingFlowEnabled(state);
|
const onboardingFlowEnabled = getIsOnboardingFlowEnabled(state);
|
||||||
|
|
||||||
if (teams.length > 0 || !onboardingFlowEnabled) {
|
if (teams.length > 0 || !onboardingFlowEnabled) {
|
||||||
redirectUserToDefaultTeam();
|
redirectUserToDefaultTeam(searchParams);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstAdminSetupComplete = await dispatch(getFirstAdminSetupComplete());
|
const firstAdminSetupComplete = await dispatch(getFirstAdminSetupComplete());
|
||||||
if (firstAdminSetupComplete?.data) {
|
if (firstAdminSetupComplete?.data) {
|
||||||
redirectUserToDefaultTeam();
|
redirectUserToDefaultTeam(searchParams);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const profilesResult = await dispatch(getProfiles(0, General.PROFILE_CHUNK_SIZE, {roles: General.SYSTEM_ADMIN_ROLE}));
|
const profilesResult = await dispatch(getProfiles(0, General.PROFILE_CHUNK_SIZE, {roles: General.SYSTEM_ADMIN_ROLE}));
|
||||||
if (profilesResult.error) {
|
if (profilesResult.error) {
|
||||||
redirectUserToDefaultTeam();
|
redirectUserToDefaultTeam(searchParams);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const currentUser = getCurrentUser(getState());
|
const currentUser = getCurrentUser(getState());
|
||||||
@@ -141,7 +141,7 @@ export function redirectToOnboardingOrDefaultTeam(history: History): ThunkAction
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
redirectUserToDefaultTeam();
|
redirectUserToDefaultTeam(searchParams);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ export default class Root extends React.PureComponent<Props, State> {
|
|||||||
|
|
||||||
if (isUserAtRootRoute) {
|
if (isUserAtRootRoute) {
|
||||||
if (isMeRequested) {
|
if (isMeRequested) {
|
||||||
this.props.actions.redirectToOnboardingOrDefaultTeam(this.props.history);
|
this.props.actions.redirectToOnboardingOrDefaultTeam(this.props.history, new URLSearchParams(this.props.location.search));
|
||||||
} else if (this.props.noAccounts) {
|
} else if (this.props.noAccounts) {
|
||||||
this.props.history.push('/signup_user_complete');
|
this.props.history.push('/signup_user_complete');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React, {useEffect} from 'react';
|
import React, {useEffect} from 'react';
|
||||||
import {Redirect, useHistory} from 'react-router-dom';
|
import {Redirect, useHistory, useLocation} from 'react-router-dom';
|
||||||
|
|
||||||
import type {ActionResult} from 'mattermost-redux/types/actions';
|
import type {ActionResult} from 'mattermost-redux/types/actions';
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ export type Props = {
|
|||||||
|
|
||||||
export default function RootRedirect(props: Props) {
|
export default function RootRedirect(props: Props) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.currentUserId) {
|
if (props.currentUserId) {
|
||||||
@@ -30,11 +31,11 @@ export default function RootRedirect(props: Props) {
|
|||||||
if (firstAdminCompletedSignup.data === false && props.isFirstAdmin && !props.areThereTeams) {
|
if (firstAdminCompletedSignup.data === false && props.isFirstAdmin && !props.areThereTeams) {
|
||||||
history.push('/preparing-workspace');
|
history.push('/preparing-workspace');
|
||||||
} else {
|
} else {
|
||||||
GlobalActions.redirectUserToDefaultTeam();
|
GlobalActions.redirectUserToDefaultTeam(new URLSearchParams(location.search));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
GlobalActions.redirectUserToDefaultTeam();
|
GlobalActions.redirectUserToDefaultTeam(new URLSearchParams(location.search));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [props.currentUserId, props.isElegibleForFirstAdmingOnboarding]);
|
}, [props.currentUserId, props.isElegibleForFirstAdmingOnboarding]);
|
||||||
|
|||||||
@@ -4011,7 +4011,7 @@
|
|||||||
"invite_modal.invited_guests": "Guests",
|
"invite_modal.invited_guests": "Guests",
|
||||||
"invite_modal.invited_members": "Members",
|
"invite_modal.invited_members": "Members",
|
||||||
"invite_modal.no_permissions.description": "You do not have permissions to add users or guests. If this seems like an error, please reach out to your system administrator.",
|
"invite_modal.no_permissions.description": "You do not have permissions to add users or guests. If this seems like an error, please reach out to your system administrator.",
|
||||||
"invite_modal.no_permissions.title": "Unable to continue",
|
"invite_modal.no_permissions.title": "Unable to invite people",
|
||||||
"invite_modal.people": "people",
|
"invite_modal.people": "people",
|
||||||
"invite_modal.restricted_invite_guest.post_trial_description": "Collaborate with users outside of your organization while tightly controlling their access to channels and team members. Upgrade to the Professional plan to create unlimited user groups.",
|
"invite_modal.restricted_invite_guest.post_trial_description": "Collaborate with users outside of your organization while tightly controlling their access to channels and team members. Upgrade to the Professional plan to create unlimited user groups.",
|
||||||
"invite_modal.restricted_invite_guest.post_trial_title": "Upgrade to invite guest",
|
"invite_modal.restricted_invite_guest.post_trial_title": "Upgrade to invite guest",
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user