Fix types so the store can return undefined channels (#26393)
* Fix types so the store can return undefined channels * Address feedback * Revert gm change * Fix lint --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
c22509eca2
Коммит
539daee634
@@ -86,7 +86,10 @@ export function executeCommand(message: string, args: CommandArgs): ActionFuncAs
|
||||
dispatch(GlobalActions.sendEphemeralPost('/leave is not supported in reply threads. Use it in the center channel instead.', args.channel_id, args.root_id));
|
||||
return {data: true};
|
||||
}
|
||||
const channel = getCurrentChannel(state) || {};
|
||||
const channel = getCurrentChannel(state);
|
||||
if (!channel) {
|
||||
return {data: false};
|
||||
}
|
||||
if (channel.type === Constants.PRIVATE_CHANNEL) {
|
||||
dispatch(openModal({modalId: ModalIdentifiers.LEAVE_PRIVATE_CHANNEL_MODAL, dialogType: LeaveChannelModal, dialogProps: {channel}}));
|
||||
return {data: true};
|
||||
|
||||
@@ -290,7 +290,7 @@ export function setEditingPost(postId = '', refocusId = '', title = '', isRHS =
|
||||
const license = state.entities.general.license;
|
||||
const userId = getCurrentUserId(state);
|
||||
const channel = getChannel(state, post.channel_id);
|
||||
const teamId = channel.team_id || '';
|
||||
const teamId = channel?.team_id || '';
|
||||
|
||||
const canEditNow = canEditPost(state, config, license, teamId, post.channel_id, userId, post);
|
||||
|
||||
|
||||
@@ -64,14 +64,14 @@ import type {GlobalState} from 'types/store';
|
||||
export function goToLastViewedChannel(): ActionFuncAsync {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const currentChannel = getCurrentChannel(state) || {};
|
||||
const currentChannel = getCurrentChannel(state);
|
||||
const channelsInTeam = getChannelsNameMapInCurrentTeam(state);
|
||||
const directChannel = getAllDirectChannelsNameMapInCurrentTeam(state);
|
||||
const channels = Object.assign({}, channelsInTeam, directChannel);
|
||||
|
||||
let channelToSwitchTo = getChannelByName(channels, getLastViewedChannelName(state));
|
||||
|
||||
if (currentChannel.id === channelToSwitchTo!.id) {
|
||||
if (currentChannel?.id === channelToSwitchTo!.id) {
|
||||
channelToSwitchTo = getChannelByName(channels, getRedirectChannelNameForTeam(state, getCurrentTeamId(state)));
|
||||
}
|
||||
|
||||
@@ -83,7 +83,10 @@ export function switchToChannelById(channelId: string): ActionFuncAsync {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const channel = getChannel(state, channelId);
|
||||
return dispatch(switchToChannel(channel));
|
||||
if (channel) {
|
||||
return dispatch(switchToChannel(channel));
|
||||
}
|
||||
return {data: true};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -119,6 +122,9 @@ export function switchToChannel(channel: Channel & {userId?: string}): ActionFun
|
||||
getHistory().push(`${teamUrl}/messages/@${channel.name}`);
|
||||
} else if (channel.type === Constants.GM_CHANNEL) {
|
||||
const gmChannel = getChannel(state, channel.id);
|
||||
if (!gmChannel?.name) {
|
||||
return {error: true};
|
||||
}
|
||||
getHistory().push(`${teamUrl}/channels/${gmChannel.name}`);
|
||||
} else if (channel.type === Constants.THREADS) {
|
||||
getHistory().push(`${teamUrl}/${channel.name}`);
|
||||
|
||||
@@ -40,7 +40,7 @@ import SaveChangesPanel from '../../save_changes_panel';
|
||||
|
||||
export interface ChannelDetailsProps {
|
||||
channelID: string;
|
||||
channel: Channel;
|
||||
channel?: Channel;
|
||||
team?: Team;
|
||||
groups: Group[];
|
||||
totalGroups: number;
|
||||
@@ -108,9 +108,9 @@ export default class ChannelDetails extends React.PureComponent<ChannelDetailsPr
|
||||
constructor(props: ChannelDetailsProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isSynced: Boolean(props.channel.group_constrained),
|
||||
isPublic: props.channel.type === Constants.OPEN_CHANNEL,
|
||||
isDefault: props.channel.name === Constants.DEFAULT_CHANNEL,
|
||||
isSynced: Boolean(props.channel?.group_constrained),
|
||||
isPublic: props.channel?.type === Constants.OPEN_CHANNEL,
|
||||
isDefault: props.channel?.name === Constants.DEFAULT_CHANNEL,
|
||||
isPrivacyChanging: false,
|
||||
saving: false,
|
||||
totalGroups: props.totalGroups,
|
||||
@@ -127,25 +127,25 @@ export default class ChannelDetails extends React.PureComponent<ChannelDetailsPr
|
||||
previousServerError: undefined,
|
||||
channelPermissions: props.channelPermissions,
|
||||
teamScheme: props.teamScheme,
|
||||
isLocalArchived: props.channel.delete_at > 0,
|
||||
isLocalArchived: props.channel?.delete_at !== 0,
|
||||
showArchiveConfirmModal: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: ChannelDetailsProps) {
|
||||
const {channel, totalGroups, actions} = this.props;
|
||||
if (channel.id !== prevProps.channel.id || totalGroups !== prevProps.totalGroups) {
|
||||
if (channel?.id !== prevProps.channel?.id || totalGroups !== prevProps.totalGroups) {
|
||||
this.setState({
|
||||
totalGroups,
|
||||
isSynced: Boolean(channel.group_constrained),
|
||||
isPublic: channel.type === Constants.OPEN_CHANNEL,
|
||||
isDefault: channel.name === Constants.DEFAULT_CHANNEL,
|
||||
isLocalArchived: channel.delete_at > 0,
|
||||
isSynced: Boolean(channel?.group_constrained),
|
||||
isPublic: channel?.type === Constants.OPEN_CHANNEL,
|
||||
isDefault: channel?.name === Constants.DEFAULT_CHANNEL,
|
||||
isLocalArchived: channel?.delete_at !== 0,
|
||||
});
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -167,7 +167,7 @@ export default class ChannelDetails extends React.PureComponent<ChannelDetailsPr
|
||||
actions.getChannel(channelID);
|
||||
}
|
||||
|
||||
if (channel.team_id) {
|
||||
if (channel?.team_id) {
|
||||
actions.getTeam(channel.team_id).
|
||||
then(async (data: any) => {
|
||||
if (data.data && data.data.scheme_id) {
|
||||
@@ -206,7 +206,7 @@ export default class ChannelDetails extends React.PureComponent<ChannelDetailsPr
|
||||
|
||||
private setToggles = (isSynced: boolean, isPublic: boolean) => {
|
||||
const {channel} = this.props;
|
||||
const isOriginallyPublic = channel.type === Constants.OPEN_CHANNEL;
|
||||
const isOriginallyPublic = channel?.type === Constants.OPEN_CHANNEL;
|
||||
this.setState(
|
||||
{
|
||||
saveNeeded: true,
|
||||
@@ -355,7 +355,7 @@ export default class ChannelDetails extends React.PureComponent<ChannelDetailsPr
|
||||
this.setState({showArchiveConfirmModal: true});
|
||||
return;
|
||||
}
|
||||
const isOriginallyPublic = channel.type === Constants.OPEN_CHANNEL;
|
||||
const isOriginallyPublic = channel?.type === Constants.OPEN_CHANNEL;
|
||||
if (isSynced) {
|
||||
isPublic = false;
|
||||
isPrivacyChanging = isOriginallyPublic;
|
||||
@@ -380,11 +380,16 @@ export default class ChannelDetails extends React.PureComponent<ChannelDetailsPr
|
||||
};
|
||||
|
||||
private handleSubmit = async () => {
|
||||
const {groups: origGroups, channelID, actions, channel} = this.props;
|
||||
|
||||
if (!channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({showConvertConfirmModal: false, showRemoveConfirmModal: false, showConvertAndRemoveConfirmModal: false, showArchiveConfirmModal: false, saving: true});
|
||||
const {groups, isSynced, isPublic, isPrivacyChanging, channelPermissions, usersToAdd, usersToRemove, rolesToUpdate} = this.state;
|
||||
let serverError: JSX.Element | undefined;
|
||||
let saveNeeded = false;
|
||||
const {groups: origGroups, channelID, actions, channel} = this.props;
|
||||
|
||||
if (this.channelToBeArchived()) {
|
||||
const result = await actions.deleteChannel(channel.id);
|
||||
@@ -607,13 +612,13 @@ export default class ChannelDetails extends React.PureComponent<ChannelDetailsPr
|
||||
|
||||
private channelToBeArchived = (): boolean => {
|
||||
const {isLocalArchived} = this.state;
|
||||
const isServerArchived = this.props.channel.delete_at !== 0;
|
||||
const isServerArchived = this.props.channel?.delete_at !== 0;
|
||||
return isLocalArchived && !isServerArchived;
|
||||
};
|
||||
|
||||
private channelToBeRestored = (): boolean => {
|
||||
const {isLocalArchived} = this.state;
|
||||
const isServerArchived = this.props.channel.delete_at !== 0;
|
||||
const isServerArchived = this.props.channel?.delete_at !== 0;
|
||||
return !isLocalArchived && isServerArchived;
|
||||
};
|
||||
|
||||
@@ -681,7 +686,7 @@ export default class ChannelDetails extends React.PureComponent<ChannelDetailsPr
|
||||
this.setState(newState);
|
||||
};
|
||||
|
||||
public render = (): JSX.Element => {
|
||||
public render = () => {
|
||||
const {
|
||||
totalGroups,
|
||||
saving,
|
||||
@@ -703,6 +708,11 @@ export default class ChannelDetails extends React.PureComponent<ChannelDetailsPr
|
||||
showArchiveConfirmModal,
|
||||
} = this.state;
|
||||
const {channel, team} = this.props;
|
||||
|
||||
if (!channel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const missingGroup = (og: {id: string}) => !groups.find((g: Group) => g.id === og.id);
|
||||
const removedGroups = this.props.groups.filter(missingGroup);
|
||||
const nonArchivedContent = (
|
||||
@@ -722,7 +732,7 @@ export default class ChannelDetails extends React.PureComponent<ChannelDetailsPr
|
||||
teamSchemeID={teamScheme?.id}
|
||||
teamSchemeDisplayName={teamScheme?.display_name}
|
||||
guestAccountsEnabled={this.props.guestAccountsEnabled}
|
||||
isPublic={this.props.channel.type === Constants.OPEN_CHANNEL}
|
||||
isPublic={channel.type === Constants.OPEN_CHANNEL}
|
||||
readOnly={this.props.isDisabled}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import Constants, {ModalIdentifiers} from 'utils/constants';
|
||||
|
||||
type Props = {
|
||||
channelId: string;
|
||||
channel: Channel;
|
||||
channel?: Channel;
|
||||
filters: GetFilteredUsersStatsOpts;
|
||||
|
||||
users: UserProfile[];
|
||||
|
||||
@@ -54,7 +54,7 @@ function makeMapStateToProps() {
|
||||
|
||||
const config = getConfig(state);
|
||||
const channelMembers = getChannelMembersInChannels(state)[channelId] || {};
|
||||
const channel = getChannel(state, channelId) || {channel_id: channelId};
|
||||
const channel = getChannel(state, channelId);
|
||||
const searchTerm = state.views.search.userGridSearch?.term || '';
|
||||
const filters = getUserGridFilters(state);
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ 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 channel = getChannel(state, channelID);
|
||||
const team = channel ? getTeam(state, channel.team_id) : undefined;
|
||||
const groups = getGroupsAssociatedToChannel(state, channelID);
|
||||
const totalGroups = groups.length;
|
||||
const allGroups = getAllGroups(state);
|
||||
|
||||
@@ -84,7 +84,7 @@ export type Props = {
|
||||
currentChannelMembersCount: number;
|
||||
|
||||
// Data used in multiple places of the component
|
||||
currentChannel: Channel;
|
||||
currentChannel?: Channel;
|
||||
|
||||
//Data used for DM prewritten messages
|
||||
currentChannelTeammateUsername?: string;
|
||||
@@ -239,7 +239,7 @@ type State = {
|
||||
uploadsProgressPercent: {[clientID: string]: FilePreviewInfo};
|
||||
renderScrollbar: boolean;
|
||||
scrollbarWidth: number;
|
||||
currentChannel: Channel;
|
||||
currentChannel?: Channel;
|
||||
errorClass: string | null;
|
||||
serverError: (ServerError & {submittedMessage?: string}) | null;
|
||||
postError?: React.ReactNode;
|
||||
@@ -271,7 +271,7 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
||||
currentChannel: props.currentChannel,
|
||||
};
|
||||
if (
|
||||
props.currentChannel.id !== state.currentChannel.id ||
|
||||
props.currentChannel?.id !== state.currentChannel?.id ||
|
||||
(props.isRemoteDraft && props.draft.message !== state.message)
|
||||
) {
|
||||
updatedState = {
|
||||
@@ -321,14 +321,14 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const {currentChannel, actions} = this.props;
|
||||
if (prevProps.currentChannel.id !== currentChannel.id) {
|
||||
if (prevProps.currentChannel?.id !== currentChannel?.id) {
|
||||
this.lastChannelSwitchAt = Date.now();
|
||||
this.focusTextbox();
|
||||
this.saveDraftWithShow(prevProps);
|
||||
this.getChannelMemberCountsByGroup();
|
||||
}
|
||||
|
||||
if (currentChannel.id !== prevProps.currentChannel.id) {
|
||||
if (currentChannel?.id !== prevProps.currentChannel?.id) {
|
||||
actions.setShowPreview(false);
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
||||
getChannelMemberCountsByGroup = () => {
|
||||
const {useLDAPGroupMentions, useCustomGroupMentions, currentChannel, actions, draft} = this.props;
|
||||
|
||||
if ((useLDAPGroupMentions || useCustomGroupMentions) && currentChannel.id) {
|
||||
if ((useLDAPGroupMentions || useCustomGroupMentions) && currentChannel?.id) {
|
||||
const mentions = mentionsMinusSpecialMentionsInText(draft.message);
|
||||
|
||||
if (mentions.length === 1) {
|
||||
@@ -458,7 +458,11 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
doSubmit = async (e?: React.FormEvent) => {
|
||||
const channelId = this.props.currentChannel.id;
|
||||
const channelId = this.props.currentChannel?.id;
|
||||
if (!channelId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
@@ -628,6 +632,10 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
||||
useCustomGroupMentions,
|
||||
} = this.props;
|
||||
|
||||
if (!updateChannel) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setShowPreview(false);
|
||||
this.isDraftSubmitting = true;
|
||||
|
||||
@@ -671,7 +679,7 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
const {data} = await this.props.actions.getChannelTimezones(this.props.currentChannel.id);
|
||||
const {data} = await this.props.actions.getChannelTimezones(updateChannel.id);
|
||||
channelTimezoneCount = data ? data.length : 0;
|
||||
}
|
||||
|
||||
@@ -748,6 +756,10 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
||||
useCustomGroupMentions,
|
||||
} = this.props;
|
||||
|
||||
if (!currentChannel) {
|
||||
return {data: false};
|
||||
}
|
||||
|
||||
let post = originalPost;
|
||||
|
||||
post.channel_id = currentChannel.id;
|
||||
@@ -864,8 +876,10 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
emitTypingEvent = () => {
|
||||
const channelId = this.props.currentChannel.id;
|
||||
GlobalActions.emitLocalUserTypingEvent(channelId, '');
|
||||
const channelId = this.props.currentChannel?.id;
|
||||
if (channelId) {
|
||||
GlobalActions.emitLocalUserTypingEvent(channelId, '');
|
||||
}
|
||||
};
|
||||
|
||||
handleChange = (e: React.ChangeEvent<TextboxElement>) => {
|
||||
@@ -889,11 +903,15 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
||||
this.handleDraftChange(draft);
|
||||
};
|
||||
|
||||
handleDraftChange = (draft: PostDraft, channelId = this.props.currentChannel.id, instant = false) => {
|
||||
handleDraftChange = (draft: PostDraft, channelId = this.props.currentChannel?.id, instant = false) => {
|
||||
if (this.saveDraftFrame) {
|
||||
clearTimeout(this.saveDraftFrame);
|
||||
}
|
||||
|
||||
if (!channelId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (instant) {
|
||||
this.props.actions.setDraft(StoragePrefixes.DRAFT + channelId, draft, channelId);
|
||||
} else {
|
||||
@@ -905,7 +923,10 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
||||
this.draftsForChannel[channelId] = draft;
|
||||
};
|
||||
|
||||
removeDraft = (channelId = this.props.currentChannel.id) => {
|
||||
removeDraft = (channelId = this.props.currentChannel?.id) => {
|
||||
if (!channelId) {
|
||||
return;
|
||||
}
|
||||
this.props.actions.setDraft(StoragePrefixes.DRAFT + channelId, null, channelId);
|
||||
this.draftsForChannel[channelId] = null;
|
||||
};
|
||||
@@ -986,6 +1007,9 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
removePreview = (id: string) => {
|
||||
if (!this.props.currentChannel) {
|
||||
return;
|
||||
}
|
||||
let modifiedDraft = {} as PostDraft;
|
||||
const draft = {...this.props.draft};
|
||||
|
||||
@@ -1264,6 +1288,9 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
handlePostPriorityApply = (settings?: PostPriorityMetadata) => {
|
||||
if (!this.props.currentChannel) {
|
||||
return;
|
||||
}
|
||||
const updatedDraft = {
|
||||
...this.props.draft,
|
||||
};
|
||||
@@ -1309,7 +1336,7 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (currentChannel.type === Constants.DM_CHANNEL) {
|
||||
if (currentChannel?.type === Constants.DM_CHANNEL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,12 +65,12 @@ function makeMapStateToProps() {
|
||||
return (state: GlobalState) => {
|
||||
const config = getConfig(state);
|
||||
const license = getLicense(state);
|
||||
const currentChannel = getCurrentChannel(state) || {};
|
||||
const currentChannelTeammateUsername = getUser(state, currentChannel.teammate_id || '')?.username;
|
||||
const draft = getChannelDraft(state, currentChannel.id);
|
||||
const isRemoteDraft = state.views.drafts.remotes[`${StoragePrefixes.DRAFT}${currentChannel.id}`] || false;
|
||||
const currentChannel = getCurrentChannel(state);
|
||||
const currentChannelTeammateUsername = currentChannel ? getUser(state, currentChannel.teammate_id || '')?.username : undefined;
|
||||
const draft = getChannelDraft(state, currentChannel?.id || '');
|
||||
const isRemoteDraft = (currentChannel && state.views.drafts.remotes[`${StoragePrefixes.DRAFT}${currentChannel.id}`]) || false;
|
||||
const latestReplyablePostId = getLatestReplyablePostId(state);
|
||||
const currentChannelMembersCount = getCurrentChannelStats(state) ? getCurrentChannelStats(state).member_count : 1;
|
||||
const currentChannelMembersCount = getCurrentChannelStats(state)?.member_count ?? 1;
|
||||
const enableEmojiPicker = config.EnableEmojiPicker === 'true';
|
||||
const enableGifPicker = config.EnableGifPicker === 'true';
|
||||
const enableConfirmNotificationsToChannel = config.EnableConfirmNotificationsToChannel === 'true';
|
||||
@@ -82,9 +82,11 @@ function makeMapStateToProps() {
|
||||
const isLDAPEnabled = license?.IsLicensed === 'true' && license?.LDAPGroups === 'true';
|
||||
const useCustomGroupMentions = isCustomGroupsEnabled(state) && haveICurrentChannelPermission(state, Permissions.USE_GROUP_MENTIONS);
|
||||
const useLDAPGroupMentions = isLDAPEnabled && haveICurrentChannelPermission(state, Permissions.USE_GROUP_MENTIONS);
|
||||
const channelMemberCountsByGroup = selectChannelMemberCountsByGroup(state, currentChannel.id);
|
||||
const channelMemberCountsByGroup = currentChannel ? selectChannelMemberCountsByGroup(state, currentChannel.id) : {};
|
||||
const currentTeamId = getCurrentTeamId(state);
|
||||
const groupsWithAllowReference = useLDAPGroupMentions || useCustomGroupMentions ? getAssociatedGroupsForReferenceByMention(state, currentTeamId, currentChannel.id) : null;
|
||||
const groupsWithAllowReference = (currentChannel && (useLDAPGroupMentions || useCustomGroupMentions)) ?
|
||||
getAssociatedGroupsForReferenceByMention(state, currentTeamId, currentChannel.id) :
|
||||
null;
|
||||
const enableTutorial = config.EnableTutorial === 'true';
|
||||
const tutorialStep = getInt(state, TutorialTourName.ONBOARDING_TUTORIAL_STEP, currentUserId, 0);
|
||||
|
||||
|
||||
@@ -113,6 +113,10 @@ function NotificationFromMembersModal(props: Props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!channel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const modalTitle = formatMessage({id: 'postypes.custom_open_pricing_modal_post_renderer.membersThatRequested', defaultMessage: 'Members that requested '});
|
||||
|
||||
const modalHeaderText = (
|
||||
|
||||
@@ -48,7 +48,7 @@ const popoverMarkdownOptions = {singleline: false, mentionHighlight: false, atMe
|
||||
export type Props = {
|
||||
teamId: string;
|
||||
currentUser: UserProfile;
|
||||
channel: Channel;
|
||||
channel?: Channel;
|
||||
memberCount?: number;
|
||||
channelMember?: ChannelMembership;
|
||||
dmUser?: UserProfile;
|
||||
@@ -169,7 +169,7 @@ class ChannelHeader extends React.PureComponent<Props, State> {
|
||||
showChannelFiles = () => {
|
||||
if (this.props.rhsState === RHSStates.CHANNEL_FILES) {
|
||||
this.props.actions.closeRightHandSide();
|
||||
} else {
|
||||
} else if (this.props.channel) {
|
||||
this.props.actions.showChannelFiles(this.props.channel.id);
|
||||
}
|
||||
};
|
||||
@@ -180,6 +180,10 @@ class ChannelHeader extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
const {actions, channel} = this.props;
|
||||
if (!channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const modalData = {
|
||||
modalId: ModalIdentifiers.EDIT_CHANNEL_HEADER,
|
||||
dialogType: EditChannelHeaderModal,
|
||||
@@ -212,7 +216,7 @@ class ChannelHeader extends React.PureComponent<Props, State> {
|
||||
toggleChannelMembersRHS = () => {
|
||||
if (this.props.rhsState === RHSStates.CHANNEL_MEMBERS) {
|
||||
this.props.actions.closeRightHandSide();
|
||||
} else {
|
||||
} else if (this.props.channel) {
|
||||
this.props.actions.showChannelMembers(this.props.channel.id);
|
||||
}
|
||||
};
|
||||
@@ -259,6 +263,10 @@ class ChannelHeader extends React.PureComponent<Props, State> {
|
||||
hasGuests,
|
||||
hideGuestTags,
|
||||
} = this.props;
|
||||
if (!channel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {formatMessage} = this.props.intl;
|
||||
const ariaLabelChannelHeader = this.props.intl.formatMessage({id: 'accessibility.sections.channelHeader', defaultMessage: 'channel header region'});
|
||||
|
||||
|
||||
@@ -19,17 +19,20 @@ const ChannelHeaderTitleFavorite = () => {
|
||||
const dispatch = useDispatch();
|
||||
const isFavorite = useSelector(isCurrentChannelFavorite);
|
||||
const channel = useSelector(getCurrentChannel);
|
||||
const channelIsArchived = channel.delete_at !== 0;
|
||||
const channelIsArchived = (channel?.delete_at ?? 0) > 0;
|
||||
const toggleFavoriteRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const toggleFavoriteCallback = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
if (!channel) {
|
||||
return;
|
||||
}
|
||||
if (isFavorite) {
|
||||
dispatch(unfavoriteChannel(channel.id));
|
||||
} else {
|
||||
dispatch(favoriteChannel(channel.id));
|
||||
}
|
||||
}, [isFavorite, channel.id]);
|
||||
}, [isFavorite, channel?.id]);
|
||||
|
||||
const removeTooltipLink = useCallback(() => {
|
||||
// Bootstrap adds the attr dynamically, removing it to prevent a11y readout
|
||||
|
||||
@@ -49,16 +49,13 @@ import type {GlobalState} from 'types/store';
|
||||
|
||||
import ChannelHeader from './channel_header';
|
||||
|
||||
const EMPTY_CHANNEL = {};
|
||||
const EMPTY_CHANNEL_STATS = {member_count: 0, guest_count: 0, pinnedpost_count: 0, files_count: 0};
|
||||
|
||||
function makeMapStateToProps() {
|
||||
const doGetProfilesInChannel = makeGetProfilesInChannel();
|
||||
const getCustomStatus = makeGetCustomStatus();
|
||||
let timestampUnits: string[] = [];
|
||||
|
||||
return function mapStateToProps(state: GlobalState) {
|
||||
const channel = getCurrentChannel(state) || EMPTY_CHANNEL;
|
||||
const channel = getCurrentChannel(state);
|
||||
const user = getCurrentUser(state);
|
||||
const teams = getMyTeams(state);
|
||||
const hasMoreThanOneTeam = teams.length > 1;
|
||||
@@ -77,7 +74,7 @@ function makeMapStateToProps() {
|
||||
} else if (channel && channel.type === General.GM_CHANNEL) {
|
||||
gmMembers = doGetProfilesInChannel(state, channel.id);
|
||||
}
|
||||
const stats = getCurrentChannelStats(state) || EMPTY_CHANNEL_STATS;
|
||||
const stats = getCurrentChannelStats(state);
|
||||
|
||||
let isLastActiveEnabled = false;
|
||||
if (dmUser) {
|
||||
@@ -89,7 +86,7 @@ function makeMapStateToProps() {
|
||||
teamId: getCurrentTeamId(state),
|
||||
channel,
|
||||
channelMember: getMyCurrentChannelMembership(state),
|
||||
memberCount: stats.member_count,
|
||||
memberCount: stats?.member_count || 0,
|
||||
currentUser: user,
|
||||
dmUser,
|
||||
gmMembers,
|
||||
@@ -98,8 +95,8 @@ function makeMapStateToProps() {
|
||||
isReadOnly: false,
|
||||
isMuted: isCurrentChannelMuted(state),
|
||||
isQuickSwitcherOpen: isModalOpen(state, ModalIdentifiers.QUICK_SWITCH),
|
||||
hasGuests: stats.guest_count > 0,
|
||||
pinnedPostsCount: stats.pinnedpost_count,
|
||||
hasGuests: stats ? stats.guest_count > 0 : false,
|
||||
pinnedPostsCount: stats?.pinnedpost_count || 0,
|
||||
hasMoreThanOneTeam,
|
||||
currentRelativeTeamUrl: getCurrentRelativeTeamUrl(state),
|
||||
announcementBarCount: getAnnouncementBarCount(state),
|
||||
|
||||
@@ -42,7 +42,7 @@ import MenuItemViewPinnedPosts from './menu_items/view_pinned_posts';
|
||||
|
||||
export type Props = {
|
||||
user: UserProfile;
|
||||
channel: Channel;
|
||||
channel?: Channel;
|
||||
isDefault: boolean;
|
||||
isFavorite: boolean;
|
||||
isReadonly: boolean;
|
||||
@@ -69,6 +69,10 @@ export default class ChannelHeaderDropdown extends React.PureComponent<Props> {
|
||||
isLicensedForLDAPGroups,
|
||||
} = this.props;
|
||||
|
||||
if (!channel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isPrivate = channel.type === Constants.PRIVATE_CHANNEL;
|
||||
const isGroupConstrained = channel.group_constrained === true;
|
||||
const channelMembersPermission = isPrivate ? Permissions.MANAGE_PRIVATE_CHANNEL_MEMBERS : Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS;
|
||||
@@ -92,7 +96,7 @@ export default class ChannelHeaderDropdown extends React.PureComponent<Props> {
|
||||
key={item.id + '_pluginmenuitem'}
|
||||
onClick={() => {
|
||||
if (item.action) {
|
||||
item.action(this.props.channel.id);
|
||||
item.action(channel.id);
|
||||
}
|
||||
}}
|
||||
text={item.text}
|
||||
|
||||
@@ -37,7 +37,7 @@ const getTeammateId = createSelector(
|
||||
getCurrentChannel,
|
||||
getCurrentUserId,
|
||||
(channel, currentUserId) => {
|
||||
if (channel.type !== Constants.DM_CHANNEL) {
|
||||
if (channel?.type !== Constants.DM_CHANNEL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import MobileChannelHeaderDropdownAnimation from './mobile_channel_header_dropdo
|
||||
|
||||
type Props = {
|
||||
user: UserProfile;
|
||||
channel: Channel;
|
||||
channel?: Channel;
|
||||
teammateId: string | null;
|
||||
teammateIsBot?: boolean;
|
||||
teammateStatus?: string;
|
||||
@@ -36,6 +36,10 @@ const MobileChannelHeaderDropdown = ({
|
||||
const intl = useIntl();
|
||||
|
||||
const getChannelTitle = () => {
|
||||
if (!channel) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (channel.type === Constants.DM_CHANNEL) {
|
||||
if (user.id === teammateId) {
|
||||
return (
|
||||
|
||||
@@ -46,9 +46,9 @@ function mapStateToProps(state: GlobalState) {
|
||||
const isInvitingPeople = isModalOpen(state, ModalIdentifiers.CHANNEL_INVITE) || isModalOpen(state, ModalIdentifiers.CREATE_DM_CHANNEL);
|
||||
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 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 channelMembers = getProfilesInCurrentChannel(state);
|
||||
|
||||
@@ -67,7 +67,7 @@ function mapStateToProps(state: GlobalState) {
|
||||
channelMembers,
|
||||
} as Props;
|
||||
|
||||
if (channel.type === Constants.DM_CHANNEL) {
|
||||
if (channel?.type === Constants.DM_CHANNEL) {
|
||||
const user = getUser(state, getUserIdFromChannelId(channel.name, currentUser.id));
|
||||
props.dmUser = {
|
||||
user,
|
||||
|
||||
@@ -26,7 +26,7 @@ export type Props = {
|
||||
/*
|
||||
* Object containing information on the current selected channel, used to define BackButton's url
|
||||
*/
|
||||
currentChannel: Channel;
|
||||
currentChannel?: Channel;
|
||||
|
||||
/*
|
||||
* String containing the custom branding's text
|
||||
|
||||
@@ -67,7 +67,7 @@ function makeMapStateToProps() {
|
||||
const userId = getCurrentUserId(state);
|
||||
const channel = getChannel(state, post.channel_id);
|
||||
const currentTeam = getCurrentTeam(state);
|
||||
const team = getTeam(state, channel.team_id);
|
||||
const team = channel ? getTeam(state, channel.team_id) : undefined;
|
||||
const teamUrl = `${getSiteURL()}/${team?.name || currentTeam?.name}`;
|
||||
const isMilitaryTime = getBool(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false);
|
||||
|
||||
@@ -123,7 +123,7 @@ function makeMapStateToProps() {
|
||||
isMobileView: getIsMobileView(state),
|
||||
timezone: getCurrentTimezone(state),
|
||||
isMilitaryTime,
|
||||
canMove: canWrangler(state, channel.type, threadReplyCount),
|
||||
canMove: channel ? canWrangler(state, channel.type, threadReplyCount) : false,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import PanelBody from '../panel/panel_body';
|
||||
import Header from '../panel/panel_header';
|
||||
|
||||
type Props = {
|
||||
channel: Channel;
|
||||
channel?: Channel;
|
||||
channelUrl: string;
|
||||
displayName: string;
|
||||
draftId: string;
|
||||
@@ -51,6 +51,7 @@ function ChannelDraft({
|
||||
user,
|
||||
value,
|
||||
isRemote,
|
||||
id: channelId,
|
||||
}: Props) {
|
||||
const dispatch = useDispatch();
|
||||
const history = useHistory();
|
||||
@@ -60,16 +61,20 @@ function ChannelDraft({
|
||||
}, [history, channelUrl]);
|
||||
|
||||
const handleOnDelete = useCallback((id: string) => {
|
||||
dispatch(removeDraft(id, channel.id));
|
||||
}, [dispatch, channel.id]);
|
||||
dispatch(removeDraft(id, channelId));
|
||||
}, [dispatch, channelId]);
|
||||
|
||||
const doSubmit = useCallback((id: string, post: Post) => {
|
||||
dispatch(createPost(post, value.fileInfos));
|
||||
dispatch(removeDraft(id, channel.id));
|
||||
dispatch(removeDraft(id, channelId));
|
||||
history.push(channelUrl);
|
||||
}, [dispatch, history, value.fileInfos, channel.id, channelUrl]);
|
||||
}, [dispatch, history, value.fileInfos, channelId, channelUrl]);
|
||||
|
||||
const showPersistNotificationModal = useCallback((id: string, post: Post) => {
|
||||
if (!channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(openModal({
|
||||
modalId: ModalIdentifiers.PERSIST_NOTIFICATION_CONFIRM_MODAL,
|
||||
dialogType: PersistNotificationConfirmModal,
|
||||
@@ -80,7 +85,7 @@ function ChannelDraft({
|
||||
onConfirm: () => doSubmit(id, post),
|
||||
},
|
||||
}));
|
||||
}, [channel.type, dispatch, doSubmit]);
|
||||
}, [channel, dispatch, doSubmit]);
|
||||
|
||||
const handleOnSend = useCallback(async (id: string) => {
|
||||
const post = {} as Post;
|
||||
@@ -135,7 +140,7 @@ function ChannelDraft({
|
||||
remote={isRemote || false}
|
||||
/>
|
||||
<PanelBody
|
||||
channelId={channel.id}
|
||||
channelId={channelId}
|
||||
displayName={displayName}
|
||||
fileInfos={value.fileInfos}
|
||||
message={value.message}
|
||||
|
||||
@@ -24,7 +24,7 @@ function makeMapStateToProps() {
|
||||
const channel = getChannel(state, ownProps);
|
||||
|
||||
const teamId = getCurrentTeamId(state);
|
||||
const channelUrl = getChannelURL(state, channel, teamId);
|
||||
const channelUrl = channel ? getChannelURL(state, channel, teamId) : '';
|
||||
|
||||
return {
|
||||
channel,
|
||||
|
||||
@@ -24,7 +24,7 @@ import PanelBody from '../panel/panel_body';
|
||||
import Header from '../panel/panel_header';
|
||||
|
||||
type Props = {
|
||||
channel: Channel;
|
||||
channel?: Channel;
|
||||
displayName: string;
|
||||
draftId: string;
|
||||
rootId: UserThread['id'] | UserThreadSynthetic['id'];
|
||||
@@ -57,29 +57,29 @@ function ThreadDraft({
|
||||
}, [thread?.id]);
|
||||
|
||||
const onSubmit = useMemo(() => {
|
||||
if (thread) {
|
||||
return makeOnSubmit(channel.id, thread.id, '');
|
||||
if (thread?.id) {
|
||||
return makeOnSubmit(value.channelId, thread.id, '');
|
||||
}
|
||||
|
||||
return () => Promise.resolve({data: true});
|
||||
}, [channel.id, thread?.id]);
|
||||
}, [value.channelId, thread?.id]);
|
||||
|
||||
const handleOnDelete = useCallback((id: string) => {
|
||||
dispatch(removeDraft(id, channel.id, rootId));
|
||||
}, [channel.id, rootId]);
|
||||
dispatch(removeDraft(id, value.channelId, rootId));
|
||||
}, [value.channelId, rootId, dispatch]);
|
||||
|
||||
const handleOnEdit = useCallback(() => {
|
||||
dispatch(selectPost({id: rootId, channel_id: channel.id} as Post));
|
||||
}, [channel]);
|
||||
dispatch(selectPost({id: rootId, channel_id: value.channelId} as Post));
|
||||
}, [value.channelId, dispatch, rootId]);
|
||||
|
||||
const handleOnSend = useCallback(async (id: string) => {
|
||||
await dispatch(onSubmit(value));
|
||||
|
||||
handleOnDelete(id);
|
||||
handleOnEdit();
|
||||
}, [value, onSubmit]);
|
||||
}, [value, onSubmit, dispatch, handleOnDelete, handleOnEdit]);
|
||||
|
||||
if (!thread) {
|
||||
if (!thread || !channel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ function mapStateToProps(state: GlobalState) {
|
||||
teamId,
|
||||
channelId,
|
||||
maxPostSize: parseInt(config.MaxPostSize || '0', 10) || Constants.DEFAULT_CHARACTER_LIMIT,
|
||||
readOnlyChannel: !isCurrentUserSystemAdmin(state) && channel.name === Constants.DEFAULT_CHANNEL,
|
||||
readOnlyChannel: !isCurrentUserSystemAdmin(state) && channel?.name === Constants.DEFAULT_CHANNEL,
|
||||
useChannelMentions,
|
||||
isRHSOpened: getIsRhsOpen(state),
|
||||
isEditHistoryShowing: getRhsState(state) === RHSStates.EDIT_HISTORY,
|
||||
|
||||
@@ -29,7 +29,7 @@ function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
|
||||
|
||||
return {
|
||||
channelDisplayName: '',
|
||||
channelType: channel.type,
|
||||
channelType: channel?.type,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
import type {ConnectedProps} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import type {Dispatch} from 'redux';
|
||||
|
||||
import type {Channel} from '@mattermost/types/channels';
|
||||
import type {Post} from '@mattermost/types/posts';
|
||||
|
||||
import type {ActionResult} from 'mattermost-redux/types/actions';
|
||||
|
||||
import {openDirectChannelToUserId} from 'actions/channel_actions';
|
||||
import {joinChannelById, switchToChannel} from 'actions/views/channel';
|
||||
import {forwardPost} from 'actions/views/posts';
|
||||
|
||||
import ForwardPostModal from './forward_post_modal';
|
||||
|
||||
export type PropsFromRedux = ConnectedProps<typeof connector>;
|
||||
|
||||
export type ActionProps = {
|
||||
|
||||
// join the selected channel when necessary
|
||||
joinChannelById: (channelId: string) => Promise<ActionResult>;
|
||||
|
||||
// switch to the selected channel
|
||||
switchToChannel: (channel: Channel) => Promise<ActionResult>;
|
||||
|
||||
// switch to the selected channel
|
||||
openDirectChannelToUserId: (userId: string) => Promise<ActionResult<Channel>>;
|
||||
|
||||
// action called to forward the post with an optional comment
|
||||
forwardPost: (post: Post, channelId: Channel, message?: string) => Promise<ActionResult>;
|
||||
}
|
||||
|
||||
export type OwnProps = {
|
||||
|
||||
// The function called immediately after the modal is hidden
|
||||
onExited?: () => void;
|
||||
|
||||
// the post that is going to be forwarded
|
||||
post: Post;
|
||||
};
|
||||
|
||||
function mapDispatchToProps(dispatch: Dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
joinChannelById,
|
||||
switchToChannel,
|
||||
forwardPost,
|
||||
openDirectChannelToUserId,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
const connector = connect(null, mapDispatchToProps);
|
||||
|
||||
export default connector(ForwardPostModal);
|
||||
@@ -4,11 +4,11 @@
|
||||
import classNames from 'classnames';
|
||||
import React, {useCallback, useRef, useState} from 'react';
|
||||
import {FormattedList, FormattedMessage, useIntl} from 'react-intl';
|
||||
import {useSelector} from 'react-redux';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
import type {ValueType} from 'react-select';
|
||||
|
||||
import {GenericModal} from '@mattermost/components';
|
||||
import type {PostPreviewMetadata} from '@mattermost/types/posts';
|
||||
import type {Post, PostPreviewMetadata} from '@mattermost/types/posts';
|
||||
|
||||
import {General, Permissions} from 'mattermost-redux/constants';
|
||||
import {makeGetChannel} from 'mattermost-redux/selectors/entities/channels';
|
||||
@@ -16,6 +16,9 @@ import {haveIChannelPermission} from 'mattermost-redux/selectors/entities/roles'
|
||||
import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams';
|
||||
import type {ActionResult} from 'mattermost-redux/types/actions';
|
||||
|
||||
import {openDirectChannelToUserId} from 'actions/channel_actions';
|
||||
import {joinChannelById, switchToChannel} from 'actions/views/channel';
|
||||
import {forwardPost} from 'actions/views/posts';
|
||||
import {getPermalinkURL} from 'selectors/urls';
|
||||
|
||||
import NotificationBox from 'components/notification_box';
|
||||
@@ -30,16 +33,22 @@ import ForwardPostChannelSelect, {makeSelectedChannelOption} from './forward_pos
|
||||
import type {ChannelOption} from './forward_post_channel_select';
|
||||
import ForwardPostCommentInput from './forward_post_comment_input';
|
||||
|
||||
import type {ActionProps, OwnProps, PropsFromRedux} from './index';
|
||||
|
||||
import './forward_post_modal.scss';
|
||||
|
||||
export type Props = PropsFromRedux & OwnProps & { actions: ActionProps };
|
||||
type Props = {
|
||||
|
||||
// The function called immediately after the modal is hidden
|
||||
onExited?: () => void;
|
||||
|
||||
// the post that is going to be forwarded
|
||||
post: Post;
|
||||
};
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
const ForwardPostModal = ({onExited, post, actions}: Props) => {
|
||||
const ForwardPostModal = ({onExited, post}: Props) => {
|
||||
const {formatMessage} = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const getChannel = makeGetChannel();
|
||||
|
||||
@@ -49,7 +58,7 @@ const ForwardPostModal = ({onExited, post, actions}: Props) => {
|
||||
const relativePermaLink = useSelector((state: GlobalState) => (currentTeam ? getPermalinkURL(state, currentTeam.id, post.id) : ''));
|
||||
const permaLink = `${getSiteURL()}${relativePermaLink}`;
|
||||
|
||||
const isPrivateConversation = channel.type !== Constants.OPEN_CHANNEL;
|
||||
const isPrivateConversation = channel?.type !== Constants.OPEN_CHANNEL;
|
||||
|
||||
const [comment, setComment] = useState('');
|
||||
const [bodyHeight, setBodyHeight] = useState<number>(0);
|
||||
@@ -77,7 +86,7 @@ const ForwardPostModal = ({onExited, post, actions}: Props) => {
|
||||
|
||||
const canPostInSelectedChannel = useSelector(
|
||||
(state: GlobalState) => {
|
||||
const channelId = isPrivateConversation ? channel.id : selectedChannelId;
|
||||
const channelId = isPrivateConversation ? post.channel_id : selectedChannelId;
|
||||
const isDMChannel = selectedChannel?.details?.type === Constants.DM_CHANNEL;
|
||||
const teamId = isPrivateConversation ? currentTeam?.id : selectedChannel?.details?.team_id;
|
||||
|
||||
@@ -123,15 +132,15 @@ const ForwardPostModal = ({onExited, post, actions}: Props) => {
|
||||
post,
|
||||
post_id: post.id,
|
||||
team_name: currentTeam?.name || '',
|
||||
channel_display_name: channel.display_name,
|
||||
channel_type: channel.type,
|
||||
channel_id: channel.id,
|
||||
channel_display_name: channel?.display_name || '',
|
||||
channel_type: channel?.type || 'O',
|
||||
channel_id: post.channel_id,
|
||||
};
|
||||
|
||||
let notification;
|
||||
if (isPrivateConversation) {
|
||||
let notificationText;
|
||||
if (channel.type === General.PRIVATE_CHANNEL) {
|
||||
if (channel?.type === General.PRIVATE_CHANNEL) {
|
||||
const channelName = `~${channel.display_name}`;
|
||||
notificationText = (
|
||||
<FormattedMessage
|
||||
@@ -144,7 +153,7 @@ const ForwardPostModal = ({onExited, post, actions}: Props) => {
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
const allParticipants = channel.display_name.split(', ');
|
||||
const allParticipants = channel?.display_name.split(', ') || [];
|
||||
const participants = allParticipants.map((participant) => <strong key={participant}>{participant}</strong>);
|
||||
|
||||
notificationText = (
|
||||
@@ -179,6 +188,10 @@ const ForwardPostModal = ({onExited, post, actions}: Props) => {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (!channel) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const channelToForward = isPrivateConversation ? makeSelectedChannelOption(channel) : selectedChannel;
|
||||
|
||||
if (!channelToForward) {
|
||||
@@ -189,7 +202,7 @@ const ForwardPostModal = ({onExited, post, actions}: Props) => {
|
||||
|
||||
return Promise.resolve().then(() => {
|
||||
if (type === Constants.DM_CHANNEL && userId) {
|
||||
return actions.openDirectChannelToUserId(userId);
|
||||
return dispatch(openDirectChannelToUserId(userId));
|
||||
}
|
||||
return {data: false} as ActionResult;
|
||||
}).then(({data}) => {
|
||||
@@ -197,20 +210,20 @@ const ForwardPostModal = ({onExited, post, actions}: Props) => {
|
||||
channelToForward.details.id = data.id;
|
||||
}
|
||||
|
||||
return actions.forwardPost(
|
||||
return dispatch(forwardPost(
|
||||
post,
|
||||
channelToForward.details,
|
||||
comment,
|
||||
);
|
||||
));
|
||||
}).then(() => {
|
||||
if (type === Constants.MENTION_MORE_CHANNELS && type === Constants.OPEN_CHANNEL) {
|
||||
return actions.joinChannelById(channelToForward.details.id);
|
||||
return dispatch(joinChannelById(channelToForward.details.id));
|
||||
}
|
||||
return {data: false};
|
||||
}).then(() => {
|
||||
// only switch channels when we are not in a private conversation
|
||||
if (!isPrivateConversation) {
|
||||
return actions.switchToChannel(channelToForward.details);
|
||||
return dispatch(switchToChannel(channelToForward.details));
|
||||
}
|
||||
return {data: false};
|
||||
}).then(() => {
|
||||
@@ -227,7 +240,7 @@ const ForwardPostModal = ({onExited, post, actions}: Props) => {
|
||||
defaultMessage: 'Originally posted in ~{channel}',
|
||||
},
|
||||
{
|
||||
channel: channel.display_name,
|
||||
channel: channel?.display_name || '',
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -58,7 +58,7 @@ export type Props = {
|
||||
) => Promise<ActionResult<InviteResults>>;
|
||||
};
|
||||
currentTeam?: Team;
|
||||
currentChannel: Channel;
|
||||
currentChannel?: Channel;
|
||||
townSquareDisplayName: string;
|
||||
invitableChannels: Channel[];
|
||||
emailInvitationsEnabled: boolean;
|
||||
|
||||
@@ -56,7 +56,7 @@ export type Props = InviteState & {
|
||||
onChannelsInputChange: (channelsInputValue: string) => void;
|
||||
onClose: () => void;
|
||||
currentTeam: Team;
|
||||
currentChannel: Channel;
|
||||
currentChannel?: Channel;
|
||||
setCustomMessage: (message: string) => void;
|
||||
toggleCustomMessage: () => void;
|
||||
channelsLoader: (value: string, callback?: (channels: Channel[]) => void) => Promise<Channel[]>;
|
||||
|
||||
@@ -48,11 +48,14 @@ const getChannelURLAction = (channelId: string, teamId: string, url: string): Th
|
||||
const state = getState();
|
||||
|
||||
if (url && isPermalinkURL(url)) {
|
||||
return getHistory().push(url);
|
||||
getHistory().push(url);
|
||||
return;
|
||||
}
|
||||
|
||||
const channel = getChannel(state, channelId);
|
||||
return getHistory().push(getChannelURL(state, channel, teamId));
|
||||
if (channel) {
|
||||
getHistory().push(getChannelURL(state, channel, teamId));
|
||||
}
|
||||
};
|
||||
|
||||
function mapDispatchToProps(dispatch: Dispatch) {
|
||||
|
||||
@@ -41,8 +41,14 @@ const getUsersAndActionsToDisplay = createSelector(
|
||||
channelMember: ChannelMembership;
|
||||
};
|
||||
} = {};
|
||||
const usersToDisplay = [];
|
||||
const usersToDisplay: UserProfile[] = [];
|
||||
|
||||
if (!channel) {
|
||||
return {
|
||||
usersToDisplay,
|
||||
actionUserProps,
|
||||
};
|
||||
}
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
const user = users[i];
|
||||
|
||||
|
||||
@@ -104,10 +104,10 @@ const MoveThreadModal = ({onExited, post, actions}: Props) => {
|
||||
post,
|
||||
post_id: post.id,
|
||||
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]);
|
||||
channel_display_name: originalChannel?.display_name || '',
|
||||
channel_type: originalChannel?.type || 'O',
|
||||
channel_id: originalChannel?.id || '',
|
||||
}), [post, currentTeam?.name, originalChannel?.display_name, originalChannel?.type, originalChannel?.id]);
|
||||
|
||||
const notificationText = formatMessage({
|
||||
id: 'move_thread_modal.notification.dm_or_gm',
|
||||
@@ -180,7 +180,7 @@ const MoveThreadModal = ({onExited, post, actions}: Props) => {
|
||||
defaultMessage: 'Originally posted in ~{channelName}',
|
||||
},
|
||||
{
|
||||
channelName: originalChannel.display_name,
|
||||
channelName: originalChannel?.display_name || '',
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -16,7 +16,8 @@ import PostEditHistory from './post_edit_history';
|
||||
function mapStateToProps(state: GlobalState) {
|
||||
const selectedPostId = getSelectedPostId(state) || '';
|
||||
const originalPost = getPost(state, selectedPostId);
|
||||
const channelDisplayName = getCurrentChannel(state) ? getCurrentChannel(state).display_name : getChannel(state, originalPost.channel_id).display_name;
|
||||
const channel = getCurrentChannel(state) ?? getChannel(state, originalPost.channel_id);
|
||||
const channelDisplayName = channel?.display_name || '';
|
||||
|
||||
return {
|
||||
channelDisplayName,
|
||||
|
||||
@@ -65,13 +65,13 @@ export default class PostMarkdown extends React.PureComponent<Props> {
|
||||
let message = this.props.message;
|
||||
|
||||
if (this.props.post) {
|
||||
const renderedSystemMessage = renderSystemMessage(this.props.post,
|
||||
const renderedSystemMessage = this.props.channel ? renderSystemMessage(this.props.post,
|
||||
this.props.currentTeam?.name ?? '',
|
||||
this.props.channel,
|
||||
this.props.hideGuestTags,
|
||||
this.props.isUserCanManageMembers,
|
||||
this.props.isMilitaryTime,
|
||||
this.props.timezone);
|
||||
this.props.timezone) : null;
|
||||
if (renderedSystemMessage) {
|
||||
return <div>{renderedSystemMessage}</div>;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import PluggableIntroButtons from './pluggable_intro_buttons';
|
||||
|
||||
type Props = {
|
||||
currentUserId: string;
|
||||
channel: Channel;
|
||||
channel?: Channel;
|
||||
fullWidth: boolean;
|
||||
locale: string;
|
||||
channelProfiles: UserProfileType[];
|
||||
@@ -59,6 +59,10 @@ type Props = {
|
||||
|
||||
export default class ChannelIntroMessage extends React.PureComponent<Props> {
|
||||
toggleFavorite = () => {
|
||||
if (!this.props.channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.props.isFavorite) {
|
||||
this.props.actions.unfavoriteChannel(this.props.channel.id);
|
||||
} else {
|
||||
@@ -98,6 +102,10 @@ export default class ChannelIntroMessage extends React.PureComponent<Props> {
|
||||
centeredIntro = 'channel-intro--centered';
|
||||
}
|
||||
|
||||
if (!channel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (channel.type === Constants.DM_CHANNEL) {
|
||||
return createDMIntroMessage(channel, centeredIntro, currentUser, isFavorite, isMobileView, this.toggleFavorite, teammate, teammateName);
|
||||
} else if (channel.type === Constants.GM_CHANNEL) {
|
||||
|
||||
@@ -28,11 +28,11 @@ function mapStateToProps(state: GlobalState) {
|
||||
const enableUserCreation = config.EnableUserCreation === 'true';
|
||||
const isReadOnly = false;
|
||||
const team = getCurrentTeam(state);
|
||||
const channel = getCurrentChannel(state) || {};
|
||||
const channel = getCurrentChannel(state);
|
||||
const channelMember = getMyCurrentChannelMembership(state);
|
||||
const teammate = getDirectTeammate(state, channel.id);
|
||||
const teammate = channel ? getDirectTeammate(state, channel.id) : undefined;
|
||||
const currentUser = getCurrentUser(state);
|
||||
const creator = getUser(state, channel.creator_id);
|
||||
const creator = channel ? getUser(state, channel.creator_id) : undefined;
|
||||
|
||||
const usersLimit = 10;
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ function makeMapStateToProps() {
|
||||
}
|
||||
|
||||
if (ownProps.metadata.channel_type === General.DM_CHANNEL) {
|
||||
channelDisplayName = getChannel(state, {id: ownProps.metadata.channel_id}).display_name;
|
||||
channelDisplayName = getChannel(state, {id: ownProps.metadata.channel_id})?.display_name || '';
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -18,7 +18,7 @@ import type {FakePost, RhsState} from 'types/store/rhs';
|
||||
type Props = {
|
||||
currentTeam?: Team;
|
||||
posts: Post[];
|
||||
channel: Channel | null;
|
||||
channel?: Channel;
|
||||
selected: Post | FakePost;
|
||||
previousRhsState?: RhsState;
|
||||
}
|
||||
|
||||
@@ -37,11 +37,11 @@ function makeMapStateToProps() {
|
||||
|
||||
const currentChannelId = getCurrentChannelId(state);
|
||||
|
||||
const unreadCount = getUnreadCount(state, channel.id);
|
||||
const unreadCount = getUnreadCount(state, channel?.id || '');
|
||||
|
||||
return {
|
||||
channel,
|
||||
isCurrentChannel: channel.id === currentChannelId,
|
||||
isCurrentChannel: channel?.id === currentChannelId,
|
||||
currentTeamName: currentTeam?.name,
|
||||
unreadMentions: unreadCount.mentions,
|
||||
isUnread: unreadCount.showUnread,
|
||||
|
||||
@@ -32,6 +32,9 @@ function SidebarChannel({
|
||||
autoSortedCategoryIds,
|
||||
}: Props) {
|
||||
const [show, setShow] = useState(true);
|
||||
if (!channel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!currentTeamName) {
|
||||
return null;
|
||||
@@ -43,7 +46,7 @@ function SidebarChannel({
|
||||
|
||||
function setRef(refMethod?: (element: HTMLLIElement) => void) {
|
||||
return (ref: HTMLLIElement) => {
|
||||
setChannelRef(channel.id, ref);
|
||||
setChannelRef(channel?.id || '', ref);
|
||||
refMethod?.(ref);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import type {RhsState} from 'types/store/rhs';
|
||||
export type Props = {
|
||||
isExpanded: boolean;
|
||||
isOpen: boolean;
|
||||
channel: Channel;
|
||||
channel?: Channel;
|
||||
team?: Team;
|
||||
teamId: Team['id'];
|
||||
productId: ProductIdentifier;
|
||||
@@ -44,7 +44,7 @@ export type Props = {
|
||||
isPluginView: boolean;
|
||||
isPostEditHistory: boolean;
|
||||
previousRhsState: RhsState;
|
||||
rhsChannel: Channel;
|
||||
rhsChannel?: Channel;
|
||||
selectedPostId: string;
|
||||
selectedPostCardId: string;
|
||||
actions: {
|
||||
@@ -150,11 +150,11 @@ export default class SidebarRight extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
const {actions, isChannelFiles, isPinnedPosts, rhsChannel, channel} = this.props;
|
||||
if (isPinnedPosts && prevProps.isPinnedPosts === isPinnedPosts && rhsChannel.id !== prevProps.rhsChannel.id) {
|
||||
if (isPinnedPosts && prevProps.isPinnedPosts === isPinnedPosts && rhsChannel && rhsChannel.id !== prevProps.rhsChannel?.id) {
|
||||
actions.showPinnedPosts(rhsChannel.id);
|
||||
}
|
||||
|
||||
if (isChannelFiles && prevProps.isChannelFiles === isChannelFiles && rhsChannel.id !== prevProps.rhsChannel.id) {
|
||||
if (isChannelFiles && prevProps.isChannelFiles === isChannelFiles && rhsChannel && rhsChannel.id !== prevProps.rhsChannel?.id) {
|
||||
actions.showChannelFiles(rhsChannel.id);
|
||||
}
|
||||
|
||||
|
||||
@@ -1452,7 +1452,7 @@ export class AppCommandParser {
|
||||
};
|
||||
|
||||
// getChannel gets the channel in which the user is typing the command
|
||||
private getChannel = (): Channel | null => {
|
||||
private getChannel = (): Channel | undefined => {
|
||||
const state = this.store.getState();
|
||||
return selectChannel(state, this.channelID);
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ function makeMapStateToProps() {
|
||||
|
||||
let postIds: string[] = [];
|
||||
let userThread: UserThread | null = null;
|
||||
let channel: Channel | null = null;
|
||||
let channel: Channel | undefined;
|
||||
|
||||
if (selected) {
|
||||
postIds = getPostIdsForThread(state, selected.id);
|
||||
|
||||
@@ -31,7 +31,7 @@ export type Props = Attrs & {
|
||||
isCollapsedThreadsEnabled: boolean;
|
||||
appsEnabled: boolean;
|
||||
userThread?: UserThread | null;
|
||||
channel: Channel | null;
|
||||
channel?: Channel;
|
||||
selected?: Post | FakePost;
|
||||
currentUserId: string;
|
||||
currentTeamId: string;
|
||||
|
||||
@@ -36,7 +36,7 @@ export function makeGetRootPosts() {
|
||||
return Object.values(allPosts).filter((post) => {
|
||||
return (
|
||||
post.root_id === '' &&
|
||||
post.channel_id === channel.id &&
|
||||
post.channel_id === channel?.id &&
|
||||
post.state !== Posts.POST_DELETED
|
||||
);
|
||||
}).reduce((map: Record<string, boolean>, obj) => {
|
||||
|
||||
@@ -1279,7 +1279,7 @@ export function favoriteChannel(channelId: string): ActionFuncAsync {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const channel = getChannelSelector(state, channelId);
|
||||
const category = getCategoryInTeamByType(state, channel.team_id || getCurrentTeamId(state), CategoryTypes.FAVORITES);
|
||||
const category = getCategoryInTeamByType(state, channel?.team_id || getCurrentTeamId(state), CategoryTypes.FAVORITES);
|
||||
|
||||
Client4.trackEvent('action', 'action_channels_favorite');
|
||||
|
||||
@@ -1296,6 +1296,10 @@ export function unfavoriteChannel(channelId: string): ActionFuncAsync {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const channel = getChannelSelector(state, channelId);
|
||||
if (!channel) {
|
||||
return {data: false};
|
||||
}
|
||||
|
||||
const category = getCategoryInTeamByType(
|
||||
state,
|
||||
channel.team_id || getCurrentTeamId(state),
|
||||
|
||||
@@ -424,13 +424,16 @@ export function decrementThreadCounts(post: ExtendedPost): ActionFunc {
|
||||
const channel = getChannel(state, post.channel_id);
|
||||
const teamId = channel?.team_id || getCurrentTeamId(state);
|
||||
|
||||
dispatch({
|
||||
type: ThreadTypes.DECREMENT_THREAD_COUNTS,
|
||||
teamId,
|
||||
replies: thread.unread_replies,
|
||||
mentions: thread.unread_mentions,
|
||||
channelType: channel.type,
|
||||
});
|
||||
if (channel) {
|
||||
dispatch({
|
||||
type: ThreadTypes.DECREMENT_THREAD_COUNTS,
|
||||
teamId,
|
||||
replies: thread.unread_replies,
|
||||
mentions: thread.unread_mentions,
|
||||
channelType: channel.type,
|
||||
});
|
||||
}
|
||||
|
||||
return {data: true};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ export function getChannelMember(state: GlobalState, channelId: string, userId:
|
||||
// - The display_name set to the other user(s) names, following the Teammate Name Display setting
|
||||
// - The teammate_id for DM channels
|
||||
// - The status of the other user in a DM channel
|
||||
export function makeGetChannel(): (state: GlobalState, props: {id: string}) => Channel {
|
||||
export function makeGetChannel(): (state: GlobalState, props: {id: string}) => Channel | undefined {
|
||||
return createSelector(
|
||||
'makeGetChannel',
|
||||
getCurrentUserId,
|
||||
@@ -183,11 +183,11 @@ export function makeGetChannel(): (state: GlobalState, props: {id: string}) => C
|
||||
|
||||
// getChannel returns a channel as it exists in the store without filling in any additional details such as the
|
||||
// display_name for DM/GM channels.
|
||||
export function getChannel(state: GlobalState, id: string) {
|
||||
export function getChannel(state: GlobalState, id: string): Channel | undefined {
|
||||
return getAllChannels(state)[id];
|
||||
}
|
||||
|
||||
export function getMyChannelMembership(state: GlobalState, channelId: string): ChannelMembership {
|
||||
export function getMyChannelMembership(state: GlobalState, channelId: string): ChannelMembership | undefined {
|
||||
return getMyChannelMemberships(state)[channelId];
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ export function makeGetChannelsForIds(): (state: GlobalState, ids: string[]) =>
|
||||
);
|
||||
}
|
||||
|
||||
export const getCurrentChannel: (state: GlobalState) => Channel = createSelector(
|
||||
export const getCurrentChannel: (state: GlobalState) => Channel | undefined = createSelector(
|
||||
'getCurrentChannel',
|
||||
getAllChannels,
|
||||
getCurrentChannelId,
|
||||
@@ -255,7 +255,7 @@ export const getMyChannelMember: (state: GlobalState, channelId: string) => Chan
|
||||
},
|
||||
);
|
||||
|
||||
export const getCurrentChannelStats: (state: GlobalState) => ChannelStats = createSelector(
|
||||
export const getCurrentChannelStats: (state: GlobalState) => ChannelStats | undefined = createSelector(
|
||||
'getCurrentChannelStats',
|
||||
getAllChannelStats,
|
||||
getCurrentChannelId,
|
||||
@@ -296,7 +296,7 @@ export const isMutedChannel: (state: GlobalState, channelId: string) => boolean
|
||||
export const isCurrentChannelArchived: (state: GlobalState) => boolean = createSelector(
|
||||
'isCurrentChannelArchived',
|
||||
getCurrentChannel,
|
||||
(channel) => channel.delete_at !== 0,
|
||||
(channel) => channel?.delete_at !== 0,
|
||||
);
|
||||
|
||||
export const isCurrentChannelDefault: (state: GlobalState) => boolean = createSelector(
|
||||
@@ -313,15 +313,15 @@ export function isChannelReadOnlyById(state: GlobalState, channelId: string): bo
|
||||
return isChannelReadOnly(state, getChannel(state, channelId));
|
||||
}
|
||||
|
||||
export function isChannelReadOnly(state: GlobalState, channel: Channel): boolean {
|
||||
return channel && channel.name === General.DEFAULT_CHANNEL && !isCurrentUserSystemAdmin(state);
|
||||
export function isChannelReadOnly(state: GlobalState, channel?: Channel): boolean {
|
||||
return Boolean(channel && channel.name === General.DEFAULT_CHANNEL && !isCurrentUserSystemAdmin(state));
|
||||
}
|
||||
|
||||
export function getChannelMessageCounts(state: GlobalState): RelationOneToOne<Channel, ChannelMessageCount> {
|
||||
return state.entities.channels.messageCounts;
|
||||
}
|
||||
|
||||
export function getChannelMessageCount(state: GlobalState, channelId: string): ChannelMessageCount {
|
||||
export function getChannelMessageCount(state: GlobalState, channelId: string): ChannelMessageCount | undefined {
|
||||
return getChannelMessageCounts(state)[channelId];
|
||||
}
|
||||
|
||||
@@ -334,8 +334,8 @@ export const countCurrentChannelUnreadMessages: (state: GlobalState) => number =
|
||||
getCurrentChannelMessageCount,
|
||||
getMyCurrentChannelMembership,
|
||||
isCollapsedThreadsEnabled,
|
||||
(messageCount: ChannelMessageCount, membership?: ChannelMembership, crtEnabled?: boolean): number => {
|
||||
if (!membership) {
|
||||
(messageCount?: ChannelMessageCount, membership?: ChannelMembership, crtEnabled?: boolean): number => {
|
||||
if (!membership || !messageCount) {
|
||||
return 0;
|
||||
}
|
||||
return crtEnabled ? messageCount.root - membership.msg_count_root : messageCount.total - membership.msg_count;
|
||||
@@ -348,7 +348,7 @@ export function makeGetChannelUnreadCount(): (state: GlobalState, channelId: str
|
||||
(state: GlobalState, channelId: string) => getChannelMessageCount(state, channelId),
|
||||
(state: GlobalState, channelId: string) => getMyChannelMembership(state, channelId),
|
||||
isCollapsedThreadsEnabled,
|
||||
(messageCount: ChannelMessageCount, member: ChannelMembership, crtEnabled) =>
|
||||
(messageCount: ChannelMessageCount | undefined, member: ChannelMembership | undefined, crtEnabled) =>
|
||||
calculateUnreadCount(messageCount, member, crtEnabled),
|
||||
);
|
||||
}
|
||||
@@ -878,7 +878,7 @@ export const canManageChannelMembers: (state: GlobalState) => boolean = createSe
|
||||
Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS,
|
||||
),
|
||||
(
|
||||
channel: Channel,
|
||||
channel: Channel | undefined,
|
||||
managePrivateMembers: boolean,
|
||||
managePublicMembers: boolean,
|
||||
): boolean => {
|
||||
|
||||
@@ -208,7 +208,7 @@ export const haveIGroupPermission: (state: GlobalState, groupID: string, permiss
|
||||
},
|
||||
);
|
||||
|
||||
export function haveIChannelPermission(state: GlobalState, teamId: string | undefined, channelId: string, permission: string): boolean {
|
||||
export function haveIChannelPermission(state: GlobalState, teamId: string | undefined, channelId: string | undefined, permission: string): boolean {
|
||||
if (getMySystemPermissions(state).has(permission)) {
|
||||
return true;
|
||||
}
|
||||
@@ -217,7 +217,11 @@ export function haveIChannelPermission(state: GlobalState, teamId: string | unde
|
||||
return true;
|
||||
}
|
||||
|
||||
return getMyPermissionsByChannel(state)[channelId]?.has(permission);
|
||||
if (channelId) {
|
||||
return getMyPermissionsByChannel(state)[channelId]?.has(permission);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function haveICurrentTeamPermission(state: GlobalState, permission: string): boolean {
|
||||
|
||||
@@ -151,8 +151,8 @@ export function getGroupDisplayNameFromUserIds(userIds: Set<string>, profiles: I
|
||||
return names.sort(sortUsernames).join(', ');
|
||||
}
|
||||
|
||||
export function isDefault(channel: Channel): boolean {
|
||||
return channel.name === General.DEFAULT_CHANNEL;
|
||||
export function isDefault(channel?: Channel): boolean {
|
||||
return channel?.name === General.DEFAULT_CHANNEL;
|
||||
}
|
||||
|
||||
export function completeDirectGroupInfo(usersState: UsersState, teammateNameDisplay: string, channel: Channel, omitCurrentUser = true) {
|
||||
|
||||
@@ -20,7 +20,7 @@ import type {PluginComponent} from 'types/store/plugins';
|
||||
import './call_button.scss';
|
||||
|
||||
type Props = {
|
||||
currentChannel: Channel;
|
||||
currentChannel?: Channel;
|
||||
channelMember?: ChannelMembership;
|
||||
pluginCallComponents: PluginComponent[];
|
||||
sidebarOpen: boolean;
|
||||
|
||||
@@ -61,7 +61,7 @@ export function findNextUnreadChannelId(curChannelId: string, allChannelIds: str
|
||||
return -1;
|
||||
}
|
||||
|
||||
export function isArchivedChannel(channel: Channel) {
|
||||
export function isArchivedChannel(channel?: Channel) {
|
||||
return Boolean(channel && channel.delete_at !== 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ export function getImageSrc(src: string, hasImageProxy = false): string {
|
||||
return src;
|
||||
}
|
||||
|
||||
export function canDeletePost(state: GlobalState, post: Post, channel: Channel): boolean {
|
||||
export function canDeletePost(state: GlobalState, post: Post, channel?: Channel): boolean {
|
||||
if (post.type === Constants.PostTypes.FAKE_PARENT_DELETED) {
|
||||
return false;
|
||||
}
|
||||
@@ -645,6 +645,9 @@ export function areConsecutivePostsBySameUser(post: Post, previousPost: Post): b
|
||||
// Note: In the case of DM_CHANNEL, users must be fetched beforehand.
|
||||
export function getPostURL(state: GlobalState, post: Post): string {
|
||||
const channel = getChannel(state, post.channel_id);
|
||||
if (!channel) {
|
||||
return '';
|
||||
}
|
||||
const currentUserId = getCurrentUserId(state);
|
||||
const team = getTeam(state, channel.team_id || getCurrentTeamId(state));
|
||||
if (!team) {
|
||||
|
||||
Ссылка в новой задаче
Block a user