Fix types so the store can return undefined teams (#26386)

* Fix types so the store can return undefined teams

* Fix post test

* fix snapshots

* Address feedback
This commit is contained in:
Daniel Espino García 2024-04-22 12:42:13 +02:00 committed by GitHub
parent 7b90b7c2e0
commit d0a67cd84a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
82 changed files with 332 additions and 206 deletions

View File

@ -37,7 +37,7 @@ import {
getCurrentRelativeTeamUrl,
getCurrentTeam,
getCurrentTeamId,
getTeam,
getRelativeTeamUrl,
getTeamsList,
} from 'mattermost-redux/selectors/entities/teams';
import {getCurrentUserId, getUserByUsername} from 'mattermost-redux/selectors/entities/users';
@ -103,7 +103,7 @@ export function switchToChannel(channel: Channel & {userId?: string}): ActionFun
return async (dispatch, getState) => {
const state = getState();
const selectedTeamId = channel.team_id;
const teamUrl = selectedTeamId ? `/${getTeam(state, selectedTeamId).name}` : getCurrentRelativeTeamUrl(state);
const teamUrl = selectedTeamId ? getRelativeTeamUrl(state, selectedTeamId) : getCurrentRelativeTeamUrl(state);
if (channel.userId) {
const username = channel.userId ? channel.name : channel.display_name;
@ -145,6 +145,9 @@ export function leaveChannel(channelId: string): ActionFuncAsync {
let state = getState();
const currentUserId = getCurrentUserId(state);
const currentTeam = getCurrentTeam(state);
if (!currentTeam) {
return {data: false};
}
const channel = getChannel(state, channelId);
const currentChannelId = getCurrentChannelId(state);

View File

@ -23,6 +23,10 @@ export function switchToChannels(): ActionFuncAsync<boolean> {
const teamId = getCurrentTeamId(state) || LocalStorageStore.getPreviousTeamId(currentUserId);
const team = getTeam(state, teamId || '');
if (!team) {
return {data: false};
}
const channel = await getTeamRedirectChannelIfIsAccesible(user, team);
const channelName = channel?.name || Constants.DEFAULT_CHANNEL;

View File

@ -26,7 +26,7 @@ const MAX_SELECTABLE_VALUES = 10;
type GroupValue = Value & {member_count?: number};
type Props = {
currentTeamName: string;
currentTeamName?: string;
currentTeamId: string;
intl: IntlShape;
searchTerm: string;
@ -291,7 +291,7 @@ export class AddGroupsToTeamModal extends React.PureComponent<Props, State> {
defaultMessage='Add New Groups to {teamName} Team'
values={{
teamName: (
<strong>{this.props.currentTeamName}</strong>
<strong>{this.props.currentTeamName ?? ''}</strong>
),
}}
/>

View File

@ -10,7 +10,7 @@ import type {Team} from '@mattermost/types/teams';
import {getGroupsNotAssociatedToTeam, linkGroupSyncable, getAllGroupsAssociatedToTeam} from 'mattermost-redux/actions/groups';
import {getGroupsNotAssociatedToTeam as selectGroupsNotAssociatedToTeam} from 'mattermost-redux/selectors/entities/groups';
import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams';
import {getCurrentTeam, getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {setModalSearchTerm} from 'actions/views/search';
@ -28,17 +28,17 @@ type Props = {
function mapStateToProps(state: GlobalState, ownProps: Props) {
const searchTerm = state.views.search.modalSearch;
const team = ownProps.team || getCurrentTeam(state) || {};
const team = ownProps.team || getCurrentTeam(state);
let groups = selectGroupsNotAssociatedToTeam(state, team.id);
let groups = selectGroupsNotAssociatedToTeam(state, team?.id || '');
if (searchTerm) {
const regex = RegExp(searchTerm, 'i');
groups = groups.filter((group) => regex.test(group.display_name) || regex.test(group.name));
}
return {
currentTeamName: team.display_name,
currentTeamId: team.id,
currentTeamName: team?.display_name,
currentTeamId: team?.id ?? getCurrentTeamId(state),
skipCommit: ownProps.skipCommit,
onAddCallback: ownProps.onAddCallback,
excludeGroups: ownProps.excludeGroups,

View File

@ -50,7 +50,21 @@ exports[`admin_console/team_channel_settings/channel/ChannelDetails should match
onToggleArchive={[Function]}
team={
Object {
"allow_open_invite": false,
"allowed_domains": "",
"company_name": "",
"create_at": 0,
"delete_at": 0,
"description": "",
"display_name": "test",
"email": "",
"group_constrained": false,
"id": "team_id",
"invite_id": "",
"name": "DN",
"scheme_id": "id",
"type": "O",
"update_at": 0,
}
}
/>
@ -235,7 +249,6 @@ exports[`admin_console/team_channel_settings/channel/ChannelDetails should match
isArchived={false}
isDisabled={false}
onToggleArchive={[Function]}
team={Object {}}
/>
<ConfirmModal
confirmButtonClass="btn btn-primary"
@ -420,7 +433,21 @@ exports[`admin_console/team_channel_settings/channel/ChannelDetails should match
onToggleArchive={[Function]}
team={
Object {
"allow_open_invite": false,
"allowed_domains": "",
"company_name": "",
"create_at": 0,
"delete_at": 0,
"description": "",
"display_name": "test",
"email": "",
"group_constrained": false,
"id": "team_id",
"invite_id": "",
"name": "DN",
"scheme_id": "id",
"type": "O",
"update_at": 0,
}
}
/>
@ -557,7 +584,6 @@ exports[`admin_console/team_channel_settings/channel/ChannelDetails should match
isArchived={false}
isDisabled={false}
onToggleArchive={[Function]}
team={Object {}}
/>
<ConfirmModal
confirmButtonClass="btn btn-primary"
@ -694,7 +720,21 @@ exports[`admin_console/team_channel_settings/channel/ChannelDetails should match
onToggleArchive={[Function]}
team={
Object {
"allow_open_invite": false,
"allowed_domains": "",
"company_name": "",
"create_at": 0,
"delete_at": 0,
"description": "",
"display_name": "test",
"email": "",
"group_constrained": false,
"id": "team_id",
"invite_id": "",
"name": "DN",
"scheme_id": "id",
"type": "O",
"update_at": 0,
}
}
/>
@ -831,7 +871,6 @@ exports[`admin_console/team_channel_settings/channel/ChannelDetails should match
isArchived={false}
isDisabled={false}
onToggleArchive={[Function]}
team={Object {}}
/>
<ConfirmModal
confirmButtonClass="btn btn-primary"

View File

@ -7,7 +7,8 @@ import React from 'react';
import type {Channel} from '@mattermost/types/channels';
import type {Group} from '@mattermost/types/groups';
import type {Scheme} from '@mattermost/types/schemes';
import type {Team} from '@mattermost/types/teams';
import {TestHelper} from 'utils/test_helper';
import ChannelDetails from './channel_details';
@ -49,9 +50,9 @@ describe('admin_console/team_channel_settings/channel/ChannelDetails', () => {
scheme_id: 'id',
group_constrained: false,
};
const team: Partial<Team> = {
const team = TestHelper.getTeamMock({
display_name: 'test',
};
});
const teamScheme: Scheme = {
id: 'asdf',
name: 'asdf',
@ -125,7 +126,7 @@ describe('admin_console/team_channel_settings/channel/ChannelDetails', () => {
<ChannelDetails
teamScheme={teamScheme}
groups={groups}
team={{}}
team={undefined}
totalGroups={groups.length}
actions={actions}
channel={testChannel}
@ -173,9 +174,9 @@ describe('admin_console/team_channel_settings/channel/ChannelDetails', () => {
scheme_id: 'id',
group_constrained: false,
};
const team: Partial<Team> = {
const team = TestHelper.getTeamMock({
display_name: 'test',
};
});
const teamScheme: Scheme = {
id: 'asdf',
name: 'asdf',
@ -249,7 +250,7 @@ describe('admin_console/team_channel_settings/channel/ChannelDetails', () => {
<ChannelDetails
teamScheme={teamScheme}
groups={groups}
team={{}}
team={undefined}
totalGroups={groups.length}
actions={actions}
channel={testChannel}
@ -298,9 +299,9 @@ describe('admin_console/team_channel_settings/channel/ChannelDetails', () => {
scheme_id: 'id',
group_constrained: false,
};
const team: Partial<Team> = {
const team = TestHelper.getTeamMock({
display_name: 'test',
};
});
const teamScheme: Scheme = {
id: 'asdf',
name: 'asdf',
@ -374,7 +375,7 @@ describe('admin_console/team_channel_settings/channel/ChannelDetails', () => {
<ChannelDetails
teamScheme={teamScheme}
groups={groups}
team={{}}
team={undefined}
totalGroups={groups.length}
actions={actions}
channel={testChannel}

View File

@ -41,7 +41,7 @@ import SaveChangesPanel from '../../save_changes_panel';
export interface ChannelDetailsProps {
channelID: string;
channel: Channel;
team: Partial<Team>;
team?: Team;
groups: Group[];
totalGroups: number;
allGroups: Record<string, Group>;
@ -145,7 +145,7 @@ export default class ChannelDetails extends React.PureComponent<ChannelDetailsPr
}
// If we don't have the team and channel on mount, we need to request the team after we load the channel
if (!prevProps.team.id && !prevProps.channel.team_id && channel.team_id) {
if (!prevProps.team?.id && !prevProps.channel.team_id && channel.team_id) {
actions.getTeam(channel.team_id).
then(async (data: any) => {
if (data.data && data.data.scheme_id) {

View File

@ -5,13 +5,14 @@ import {shallow} from 'enzyme';
import React from 'react';
import type {Channel} from '@mattermost/types/channels';
import type {Team} from '@mattermost/types/teams';
import {TestHelper} from 'utils/test_helper';
import {ChannelProfile} from './channel_profile';
describe('admin_console/team_channel_settings/channel/ChannelProfile', () => {
test('should match snapshot', () => {
const testTeam: Partial<Team> = {display_name: 'test'};
const testTeam = TestHelper.getTeamMock({display_name: 'test'});
const testChannel: Partial<Channel> = {display_name: 'test'};
const wrapper = shallow(
<ChannelProfile
@ -24,7 +25,7 @@ describe('admin_console/team_channel_settings/channel/ChannelProfile', () => {
});
test('should match snapshot for a shared channel', () => {
const testTeam: Partial<Team> = {display_name: 'test'};
const testTeam = TestHelper.getTeamMock({display_name: 'test'});
const testChannel: Partial<Channel> = {
display_name: 'test',
type: 'O',

View File

@ -15,7 +15,7 @@ import AdminPanel from 'components/widgets/admin_console/admin_panel';
import './channel_profile.scss';
interface ChannelProfileProps {
channel: Partial<Channel>;
team: Partial<Team>;
team?: Team;
onToggleArchive?: () => void;
isArchived: boolean;
isDisabled?: boolean;
@ -71,7 +71,7 @@ export const ChannelProfile = (props: ChannelProfileProps): JSX.Element => {
defaultMessage='**Team**'
/>
<br/>
{team.display_name}
{team?.display_name}
</div>
{sharedBlock}
<div className='AdminChannelDetails_archiveContainer'>

View File

@ -63,12 +63,12 @@ function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
const guestAccountsEnabled = config.EnableGuestAccounts === 'true';
const channelID = ownProps.match.params.channel_id;
const channel = getChannel(state, channelID) || {};
const team = getTeam(state, channel.team_id) || {};
const team = getTeam(state, channel.team_id);
const groups = getGroupsAssociatedToChannel(state, channelID);
const totalGroups = groups.length;
const allGroups = getAllGroups(state);
const channelPermissions = getChannelModerations(state, channelID);
const teamScheme = getScheme(state, team.scheme_id);
const teamScheme = team ? getScheme(state, team.scheme_id) : undefined;
return {
channelID,
channel,

View File

@ -32,7 +32,7 @@ import SaveChangesPanel from '../../save_changes_panel';
export type Props = {
teamID: string;
team: Team;
team?: Team;
totalGroups: number;
groups: Group[];
allGroups: Record<string, Group>;
@ -90,10 +90,10 @@ export default class TeamDetails extends React.PureComponent<Props, State> {
const team = props.team;
this.state = {
groups: props.groups,
syncChecked: Boolean(team.group_constrained),
allAllowedChecked: team.allow_open_invite,
allowedDomainsChecked: Boolean(team.allowed_domains && team.allowed_domains !== ''),
allowedDomains: team.allowed_domains || '',
syncChecked: Boolean(team?.group_constrained),
allAllowedChecked: Boolean(team?.allow_open_invite),
allowedDomainsChecked: Boolean(team?.allowed_domains),
allowedDomains: team?.allowed_domains || '',
saving: false,
showRemoveConfirmation: false,
usersToRemoveCount: 0,
@ -104,21 +104,21 @@ export default class TeamDetails extends React.PureComponent<Props, State> {
saveNeeded: false,
serverError: undefined,
previousServerError: undefined,
isLocalArchived: team.delete_at > 0,
isLocalArchived: team ? team.delete_at > 0 : true,
showArchiveConfirmModal: false,
};
}
componentDidUpdate(prevProps: Props) {
const {totalGroups, team} = this.props;
if (prevProps.team.id !== team.id || totalGroups !== prevProps.totalGroups) {
if (prevProps.team?.id !== team?.id || totalGroups !== prevProps.totalGroups) {
this.setState({
totalGroups,
syncChecked: Boolean(team.group_constrained),
allAllowedChecked: team.allow_open_invite,
allowedDomainsChecked: Boolean(team.allowed_domains && team.allowed_domains !== ''),
allowedDomains: team.allowed_domains || '',
isLocalArchived: team.delete_at > 0,
syncChecked: Boolean(team?.group_constrained),
allAllowedChecked: Boolean(team?.allow_open_invite),
allowedDomainsChecked: Boolean(team?.allowed_domains),
allowedDomains: team?.allowed_domains || '',
isLocalArchived: team ? team.delete_at > 0 : true,
});
}
}
@ -141,12 +141,16 @@ export default class TeamDetails extends React.PureComponent<Props, State> {
};
handleSubmit = async () => {
const {team, groups: origGroups, teamID, actions} = this.props;
if (!team) {
return;
}
this.setState({showRemoveConfirmation: false, saving: true});
const {groups, allAllowedChecked, allowedDomainsChecked, allowedDomains, syncChecked, usersToAdd, usersToRemove, rolesToUpdate} = this.state;
let serverError: JSX.Element | undefined;
const {team, groups: origGroups, teamID, actions} = this.props;
if (this.teamToBeArchived()) {
let saveNeeded = false;
const result = await actions.deleteTeam(team.id);
@ -402,13 +406,13 @@ export default class TeamDetails extends React.PureComponent<Props, State> {
teamToBeArchived = () => {
const {isLocalArchived} = this.state;
const isServerArchived = this.props.team.delete_at !== 0;
const isServerArchived = this.props.team?.delete_at !== 0;
return isLocalArchived && !isServerArchived;
};
teamToBeRestored = () => {
const {isLocalArchived} = this.state;
const isServerArchived = this.props.team.delete_at !== 0;
const isServerArchived = this.props.team?.delete_at !== 0;
return !isLocalArchived && isServerArchived;
};
@ -444,6 +448,11 @@ export default class TeamDetails extends React.PureComponent<Props, State> {
render = () => {
const {team, isLicensedForLDAPGroups} = this.props;
if (!team) {
return null;
}
const {totalGroups, saving, saveNeeded, serverError, groups, allAllowedChecked, allowedDomainsChecked, allowedDomains, syncChecked, showRemoveConfirmation, usersToRemoveCount, isLocalArchived, showArchiveConfirmModal} = this.state;
const missingGroup = (og: {id: string}) => !groups.find((g) => g.id === og.id);
const removedGroups = this.props.groups.filter(missingGroup);

View File

@ -39,7 +39,7 @@ function mapStateToProps(state: GlobalState, props: Props) {
let {usersToAdd} = props;
const teamMembers = getMembersInTeams(state)[teamId] || {};
const team = getTeam(state, teamId) || {};
const team = getTeam(state, teamId);
const config = getConfig(state);
const searchTerm = state.views.search.userGridSearch?.term || '';
const filters = state.views.search.userGridSearch?.filters || {};

View File

@ -23,7 +23,7 @@ import Constants, {ModalIdentifiers} from 'utils/constants';
type Props = {
teamId: string;
team: Team;
team?: Team;
filters: GetFilteredUsersStatsOpts;
users: UserProfile[];

View File

@ -66,7 +66,7 @@ type Props = {
/**
* Current team.
*/
team: Team;
team?: Team;
/**
* Object from react-router

View File

@ -38,7 +38,7 @@ function mapStateToProps(state: GlobalState) {
}
}
const canManageTeamIntegrations = (haveITeamPermission(state, team.id, Permissions.MANAGE_SLASH_COMMANDS) || haveITeamPermission(state, team.id, Permissions.MANAGE_OAUTH) || haveITeamPermission(state, team.id, Permissions.MANAGE_INCOMING_WEBHOOKS) || haveITeamPermission(state, team.id, Permissions.MANAGE_OUTGOING_WEBHOOKS));
const canManageTeamIntegrations = (haveITeamPermission(state, team?.id, Permissions.MANAGE_SLASH_COMMANDS) || haveITeamPermission(state, team?.id, Permissions.MANAGE_OAUTH) || haveITeamPermission(state, team?.id, Permissions.MANAGE_INCOMING_WEBHOOKS) || haveITeamPermission(state, team?.id, Permissions.MANAGE_OUTGOING_WEBHOOKS));
const canManageSystemBots = (haveISystemPermission(state, {permission: Permissions.MANAGE_BOTS}) || haveISystemPermission(state, {permission: Permissions.MANAGE_OTHERS_BOTS}));
const canManageIntegrations = canManageTeamIntegrations || canManageSystemBots;

View File

@ -61,7 +61,7 @@ export type Props = {
privateChannels: Channel[];
currentUserId: string;
teamId: string;
teamName: string;
teamName?: string;
channelsRequestStarted?: boolean;
canShowArchivedChannels?: boolean;
myChannelMemberships: RelationOneToOne<Channel, ChannelMembership>;
@ -103,6 +103,11 @@ export default class BrowseChannels extends React.PureComponent<Props, State> {
}
componentDidMount() {
if (!this.props.teamId) {
this.loadComplete();
return;
}
const promises = [
this.props.actions.getChannels(this.props.teamId, 0, CHANNELS_CHUNK_SIZE * 2),
];
@ -178,7 +183,7 @@ export default class BrowseChannels extends React.PureComponent<Props, State> {
this.setState({serverError: result.error.message});
} else {
this.props.actions.getChannelsMemberCount([channel.id]);
getHistory().push(getRelativeChannelURL(teamName, channel.name));
getHistory().push(getRelativeChannelURL(teamName!, channel.name));
this.closeEditRHS();
}
@ -288,6 +293,7 @@ export default class BrowseChannels extends React.PureComponent<Props, State> {
render() {
const {teamId, channelsRequestStarted, shouldHideJoinedChannels} = this.props;
const {search, serverError: serverErrorState, searching} = this.state;
this.activeChannels = this.getActiveChannels();
let serverError;

View File

@ -12,7 +12,7 @@ import {RequestStatus} from 'mattermost-redux/constants';
import {createSelector} from 'mattermost-redux/selectors/create_selector';
import {getChannelsInCurrentTeam, getMyChannelMemberships, getChannelsMemberCount as getChannelsMemberCountSelector} from 'mattermost-redux/selectors/entities/channels';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams';
import {getCurrentTeam, getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
import {setGlobalItem} from 'actions/storage';
@ -46,7 +46,7 @@ const getPrivateChannelsSelector = createSelector(
);
function mapStateToProps(state: GlobalState) {
const team = getCurrentTeam(state) || {};
const team = getCurrentTeam(state);
const getGlobalItem = makeGetGlobalItem(StoragePrefixes.HIDE_JOINED_CHANNELS, 'false');
return {
@ -54,8 +54,8 @@ function mapStateToProps(state: GlobalState) {
archivedChannels: getArchivedOtherChannels(state) || [],
privateChannels: getPrivateChannelsSelector(state) || [],
currentUserId: getCurrentUserId(state),
teamId: team.id,
teamName: team.name,
teamId: getCurrentTeamId(state),
teamName: team?.name,
channelsRequestStarted: state.requests.channels.getChannels.status === RequestStatus.STARTED,
canShowArchivedChannels: (getConfig(state).ExperimentalViewArchivedChannels === 'true'),
myChannelMemberships: getMyChannelMemberships(state) || {},

View File

@ -24,7 +24,7 @@ type Props = {
/**
* Object with info about currentTeam
*/
currentTeam: Team;
currentTeam?: Team;
/**
* String with info about redirect
@ -86,7 +86,9 @@ export default class CloseMessage extends React.PureComponent<Props> {
leaveDirectChannel(channel.name);
savePreferences(currentUser.id, [{user_id: currentUser.id, category, name, value: 'false'}]);
getHistory().push(`/${currentTeam.name}/channels/${redirectChannel}`);
if (currentTeam) {
getHistory().push(`/${currentTeam.name}/channels/${redirectChannel}`);
}
};
render(): React.ReactNode {

View File

@ -47,8 +47,8 @@ function mapStateToProps(state: GlobalState) {
const isMobile = getIsMobileView(state);
const isPrivate = channel.type === Constants.PRIVATE_CHANNEL;
const canManageMembers = haveIChannelPermission(state, currentTeam.id, channel.id, isPrivate ? Permissions.MANAGE_PRIVATE_CHANNEL_MEMBERS : Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS);
const canManageProperties = haveIChannelPermission(state, currentTeam.id, channel.id, isPrivate ? Permissions.MANAGE_PRIVATE_CHANNEL_PROPERTIES : Permissions.MANAGE_PUBLIC_CHANNEL_PROPERTIES);
const canManageMembers = haveIChannelPermission(state, currentTeam?.id, channel.id, isPrivate ? Permissions.MANAGE_PRIVATE_CHANNEL_MEMBERS : Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS);
const canManageProperties = haveIChannelPermission(state, currentTeam?.id, channel.id, isPrivate ? Permissions.MANAGE_PRIVATE_CHANNEL_PROPERTIES : Permissions.MANAGE_PUBLIC_CHANNEL_PROPERTIES);
const channelMembers = getProfilesInCurrentChannel(state);

View File

@ -47,7 +47,7 @@ export type Props = {
profilesNotInCurrentTeam: UserProfile[];
profilesFromRecentDMs: UserProfile[];
intl: IntlShape;
membersInTeam: RelationOneToOne<UserProfile, TeamMembership>;
membersInTeam?: RelationOneToOne<UserProfile, TeamMembership>;
userStatuses: RelationOneToOne<UserProfile, string>;
onExited: () => void;
channel: Channel;

View File

@ -5,9 +5,7 @@ import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import type {Dispatch} from 'redux';
import type {TeamMembership} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
import type {RelationOneToOne} from '@mattermost/types/utilities';
import {getTeamStats, getTeamMembersByIds} from 'mattermost-redux/actions/teams';
import {getProfilesNotInChannel, getProfilesInChannel, searchProfiles} from 'mattermost-redux/actions/users';
@ -50,7 +48,7 @@ function makeMapStateToProps(initialState: GlobalState, initialProps: OwnProps)
let profilesNotInCurrentChannel: UserProfile[];
let profilesInCurrentChannel: UserProfile[];
let profilesNotInCurrentTeam: UserProfile[];
let membersInTeam: RelationOneToOne<UserProfile, TeamMembership>;
let membersInTeam;
if (props.channelId && props.teamId) {
profilesNotInCurrentChannel = doGetProfilesNotInChannel(state, props.channelId);
@ -72,7 +70,7 @@ function makeMapStateToProps(initialState: GlobalState, initialProps: OwnProps)
const guestAccountsEnabled = config.EnableGuestAccounts === 'true';
const emailInvitationsEnabled = config.EnableEmailInvitations === 'true';
const isLicensed = license && license.IsLicensed === 'true';
const isGroupConstrained = Boolean(currentTeam.group_constrained);
const isGroupConstrained = Boolean(currentTeam?.group_constrained);
const canInviteGuests = !isGroupConstrained && isLicensed && guestAccountsEnabled && haveICurrentTeamPermission(state, Permissions.INVITE_GUEST);
const enableCustomUserGroups = isCustomGroupsEnabled(state);

View File

@ -110,11 +110,11 @@ const TeamWarningBanner = (props: Props) => {
mentionName={firstName}
/>
),
team: (<strong>{team.display_name}</strong>),
team: (<strong>{team?.display_name}</strong>),
},
)
);
}, [guests, formatMessage, getCommaSeparatedUsernames, team.display_name]);
}, [guests, formatMessage, getCommaSeparatedUsernames, team?.display_name]);
const getMessage = useCallback(() => {
const commaSeparatedUsernames = getCommaSeparatedUsernames(users);
@ -151,7 +151,7 @@ const TeamWarningBanner = (props: Props) => {
</span>
</SimpleTooltip>
),
team: (<strong>{team.display_name}</strong>),
team: (<strong>{team?.display_name}</strong>),
},
);
}
@ -180,7 +180,7 @@ const TeamWarningBanner = (props: Props) => {
mentionName={firstName}
/>
),
team: (<strong>{team.display_name}</strong>),
team: (<strong>{team?.display_name}</strong>),
},
)
);

View File

@ -118,7 +118,7 @@ function mapStateToProps(state: GlobalState) {
const isPrivate = channel.type === Constants.PRIVATE_CHANNEL;
const canManageMembers = haveIChannelPermission(
state,
currentTeam.id,
currentTeam?.id,
channel.id,
isPrivate ? Permissions.MANAGE_PRIVATE_CHANNEL_MEMBERS : Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS,
) && !isArchived;

View File

@ -61,7 +61,7 @@ const ChannelNameFormField = (props: Props): JSX.Element => {
const [urlError, setURLError] = useState<string>('');
const [inputCustomMessage, setInputCustomMessage] = useState<CustomMessageInputType | null>(null);
const {name: currentTeamName} = useSelector(getCurrentTeam);
const currentTeamName = useSelector(getCurrentTeam)?.name;
const teamName = props.team ? props.team.name : currentTeamName;
const handleOnDisplayNameChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {

View File

@ -21,7 +21,7 @@ export type Props = {
/*
* Object containing information on the current team, used to define BackButton's url
*/
currentTeam: Team;
currentTeam?: Team;
/*
* Object containing information on the current selected channel, used to define BackButton's url

View File

@ -15,7 +15,7 @@ import Constants from 'utils/constants';
export type Props = {
onExited: () => void;
channel: Channel;
currentTeamDetails: {name: string};
currentTeamDetails?: {name: string};
canViewArchivedChannels?: boolean;
penultimateViewedChannelName: string;
actions: {
@ -39,7 +39,9 @@ export default class DeleteChannelModal extends React.PureComponent<Props, State
}
if (!this.props.canViewArchivedChannels) {
const {penultimateViewedChannelName} = this.props;
getHistory().push('/' + this.props.currentTeamDetails.name + '/channels/' + penultimateViewedChannelName);
if (this.props.currentTeamDetails) {
getHistory().push('/' + this.props.currentTeamDetails.name + '/channels/' + penultimateViewedChannelName);
}
}
this.props.actions.deleteChannel(this.props.channel.id);
this.onHide();

View File

@ -66,9 +66,9 @@ function makeMapStateToProps() {
const config = getConfig(state);
const userId = getCurrentUserId(state);
const channel = getChannel(state, post.channel_id);
const currentTeam = getCurrentTeam(state) || {};
const currentTeam = getCurrentTeam(state);
const team = getTeam(state, channel.team_id);
const teamUrl = `${getSiteURL()}/${team?.name || currentTeam.name}`;
const teamUrl = `${getSiteURL()}/${team?.name || currentTeam?.name}`;
const isMilitaryTime = getBool(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false);
const systemMessage = isSystemMessage(post);

View File

@ -6,7 +6,7 @@ import {bindActionCreators} from 'redux';
import type {Dispatch} from 'redux';
import {renameCategory} from 'mattermost-redux/actions/channel_categories';
import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams';
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {createCategory} from 'actions/views/channel_sidebar';
@ -15,10 +15,8 @@ import type {GlobalState} from 'types/store';
import EditCategoryModal from './edit_category_modal';
function mapStateToProps(state: GlobalState) {
const currentTeam = getCurrentTeam(state);
return {
currentTeamId: currentTeam.id,
currentTeamId: getCurrentTeamId(state),
};
}

View File

@ -15,7 +15,6 @@ import * as Utils from 'utils/utils';
import EmojiList from './emoji_list';
type Props = {
teamId: string;
teamName?: string;
teamDisplayName?: string;
siteName?: string;

View File

@ -14,12 +14,11 @@ import EmojiPage from 'components/emoji/emoji_page';
import type {GlobalState} from 'types/store';
function mapStateToProps(state: GlobalState) {
const team = getCurrentTeam(state) || {};
const team = getCurrentTeam(state);
return {
teamId: team.id,
teamName: team.name,
teamDisplayName: team.display_name,
teamName: team?.name,
teamDisplayName: team?.display_name,
siteName: state.entities.general.config.SiteName,
currentTheme: getTheme(state),
};

View File

@ -46,7 +46,7 @@ const ForwardPostModal = ({onExited, post, actions}: Props) => {
const channel = useSelector((state: GlobalState) => getChannel(state, {id: post.channel_id}));
const currentTeam = useSelector(getCurrentTeam);
const relativePermaLink = useSelector((state: GlobalState) => getPermalinkURL(state, currentTeam.id, post.id));
const relativePermaLink = useSelector((state: GlobalState) => (currentTeam ? getPermalinkURL(state, currentTeam.id, post.id) : ''));
const permaLink = `${getSiteURL()}${relativePermaLink}`;
const isPrivateConversation = channel.type !== Constants.OPEN_CHANNEL;
@ -79,11 +79,11 @@ const ForwardPostModal = ({onExited, post, actions}: Props) => {
(state: GlobalState) => {
const channelId = isPrivateConversation ? channel.id : selectedChannelId;
const isDMChannel = selectedChannel?.details?.type === Constants.DM_CHANNEL;
const teamId = isPrivateConversation ? currentTeam.id : selectedChannel?.details?.team_id;
const teamId = isPrivateConversation ? currentTeam?.id : selectedChannel?.details?.team_id;
const hasChannelPermission = haveIChannelPermission(
state,
teamId || currentTeam.id,
teamId || currentTeam?.id,
channelId,
Permissions.CREATE_POST,
);
@ -122,7 +122,7 @@ const ForwardPostModal = ({onExited, post, actions}: Props) => {
const previewMetaData: PostPreviewMetadata = {
post,
post_id: post.id,
team_name: currentTeam.name,
team_name: currentTeam?.name || '',
channel_display_name: channel.display_name,
channel_type: channel.type,
channel_id: channel.id,

View File

@ -36,7 +36,7 @@ import ProductMenuList from './product_menu_list';
function mapStateToProps(state: GlobalState) {
const config = getConfig(state);
const currentTeam = getCurrentTeam(state) || {};
const currentTeam = getCurrentTeam(state);
const currentUser = getCurrentUser(state);
const appDownloadLink = config.AppDownloadLink || '';
@ -81,8 +81,8 @@ function mapStateToProps(state: GlobalState) {
enablePluginMarketplace,
pluginMenuItems: state.plugins.components.MainMenu,
siteName,
teamId: currentTeam.id,
teamName: currentTeam.name,
teamId: currentTeam?.id,
teamName: currentTeam?.name,
currentUser,
firstAdminVisitMarketplaceStatus: getFirstAdminVisitMarketplaceStatus(state),
showVisitSystemConsoleTour,

View File

@ -36,8 +36,8 @@ import './product_menu_list.scss';
export type Props = {
isMobile: boolean;
teamId: string;
teamName: string;
teamId?: string;
teamName?: string;
siteName: string;
currentUser: UserProfile;
appDownloadLink: string;

View File

@ -7,9 +7,11 @@ import React from 'react';
import DeleteIntegrationLink from 'components/integrations/delete_integration_link';
import InstalledOAuthApp from 'components/integrations/installed_oauth_app/installed_oauth_app';
import {TestHelper} from 'utils/test_helper';
describe('components/integrations/InstalledOAuthApp', () => {
const FAKE_SECRET = '***************';
const team = {name: 'team_name'};
const team = TestHelper.getTeamMock({name: 'team_name'});
const oauthApp = {
id: 'facxd9wpzpbpfp8pad78xj75pr',
name: 'testApp',

View File

@ -31,7 +31,7 @@ export type InstalledOAuthAppProps = {
/**
* The team data
*/
team: Partial<Team>;
team?: Team;
/**
* The oauthApp data
@ -255,7 +255,7 @@ export default class InstalledOAuthApp extends React.PureComponent<InstalledOAut
{' - '}
{regen}
{' - '}
<Link to={`/${this.props.team.name}/integrations/oauth2-apps/edit?id=${oauthApp.id}`}>
<Link to={`/${this.props.team?.name}/integrations/oauth2-apps/edit?id=${oauthApp.id}`}>
<FormattedMessage
id='installed_integrations.edit'
defaultMessage='Edit'

View File

@ -85,7 +85,21 @@ exports[`components/integrations/InstalledOAuthApps should match snapshot 2`] =
onRegenerateSecret={[MockFunction]}
team={
Object {
"allow_open_invite": false,
"allowed_domains": "",
"company_name": "",
"create_at": 0,
"delete_at": 0,
"description": "",
"display_name": "name",
"email": "",
"group_constrained": false,
"id": "team_id",
"invite_id": "",
"name": "test",
"scheme_id": "id",
"type": "O",
"update_at": 0,
}
}
/>
@ -122,7 +136,21 @@ exports[`components/integrations/InstalledOAuthApps should match snapshot for Ap
onRegenerateSecret={[MockFunction]}
team={
Object {
"allow_open_invite": false,
"allowed_domains": "",
"company_name": "",
"create_at": 0,
"delete_at": 0,
"description": "",
"display_name": "name",
"email": "",
"group_constrained": false,
"id": "team_id",
"invite_id": "",
"name": "test",
"scheme_id": "id",
"type": "O",
"update_at": 0,
}
}
/>

View File

@ -7,6 +7,8 @@ import React from 'react';
import BackstageList from 'components/backstage/components/backstage_list';
import InstalledOAuthApps from 'components/integrations/installed_oauth_apps/installed_oauth_apps';
import {TestHelper} from 'utils/test_helper';
describe('components/integrations/InstalledOAuthApps', () => {
const oauthApps = {
facxd9wpzpbpfp8pad78xj75pr: {
@ -38,9 +40,9 @@ describe('components/integrations/InstalledOAuthApps', () => {
};
const baseProps = {
team: {
team: TestHelper.getTeamMock({
name: 'test',
},
}),
oauthApps,
canManageOauth: true,
actions: {

View File

@ -5,6 +5,7 @@ import React from 'react';
import {FormattedMessage} from 'react-intl';
import type {OAuthApp} from '@mattermost/types/integrations';
import type {Team} from '@mattermost/types/teams';
import type {ActionResult} from 'mattermost-redux/types/actions';
@ -23,7 +24,7 @@ type Props = {
/**
* The team data
*/
team: {name: string};
team?: Team;
/**
* The oauthApps data
@ -123,7 +124,10 @@ export default class InstalledOAuthApps extends React.PureComponent<Props, State
);
});
render(): JSX.Element {
render() {
if (!this.props.team) {
return null;
}
const integrationsEnabled = this.props.enableOAuthServiceProvider && this.props.canManageOauth;
let props;
if (integrationsEnabled) {

View File

@ -178,6 +178,6 @@ describe('mapStateToProps', () => {
const props = mapStateToProps(testState, {channelToInvite: testChannel});
expect(props.currentTeam.id).toBe(testChannel.team_id);
expect(props.currentTeam?.id).toBe(testChannel.team_id);
});
});

View File

@ -63,14 +63,14 @@ export function mapStateToProps(state: GlobalState, props: OwnProps) {
return false;
}
if (channel.type === Constants.PRIVATE_CHANNEL) {
return haveIChannelPermission(state, currentTeam.id, channel.id, Permissions.MANAGE_PRIVATE_CHANNEL_MEMBERS);
return haveIChannelPermission(state, currentTeam?.id, channel.id, Permissions.MANAGE_PRIVATE_CHANNEL_MEMBERS);
}
return haveIChannelPermission(state, currentTeam.id, channel.id, Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS);
return haveIChannelPermission(state, currentTeam?.id, channel.id, Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS);
});
const guestAccountsEnabled = config.EnableGuestAccounts === 'true';
const emailInvitationsEnabled = config.EnableEmailInvitations === 'true';
const isEnterpriseReady = config.BuildEnterpriseReady === 'true';
const isGroupConstrained = Boolean(currentTeam.group_constrained);
const isGroupConstrained = Boolean(currentTeam?.group_constrained);
const canInviteGuests = !isGroupConstrained && isEnterpriseReady && guestAccountsEnabled && haveICurrentTeamPermission(state, Permissions.INVITE_GUEST);
const isCloud = license.Cloud === 'true';

View File

@ -59,7 +59,7 @@ export type Props = {
message: string,
) => Promise<ActionResult<InviteResults>>;
};
currentTeam: Team;
currentTeam?: Team;
currentChannel: Channel;
townSquareDisplayName: string;
invitableChannels: Channel[];
@ -159,6 +159,9 @@ export class InvitationModal extends React.PureComponent<Props, State> {
};
invite = async () => {
if (!this.props.currentTeam) {
return;
}
const roleForTrackFlow = getRoleForTrackFlow();
const inviteAs = this.state.invite.inviteType;
if (inviteAs === InviteType.MEMBER && this.props.isCloud) {
@ -246,7 +249,7 @@ export class InvitationModal extends React.PureComponent<Props, State> {
}));
};
debouncedSearchChannels = debounce((term) => this.props.actions.searchChannels(this.props.currentTeam.id, term), 150);
debouncedSearchChannels = debounce((term) => this.props.currentTeam && this.props.actions.searchChannels(this.props.currentTeam.id, term), 150);
channelsLoader = async (value: string) => {
if (!value) {
@ -357,6 +360,10 @@ export class InvitationModal extends React.PureComponent<Props, State> {
};
render() {
if (!this.props.currentTeam) {
return null;
}
let view = (
<InviteView
setInviteAs={this.setInviteAs}

View File

@ -105,7 +105,7 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
const initializing = useSelector((state: GlobalState) => state.requests.users.logout.status === RequestStatus.SUCCESS || !state.storage.initialized);
const currentUser = useSelector(getCurrentUser);
const experimentalPrimaryTeam = useSelector((state: GlobalState) => (ExperimentalPrimaryTeam ? getTeamByName(state, ExperimentalPrimaryTeam) : undefined));
const experimentalPrimaryTeamMember = useSelector((state: GlobalState) => getMyTeamMember(state, experimentalPrimaryTeam?.id ?? ''));
const experimentalPrimaryTeamMember = useSelector((state: GlobalState) => (experimentalPrimaryTeam ? getMyTeamMember(state, experimentalPrimaryTeam.id) : undefined));
const onboardingFlowEnabled = useSelector(getIsOnboardingFlowEnabled);
const loginIdInput = useRef<HTMLInputElement>(null);
@ -666,7 +666,7 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
history.push(redirectTo);
} else if (team) {
history.push(`/${team.name}`);
} else if (experimentalPrimaryTeamMember.team_id) {
} else if (experimentalPrimaryTeamMember?.team_id) {
// Only set experimental team if user is on that team
history.push(`/${ExperimentalPrimaryTeam}`);
} else if (onboardingFlowEnabled) {

View File

@ -76,11 +76,11 @@ function mapStateToProps(state: GlobalState) {
pluginMenuItems: state.plugins.components.MainMenu,
moreTeamsToJoin,
siteName,
teamId: currentTeam.id,
teamName: currentTeam.name,
teamId: currentTeam?.id,
teamName: currentTeam?.name,
currentUser,
isMentionSearch: rhsState === RHSStates.MENTION,
teamIsGroupConstrained: Boolean(currentTeam.group_constrained),
teamIsGroupConstrained: Boolean(currentTeam?.group_constrained),
isLicensedForLDAPGroups: state.entities.general.license.LDAPGroups === 'true',
guestAccessEnabled: config.EnableGuestAccounts === 'true',
canInviteTeamMember,

View File

@ -21,7 +21,7 @@ import type {GlobalState} from 'types/store';
import MemberListTeam from './member_list_team';
type Props = {
teamId: string;
teamId?: string;
}
function mapStateToProps(state: GlobalState, ownProps: Props) {

View File

@ -71,8 +71,8 @@ const makeMapStateToProps = () => {
const stats = getTotalUsersStatsSelector(state) || {total_users_count: 0};
return {
currentTeamId: team.id,
currentTeamName: team.name,
currentTeamId: team?.id,
currentTeamName: team?.name,
searchTerm,
users,
currentChannelMembers,

View File

@ -27,8 +27,8 @@ import type {
export type Props = {
currentUserId: string;
currentTeamId: string;
currentTeamName: string;
currentTeamId?: string;
currentTeamName?: string;
searchTerm: string;
users: UserProfile[];
totalCount: number;
@ -239,7 +239,7 @@ export default class MoreDirectChannels extends React.PureComponent<Props, State
this.setUsersLoadingState(false);
});
} else {
this.props.actions.getProfilesInTeam(this.props.currentTeamId, pageNum, USERS_PER_PAGE * 2).then(() => {
this.props.actions.getProfilesInTeam(this.props.currentTeamId || '', pageNum, USERS_PER_PAGE * 2).then(() => {
this.setUsersLoadingState(false);
});
}

View File

@ -103,11 +103,11 @@ const MoveThreadModal = ({onExited, post, actions}: Props) => {
const previewMetaData: PostPreviewMetadata = useMemo(() => ({
post,
post_id: post.id,
team_name: currentTeam.name,
team_name: currentTeam?.name || '',
channel_display_name: originalChannel.display_name,
channel_type: originalChannel.type,
channel_id: originalChannel.id,
}), [post, currentTeam.name, originalChannel.display_name, originalChannel.type, originalChannel.id]);
}), [post, currentTeam?.name, originalChannel.display_name, originalChannel.type, originalChannel.id]);
const notificationText = formatMessage({
id: 'move_thread_modal.notification.dm_or_gm',

View File

@ -59,7 +59,7 @@ const NewChannelModal = () => {
const intl = useIntl();
const {formatMessage} = intl;
const {id: currentTeamId} = useSelector(getCurrentTeam);
const currentTeamId = useSelector(getCurrentTeam)?.id;
const canCreatePublicChannel = useSelector((state: GlobalState) => (currentTeamId ? haveICurrentChannelPermission(state, Permissions.CREATE_PUBLIC_CHANNEL) : false));
const canCreatePrivateChannel = useSelector((state: GlobalState) => (currentTeamId ? haveICurrentChannelPermission(state, Permissions.CREATE_PRIVATE_CHANNEL) : false));
@ -88,7 +88,7 @@ const NewChannelModal = () => {
}, []);
const handleOnModalConfirm = async () => {
if (!canCreate) {
if (!canCreate || !currentTeamId) {
return;
}
@ -135,7 +135,7 @@ const NewChannelModal = () => {
};
const addBoardToChannel = async (channelId: string) => {
if (!createBoardFromChannelPlugin) {
if (!createBoardFromChannelPlugin || !currentTeamId) {
return false;
}
if (!actionFromPluggable) {

View File

@ -63,7 +63,7 @@ function focusReplyPost(post: Post, channel: Channel, teamId: string, returnTo:
const team = getTeam(state, channel.team_id || teamId);
const currentChannel = getCurrentChannel(state);
const sameTeam = currentChannel && currentChannel.team_id === team.id;
const sameTeam = currentChannel && currentChannel.team_id === team?.id;
const {skipRedirectReplyPermalink} = option;
@ -100,6 +100,9 @@ export function focusPost(postId: string, returnTo = '', currentUserId: string,
const state = getState();
const currentTeam = getCurrentTeam(state);
if (!currentTeam) {
return;
}
if (!postInfo.has_joined_channel) {
// Prompt system admin before joining the private channel

View File

@ -10,7 +10,7 @@ import type {GlobalState} from 'types/store';
import TeamPermissionGate from './team_permission_gate';
type Props = {
teamId: string;
teamId?: string;
permissions: string[];
}

View File

@ -5,16 +5,6 @@ import React from 'react';
type Props = {
/**
* Team to check the permission
*/
teamId?: string;
/**
* Permissions enough to pass the gate (binary OR)
*/
permissions: string[];
/**
* Has permission
*/

View File

@ -133,8 +133,8 @@ function makeMapStateToProps() {
const currentTeam = getCurrentTeam(state);
const team = getTeam(state, channel.team_id);
let teamName = currentTeam.name;
let teamDisplayName = '';
let teamName = currentTeam?.name;
let teamDisplayName;
const memberships = getTeamMemberships(state);
const isDMorGM = channel.type === General.DM_CHANNEL || channel.type === General.GM_CHANNEL;
@ -145,10 +145,10 @@ function makeMapStateToProps() {
memberships && Object.values(memberships).length > 1 // Not show if the user only belongs to one team
) {
teamDisplayName = team?.display_name;
teamName = team?.name || currentTeam.name;
teamName = team?.name || currentTeam?.name;
}
const canReply = isDMorGM || (channel.team_id === currentTeam.id);
const canReply = isDMorGM || (channel.team_id === currentTeam?.id);
const directTeammate = getDirectTeammate(state, channel.id);
const previewCollapsed = get(

View File

@ -55,7 +55,7 @@ import PostUserProfile from './user_profile';
export type Props = {
post: Post;
currentTeam: Team;
currentTeam?: Team;
team?: Team;
currentUserId: string;
compactDisplay?: boolean;
@ -128,7 +128,7 @@ const PostComponent = (props: Props): JSX.Element => {
const isRHS = props.location === Locations.RHS_ROOT || props.location === Locations.RHS_COMMENT || props.location === Locations.SEARCH;
const postRef = useRef<HTMLDivElement>(null);
const postHeaderRef = useRef<HTMLDivElement>(null);
const teamId = props.team?.id || props.currentTeam.id;
const teamId = props.team?.id ?? props.currentTeam?.id ?? '';
const [hover, setHover] = useState(false);
const [a11yActive, setA11y] = useState(false);
@ -391,12 +391,12 @@ const PostComponent = (props: Props): JSX.Element => {
}, [post, props.actions, props.actions.selectPostFromRightHandSideSearch]);
const handleThreadClick = useCallback((e: React.MouseEvent) => {
if (props.currentTeam.id === teamId) {
if (props.currentTeam?.id === teamId) {
handleCommentClick(e);
} else {
handleJumpClick(e);
}
}, [handleCommentClick, handleJumpClick, props.currentTeam.id, teamId]);
}, [handleCommentClick, handleJumpClick, props.currentTeam?.id, teamId]);
const postClass = classNames('post__body', {'post--edited': PostUtils.isEdited(post), 'search-item-snippet': isSearchResultItem});

View File

@ -61,7 +61,7 @@ function makeMapStateToProps() {
return (state: GlobalState, ownProps: OwnProps) => {
const channel = getChannel(state, ownProps.channelId);
const currentTeam = getCurrentTeam(state) || {};
const currentTeam = getCurrentTeam(state);
const license = getLicense(state);
const subscriptionProduct = getSubscriptionProduct(state);

View File

@ -66,7 +66,7 @@ export default class PostMarkdown extends React.PureComponent<Props> {
if (this.props.post) {
const renderedSystemMessage = renderSystemMessage(this.props.post,
this.props.currentTeam,
this.props.currentTeam?.name ?? '',
this.props.channel,
this.props.hideGuestTags,
this.props.isUserCanManageMembers,
@ -78,6 +78,9 @@ export default class PostMarkdown extends React.PureComponent<Props> {
}
if (this.props.post && this.props.post.type === Posts.POST_TYPES.REMINDER) {
if (!this.props.currentTeam) {
return null;
}
const renderedSystemBotMessage = renderReminderSystemBotMessage(this.props.post, this.props.currentTeam);
return <div>{renderedSystemBotMessage}</div>;
}

View File

@ -389,10 +389,10 @@ const systemMessageRenderers = {
[Posts.POST_TYPES.ME]: renderMeMessage,
};
export function renderSystemMessage(post: Post, currentTeam: Team, channel: Channel, hideGuestTags: boolean, isUserCanManageMembers?: boolean, isMilitaryTime?: boolean, timezone?: string): ReactNode {
export function renderSystemMessage(post: Post, currentTeamName: string, channel: Channel, hideGuestTags: boolean, isUserCanManageMembers?: boolean, isMilitaryTime?: boolean, timezone?: string): ReactNode {
const isEphemeral = isPostEphemeral(post);
if (isEphemeral && post.props?.type === Posts.POST_TYPES.REMINDER) {
return renderReminderACKMessage(post, currentTeam, Boolean(isMilitaryTime), timezone);
return renderReminderACKMessage(post, currentTeamName, Boolean(isMilitaryTime), timezone);
}
if (post.props && post.props.add_channel_member) {
if (channel && (channel.type === General.PRIVATE_CHANNEL || channel.type === General.OPEN_CHANNEL) &&
@ -439,9 +439,9 @@ export function renderSystemMessage(post: Post, currentTeam: Team, channel: Chan
return null;
}
function renderReminderACKMessage(post: Post, currentTeam: Team, isMilitaryTime: boolean, timezone?: string): ReactNode {
function renderReminderACKMessage(post: Post, currentTeamName: string, isMilitaryTime: boolean, timezone?: string): ReactNode {
const username = renderUsername(post.props.username);
const teamUrl = `${getSiteURL()}/${post.props.team_name || currentTeam.name}`;
const teamUrl = `${getSiteURL()}/${post.props.team_name || currentTeamName}`;
const link = `${teamUrl}/pl/${post.props.post_id}`;
const permaLink = renderFormattedText(`[${link}](${link})`);
const localTime = new Date(post.props.target_time * 1000);

View File

@ -47,7 +47,7 @@ function mapStateToProps(state: GlobalState) {
enableUserCreation,
isReadOnly,
isFavorite: isCurrentChannelFavorite(state),
teamIsGroupConstrained: Boolean(team.group_constrained),
teamIsGroupConstrained: Boolean(team?.group_constrained),
creatorName: getDisplayNameByUser(state, creator),
teammate,
teammateName: getDisplayNameByUser(state, teammate),

View File

@ -29,7 +29,7 @@ type Props = {
function getIsInCurrentTeam(state: GlobalState, userId: string) {
const team = getCurrentTeam(state);
const teamMember = getTeamMember(state, team.id, userId);
const teamMember = team ? getTeamMember(state, team.id, userId) : undefined;
return Boolean(teamMember) && teamMember?.delete_at === 0;
}

View File

@ -33,7 +33,7 @@ type Props = {
function getIsTeamAdmin(state: GlobalState, userId: string) {
const team = getCurrentTeam(state);
const teamMember = getTeamMember(state, team.id, userId);
const teamMember = team ? getTeamMember(state, team.id, userId) : undefined;
return Boolean(teamMember && teamMember.scheme_admin);
}

View File

@ -20,7 +20,7 @@ const mapStateToPropsRenameChannel = createSelector(
(state: GlobalState) => {
const currentTeamId = state.entities.teams.currentTeamId;
const team = getTeam(state, currentTeamId);
const currentTeamUrl = `${getSiteURL()}/${team.name}`;
const currentTeamUrl = `${getSiteURL()}/${team ? team.name : ''}`;
return {
currentTeamUrl,
team,

View File

@ -56,7 +56,7 @@ type Props = {
/**
* Object with info about current team
*/
team: Team;
team?: Team;
/**
* String with the current team URL
@ -190,7 +190,9 @@ export class RenameChannelModal extends React.PureComponent<Props, State> {
onSaveSuccess = () => {
this.handleHide();
this.unsetError();
getHistory().push('/' + this.props.team.name + '/channels/' + this.state.channelName);
if (this.props.team) {
getHistory().push('/' + this.props.team.name + '/channels/' + this.state.channelName);
}
};
handleCancel = (e?: MouseEvent) => {

View File

@ -16,7 +16,7 @@ import ThreadViewer from 'components/threading/thread_viewer';
import type {FakePost, RhsState} from 'types/store/rhs';
type Props = {
currentTeam: Team;
currentTeam?: Team;
posts: Post[];
channel: Channel | null;
selected: Post | FakePost;
@ -35,7 +35,7 @@ const RhsThread = ({
const dispatch = useDispatch();
useEffect(() => {
if (channel?.team_id && channel.team_id !== currentTeam.id) {
if (channel?.team_id && channel.team_id !== currentTeam?.id) {
// if team-scoped and mismatched team, close rhs
dispatch(closeRightHandSide());
}

View File

@ -30,10 +30,10 @@ const VerticalStack = styled.div`
`;
type Props = {
teamDescription: string;
teamId: string;
teamDescription?: string;
teamId?: string;
currentUser: UserProfile;
teamDisplayName: string;
teamDisplayName?: string;
actions: Actions;
};
@ -52,7 +52,7 @@ export default class Contents extends React.PureComponent<Props> {
};
render() {
if (!this.props.currentUser) {
if (!this.props.currentUser || !this.props.teamId) {
return null;
}

View File

@ -20,9 +20,9 @@ function mapStateToProps(state: GlobalState) {
return {
currentUser,
teamDescription: currentTeam.description,
teamDisplayName: currentTeam.display_name,
teamId: currentTeam.id,
teamDescription: currentTeam?.description,
teamDisplayName: currentTeam?.display_name,
teamId: currentTeam?.id,
};
}

View File

@ -42,7 +42,7 @@ function makeMapStateToProps() {
return {
channel,
isCurrentChannel: channel.id === currentChannelId,
currentTeamName: currentTeam.name,
currentTeamName: currentTeam?.name,
unreadMentions: unreadCount.mentions,
isUnread: unreadCount.showUnread,
draggingState: getDraggingState(state),

View File

@ -33,6 +33,10 @@ function SidebarChannel({
}: Props) {
const [show, setShow] = useState(true);
if (!currentTeamName) {
return null;
}
function isCollapsed() {
return isCategoryDragged || (isCategoryCollapsed && !isUnread && !isCurrentChannel);
}

View File

@ -52,7 +52,6 @@ function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
}
return {
currentTeamId: currentTeam.id,
currentUserId: getCurrentUserId(state),
categories,
currentCategory,

View File

@ -26,7 +26,7 @@ function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
const teammate = getUser(state, ownProps.channel.teammate_id!);
const currentUser = getCurrentUser(state);
const currentTeam = getCurrentTeam(state);
const redirectChannel = getRedirectChannelNameForTeam(state, currentTeam.id);
const redirectChannel = currentTeam ? getRedirectChannelNameForTeam(state, currentTeam.id) : '';
const currentChannelId = getCurrentChannelId(state);
const active = ownProps.channel.id === currentChannelId;

View File

@ -25,7 +25,7 @@ function makeMapStateToProps() {
return (state: GlobalState, ownProps: OwnProps) => {
const currentUserId = getCurrentUserId(state);
const currentTeam = getCurrentTeam(state);
const redirectChannel = getRedirectChannelNameForTeam(state, currentTeam.id);
const redirectChannel = currentTeam ? getRedirectChannelNameForTeam(state, currentTeam.id) : '';
const currentChannelId = getCurrentChannelId(state);
const membersCount = getMemberCount(state, ownProps.channel);
const active = ownProps.channel.id === currentChannelId;

View File

@ -97,7 +97,7 @@ export type Props = {
canCreateCustomGroups: boolean;
}
const SidebarHeader: React.FC<Props> = (props: Props): JSX.Element => {
const SidebarHeader = (props: Props) => {
const dispatch = useDispatch();
const currentTeam = useSelector((state: GlobalState) => getCurrentTeam(state));
const showCreateTutorialTip = useShowOnboardingTutorialStep(OnboardingTourSteps.CREATE_AND_JOIN_CHANNELS);
@ -115,6 +115,10 @@ const SidebarHeader: React.FC<Props> = (props: Props): JSX.Element => {
setMenuToggled(!menuToggled);
};
if (!currentTeam) {
return null;
}
return (
<CompassThemeProvider theme={theme}>
<SidebarHeaderContainer

View File

@ -74,7 +74,7 @@ export function renderThumbVertical(props: any) {
const scrollbarStyles: CSSProperties = {position: 'absolute'};
type Props = {
currentTeam: Team;
currentTeam?: Team;
currentChannelId: string;
categories: ChannelCategory[];
unreadChannelIds: string[];
@ -454,7 +454,7 @@ export default class SidebarList extends React.PureComponent<Props, State> {
this.props.actions.moveChannelsInSidebar(result.destination.droppableId, result.destination.index, result.draggableId);
trackEvent('ui', 'ui_sidebar_dragdrop_dropped_channel');
} else if (result.type === 'SIDEBAR_CATEGORY') {
this.props.actions.moveCategory(this.props.currentTeam.id, result.draggableId, result.destination.index);
this.props.actions.moveCategory(this.props.currentTeam!.id, result.draggableId, result.destination.index);
trackEvent('ui', 'ui_sidebar_dragdrop_dropped_category');
}
}

View File

@ -32,7 +32,7 @@ export type Props = {
isExpanded: boolean;
isOpen: boolean;
channel: Channel;
team: Team;
team?: Team;
teamId: Team['id'];
productId: ProductIdentifier;
postRightVisible: boolean;

View File

@ -1329,7 +1329,11 @@ export class AppCommandParser {
const getChannel = async (channelName: string) => {
let channel = selectChannelByName(this.store.getState(), channelName);
if (!channel) {
const dispatchResult = await this.store.dispatch(getChannelByNameAndTeamName(getCurrentTeam(this.store.getState()).name, channelName) as any);
const team = getCurrentTeam(this.store.getState());
if (!team) {
return null;
}
const dispatchResult = await this.store.dispatch(getChannelByNameAndTeamName(team.name, channelName) as any);
if ('error' in dispatchResult) {
return null;
}

View File

@ -26,7 +26,7 @@ type Props = {
currentUser: UserProfile;
teamMember: TeamMembership;
teamUrl: string;
currentTeam: Team;
currentTeam?: Team;
index: number;
totalUsers: number;
collapsedThreads: ReturnType<typeof isCollapsedThreadsEnabled>;
@ -190,7 +190,7 @@ export default class TeamMembersDropdown extends React.PureComponent<Props, Stat
showMakeAdmin = false;
}
const canRemoveFromTeam = user.id !== me.id && (!currentTeam.group_constrained || user.is_bot);
const canRemoveFromTeam = user.id !== me.id && (!currentTeam?.group_constrained || user.is_bot);
let makeDemoteModal = null;
if (user.id === me.id) {

View File

@ -18,7 +18,7 @@ import {ModalIdentifiers} from 'utils/constants';
import type {ModalData} from 'types/actions';
type Props = {
currentTeam: Team;
currentTeam?: Team;
onExited: () => void;
onLoad?: () => void;
actions: {
@ -90,7 +90,7 @@ export default class TeamMembersModal extends React.PureComponent<Props, State>
/>
</Modal.Title>
<TeamPermissionGate
teamId={this.props.currentTeam.id}
teamId={this.props.currentTeam?.id}
permissions={[Permissions.ADD_USER_TO_TEAM, Permissions.INVITE_GUEST]}
>
<button
@ -108,7 +108,7 @@ export default class TeamMembersModal extends React.PureComponent<Props, State>
</Modal.Header>
<Modal.Body>
<MemberListTeam
teamId={this.props.currentTeam.id}
teamId={this.props.currentTeam?.id}
/>
</Modal.Body>
</Modal>

View File

@ -16,7 +16,7 @@ type Props = {
setHasChangeTabError: (hasChangesError: boolean) => void;
closeModal: () => void;
collapseModal: () => void;
team: Team;
team?: Team;
};
const TeamSettings = ({
@ -28,7 +28,7 @@ const TeamSettings = ({
hasChangeTabError,
setHasChanges,
setHasChangeTabError,
}: Props): JSX.Element | null => {
}: Props) => {
if (!team) {
return null;
}

View File

@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {ComponentProps} from 'react';
import {connect} from 'react-redux';
import {withRouter, matchPath} from 'react-router-dom';
import type {RouteChildrenProps} from 'react-router-dom';
@ -18,7 +17,7 @@ import UnreadsStatusHandler from './unreads_status_handler';
type Props = RouteChildrenProps;
function mapStateToProps(state: GlobalState, {location: {pathname}}: Props): ComponentProps<typeof UnreadsStatusHandler> {
function mapStateToProps(state: GlobalState, {location: {pathname}}: Props) {
const config = getConfig(state);
const currentChannel = getCurrentChannel(state);
const currentTeammate = (currentChannel && currentChannel.teammate_id) ? currentChannel : null;

View File

@ -41,7 +41,7 @@ type Props = {
unreadStatus: BasicUnreadStatus;
siteName?: string;
currentChannel?: Channel;
currentTeam: Team;
currentTeam?: Team;
currentTeammate: Channel | null;
inGlobalThreads: boolean;
inDrafts: boolean;

View File

@ -192,7 +192,7 @@ export const getGroupsNotAssociatedToChannel: (state: GlobalState, channelID: st
(state: GlobalState, channelID: string, teamID: string) => getGroupsAssociatedToTeam(state, teamID),
(allGroups, channelGroupIDSet, team, teamGroups) => {
let result = Object.values(allGroups).filter((group) => !channelGroupIDSet.has(group.id) && group.source === GroupSource.Ldap);
if (team.group_constrained) {
if (team?.group_constrained) {
const gids = teamGroups.map((group) => group.id);
result = result.filter((group) => gids?.includes(group.id));
}

View File

@ -168,11 +168,15 @@ const getMyPermissionsByChannel = createSelector(
},
);
export function haveITeamPermission(state: GlobalState, teamId: string, permission: string) {
return (
getMySystemPermissions(state).has(permission) ||
getMyPermissionsByTeam(state)[teamId]?.has(permission)
);
export function haveITeamPermission(state: GlobalState, teamId: string | undefined, permission: string) {
if (getMySystemPermissions(state).has(permission)) {
return true;
}
if (!teamId) {
return false;
}
return getMyPermissionsByTeam(state)[teamId]?.has(permission);
}
export const haveIGroupPermission: (state: GlobalState, groupID: string, permission: string) => boolean = createSelector(
@ -204,12 +208,16 @@ export const haveIGroupPermission: (state: GlobalState, groupID: string, permiss
},
);
export function haveIChannelPermission(state: GlobalState, teamId: string, channelId: string, permission: string): boolean {
return (
getMySystemPermissions(state).has(permission) ||
getMyPermissionsByTeam(state)[teamId]?.has(permission) ||
getMyPermissionsByChannel(state)[channelId]?.has(permission)
);
export function haveIChannelPermission(state: GlobalState, teamId: string | undefined, channelId: string, permission: string): boolean {
if (getMySystemPermissions(state).has(permission)) {
return true;
}
if (teamId && getMyPermissionsByTeam(state)[teamId]?.has(permission)) {
return true;
}
return getMyPermissionsByChannel(state)[channelId]?.has(permission);
}
export function haveICurrentTeamPermission(state: GlobalState, permission: string): boolean {

View File

@ -4,7 +4,7 @@
import type {GlobalState} from '@mattermost/types/store';
import type {Team, TeamMembership, TeamStats} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
import type {IDMappedObjects, RelationOneToOne} from '@mattermost/types/utilities';
import type {RelationOneToOne} from '@mattermost/types/utilities';
import {Permissions} from 'mattermost-redux/constants';
import {createSelector} from 'mattermost-redux/selectors/create_selector';
@ -27,7 +27,7 @@ export function getTeamByName(state: GlobalState, name: string) {
return Object.values(teams).find((team) => team.name === name);
}
export function getTeams(state: GlobalState): IDMappedObjects<Team> {
export function getTeams(state: GlobalState) {
return state.entities.teams.teams;
}
@ -84,7 +84,7 @@ export const getActiveTeamsList: (state: GlobalState) => Team[] = createSelector
},
);
export const getCurrentTeam: (state: GlobalState) => Team = createSelector(
export const getCurrentTeam: (state: GlobalState) => Team | undefined = createSelector(
'getCurrentTeam',
getTeams,
getCurrentTeamId,
@ -93,12 +93,12 @@ export const getCurrentTeam: (state: GlobalState) => Team = createSelector(
},
);
export function getTeam(state: GlobalState, id: string): Team {
export function getTeam(state: GlobalState, id: string): Team | undefined {
const teams = getTeams(state);
return teams[id];
}
export const getCurrentTeamMembership: (state: GlobalState) => TeamMembership = createSelector(
export const getCurrentTeamMembership: (state: GlobalState) => TeamMembership | undefined = createSelector(
'getCurrentTeamMembership',
getCurrentTeamId,
getTeamMemberships,
@ -145,10 +145,13 @@ export const getCurrentRelativeTeamUrl: (state: GlobalState) => string = createS
export function getRelativeTeamUrl(state: GlobalState, teamId: string): string {
const team = getTeam(state, teamId);
if (!team) {
return '/';
}
return `/${team.name}`;
}
export const getCurrentTeamStats: (state: GlobalState) => TeamStats = createSelector(
export const getCurrentTeamStats: (state: GlobalState) => TeamStats | undefined = createSelector(
'getCurrentTeamStats',
getCurrentTeamId,
getTeamStats,
@ -175,16 +178,16 @@ export const getMyDeletedTeams: (state: GlobalState) => Team[] = createSelector(
},
);
export const getMyTeamMember: (state: GlobalState, teamId: string) => TeamMembership = createSelector(
export const getMyTeamMember: (state: GlobalState, teamId: string) => TeamMembership | undefined = createSelector(
'getMyTeamMember',
getTeamMemberships,
(state: GlobalState, teamId: string) => teamId,
(teamMemberships, teamId) => {
return teamMemberships[teamId] || {};
return teamMemberships[teamId];
},
);
export const getMembersInCurrentTeam: (state: GlobalState) => RelationOneToOne<UserProfile, TeamMembership> = createSelector(
export const getMembersInCurrentTeam: (state: GlobalState) => RelationOneToOne<UserProfile, TeamMembership> | undefined = createSelector(
'getMembersInCurrentTeam',
getCurrentTeamId,
getMembersInTeams,
@ -193,7 +196,7 @@ export const getMembersInCurrentTeam: (state: GlobalState) => RelationOneToOne<U
},
);
export const getMembersInTeam: (state: GlobalState, teamId: string) => RelationOneToOne<UserProfile, TeamMembership> = createSelector(
export const getMembersInTeam: (state: GlobalState, teamId: string) => RelationOneToOne<UserProfile, TeamMembership> | undefined = createSelector(
'getMembersInTeam',
(state: GlobalState, teamId: string) => teamId,
getMembersInTeams,
@ -334,7 +337,7 @@ export const isTeamSameWithCurrentTeam = (state: GlobalState, teamName: string):
const targetTeam = getTeamByName(state, teamName);
const currentTeam = getCurrentTeam(state);
return Boolean(targetTeam && targetTeam.id === currentTeam.id);
return Boolean(targetTeam && targetTeam.id === currentTeam?.id);
};
// returns the badge for a team

View File

@ -647,6 +647,9 @@ export function getPostURL(state: GlobalState, post: Post): string {
const channel = getChannel(state, post.channel_id);
const currentUserId = getCurrentUserId(state);
const team = getTeam(state, channel.team_id || getCurrentTeamId(state));
if (!team) {
return '';
}
const postURI = isCollapsedThreadsEnabled(state) && isComment(post) ? '' : `/${post.id}`;