MM-52797 : Drafts badge doesn't clear when parent message deleted and removed (#23636)
* yup * int * c * ok * Update posts.ts * actual change * a * Update actions.ts * ty * Update drafts_link.tsx * revert to old function name --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
c0ee02356e
Коммит
adb248497d
@@ -299,6 +299,7 @@ export function markMostRecentPostInChannelAsUnread(channelId: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Action called by DeletePostModal when the post is deleted
|
||||||
export function deleteAndRemovePost(post: Post) {
|
export function deleteAndRemovePost(post: Post) {
|
||||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||||
const {error} = await dispatch(PostActions.deletePost(post));
|
const {error} = await dispatch(PostActions.deletePost(post));
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ type Draft = {
|
|||||||
*/
|
*/
|
||||||
export function getDrafts(teamId: string) {
|
export function getDrafts(teamId: string) {
|
||||||
const getLocalDrafts = makeGetDrafts(false);
|
const getLocalDrafts = makeGetDrafts(false);
|
||||||
|
|
||||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||||
const state = getState() as GlobalState;
|
const state = getState() as GlobalState;
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,24 @@
|
|||||||
/* eslint-disable max-lines */
|
/* eslint-disable max-lines */
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {ModalData} from 'types/actions.js';
|
|
||||||
|
|
||||||
import {isNil} from 'lodash';
|
import {isNil} from 'lodash';
|
||||||
|
|
||||||
|
import {PreferenceType} from '@mattermost/types/preferences';
|
||||||
|
import {Group, GroupSource} from '@mattermost/types/groups';
|
||||||
|
import {ChannelMemberCountsByGroup} from '@mattermost/types/channels';
|
||||||
|
import {Emoji} from '@mattermost/types/emojis';
|
||||||
|
import {ServerError} from '@mattermost/types/errors';
|
||||||
|
import {FileInfo} from '@mattermost/types/files';
|
||||||
|
|
||||||
|
import {ActionResult} from 'mattermost-redux/types/actions';
|
||||||
import {sortFileInfos} from 'mattermost-redux/utils/file_utils';
|
import {sortFileInfos} from 'mattermost-redux/utils/file_utils';
|
||||||
|
|
||||||
import * as GlobalActions from 'actions/global_actions';
|
import * as GlobalActions from 'actions/global_actions';
|
||||||
|
|
||||||
|
import {PostDraft} from 'types/store/draft';
|
||||||
|
import {ModalData} from 'types/actions';
|
||||||
|
|
||||||
import Constants, {AdvancedTextEditor as AdvancedTextEditorConst, Locations, ModalIdentifiers, Preferences} from 'utils/constants';
|
import Constants, {AdvancedTextEditor as AdvancedTextEditorConst, Locations, ModalIdentifiers, Preferences} from 'utils/constants';
|
||||||
import {PreferenceType} from '@mattermost/types/preferences';
|
|
||||||
import * as Keyboard from 'utils/keyboard';
|
import * as Keyboard from 'utils/keyboard';
|
||||||
import * as UserAgent from 'utils/user_agent';
|
import * as UserAgent from 'utils/user_agent';
|
||||||
import * as Utils from 'utils/utils';
|
import * as Utils from 'utils/utils';
|
||||||
@@ -28,18 +35,6 @@ import {
|
|||||||
mentionsMinusSpecialMentionsInText,
|
mentionsMinusSpecialMentionsInText,
|
||||||
} from 'utils/post_utils';
|
} from 'utils/post_utils';
|
||||||
import {getTable, hasHtmlLink, formatMarkdownMessage, isGitHubCodeBlock, formatGithubCodePaste, isHttpProtocol, isHttpsProtocol} from 'utils/paste';
|
import {getTable, hasHtmlLink, formatMarkdownMessage, isGitHubCodeBlock, formatGithubCodePaste, isHttpProtocol, isHttpsProtocol} from 'utils/paste';
|
||||||
|
|
||||||
import NotifyConfirmModal from 'components/notify_confirm_modal';
|
|
||||||
import {FileUpload as FileUploadClass} from 'components/file_upload/file_upload';
|
|
||||||
import PostDeletedModal from 'components/post_deleted_modal';
|
|
||||||
import {PostDraft} from 'types/store/draft';
|
|
||||||
import {Group, GroupSource} from '@mattermost/types/groups';
|
|
||||||
import {ChannelMemberCountsByGroup} from '@mattermost/types/channels';
|
|
||||||
import {FilePreviewInfo} from 'components/file_preview/file_preview';
|
|
||||||
import {Emoji} from '@mattermost/types/emojis';
|
|
||||||
import {ActionResult} from 'mattermost-redux/types/actions';
|
|
||||||
import {ServerError} from '@mattermost/types/errors';
|
|
||||||
import {FileInfo} from '@mattermost/types/files';
|
|
||||||
import EmojiMap from 'utils/emoji_map';
|
import EmojiMap from 'utils/emoji_map';
|
||||||
import {
|
import {
|
||||||
applyLinkMarkdown,
|
applyLinkMarkdown,
|
||||||
@@ -47,10 +42,14 @@ import {
|
|||||||
applyMarkdown,
|
applyMarkdown,
|
||||||
ApplyMarkdownOptions,
|
ApplyMarkdownOptions,
|
||||||
} from 'utils/markdown/apply_markdown';
|
} from 'utils/markdown/apply_markdown';
|
||||||
import AdvancedTextEditor from '../advanced_text_editor/advanced_text_editor';
|
|
||||||
import {TextboxClass, TextboxElement} from '../textbox';
|
|
||||||
|
|
||||||
import FileLimitStickyBanner from '../file_limit_sticky_banner';
|
import NotifyConfirmModal from 'components/notify_confirm_modal';
|
||||||
|
import {FileUpload as FileUploadClass} from 'components/file_upload/file_upload';
|
||||||
|
import PostDeletedModal from 'components/post_deleted_modal';
|
||||||
|
import {FilePreviewInfo} from 'components/file_preview/file_preview';
|
||||||
|
import AdvancedTextEditor from 'components/advanced_text_editor/advanced_text_editor';
|
||||||
|
import {TextboxClass, TextboxElement} from 'components/textbox';
|
||||||
|
import FileLimitStickyBanner from 'components/file_limit_sticky_banner';
|
||||||
|
|
||||||
const KeyCodes = Constants.KeyCodes;
|
const KeyCodes = Constants.KeyCodes;
|
||||||
|
|
||||||
@@ -350,7 +349,7 @@ class AdvancedCreateComment extends React.PureComponent<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
saveDraftOnUnmount = () => {
|
saveDraftOnUnmount = () => {
|
||||||
if (!this.isDraftEdited || !this.state.draft) {
|
if (!this.isDraftEdited || !this.state.draft || this.props.rootDeleted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ type Props = {
|
|||||||
type: 'channel' | 'thread';
|
type: 'channel' | 'thread';
|
||||||
user: UserProfile;
|
user: UserProfile;
|
||||||
value: PostDraft;
|
value: PostDraft;
|
||||||
isRemote: boolean;
|
isRemote?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ChannelDraft({
|
function ChannelDraft({
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ type Props = {
|
|||||||
status: UserStatus['status'];
|
status: UserStatus['status'];
|
||||||
displayName: string;
|
displayName: string;
|
||||||
draft: Draft;
|
draft: Draft;
|
||||||
isRemote: boolean;
|
isRemote?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DraftRow({draft, user, status, displayName, isRemote}: Props) {
|
function DraftRow({draft, user, status, displayName, isRemote}: Props) {
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ function Drafts({
|
|||||||
key={d.key}
|
key={d.key}
|
||||||
displayName={displayName}
|
displayName={displayName}
|
||||||
draft={d}
|
draft={d}
|
||||||
isRemote={draftRemotes[d.key]}
|
isRemote={draftRemotes?.[d.key]}
|
||||||
user={user}
|
user={user}
|
||||||
status={status}
|
status={status}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,56 +1,43 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React, {memo, useCallback, useEffect} from 'react';
|
import React, {memo, useEffect} from 'react';
|
||||||
import {useSelector, useDispatch} from 'react-redux';
|
import {useSelector, useDispatch} from 'react-redux';
|
||||||
import {NavLink, useRouteMatch} from 'react-router-dom';
|
import {NavLink, useRouteMatch} from 'react-router-dom';
|
||||||
import {useIntl} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
import {localDraftsAreEnabled, syncedDraftsAreAllowedAndEnabled} from 'mattermost-redux/selectors/entities/preferences';
|
import {localDraftsAreEnabled, syncedDraftsAreAllowedAndEnabled} from 'mattermost-redux/selectors/entities/preferences';
|
||||||
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
|
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
|
||||||
|
|
||||||
import {getDrafts} from 'actions/views/drafts';
|
import {getDrafts} from 'actions/views/drafts';
|
||||||
import {closeRightHandSide} from 'actions/views/rhs';
|
|
||||||
|
|
||||||
import {makeGetDraftsCount} from 'selectors/drafts';
|
import {makeGetDraftsCount} from 'selectors/drafts';
|
||||||
import {getIsRhsOpen, getRhsState} from 'selectors/rhs';
|
|
||||||
|
|
||||||
import {RHSStates} from 'utils/constants';
|
|
||||||
|
|
||||||
import ChannelMentionBadge from 'components/sidebar/sidebar_channel/channel_mention_badge';
|
import ChannelMentionBadge from 'components/sidebar/sidebar_channel/channel_mention_badge';
|
||||||
|
import DraftsTourTip from 'components/drafts/drafts_link/drafts_tour_tip/drafts_tour_tip';
|
||||||
|
|
||||||
import './drafts_link.scss';
|
import './drafts_link.scss';
|
||||||
|
|
||||||
import DraftsTourTip from './drafts_tour_tip/drafts_tour_tip';
|
|
||||||
|
|
||||||
const getDraftsCount = makeGetDraftsCount();
|
const getDraftsCount = makeGetDraftsCount();
|
||||||
|
|
||||||
function DraftsLink() {
|
function DraftsLink() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const localDraftsEnabled = useSelector(localDraftsAreEnabled);
|
const localDraftsEnabled = useSelector(localDraftsAreEnabled);
|
||||||
const syncedDraftsAllowedAndEnabled = useSelector(syncedDraftsAreAllowedAndEnabled);
|
const syncedDraftsAllowedAndEnabled = useSelector(syncedDraftsAreAllowedAndEnabled);
|
||||||
const {formatMessage} = useIntl();
|
|
||||||
const {url} = useRouteMatch();
|
|
||||||
const match = useRouteMatch('/:team/drafts');
|
|
||||||
const count = useSelector(getDraftsCount);
|
const count = useSelector(getDraftsCount);
|
||||||
const teamId = useSelector(getCurrentTeamId);
|
const teamId = useSelector(getCurrentTeamId);
|
||||||
const rhsOpen = useSelector(getIsRhsOpen);
|
|
||||||
const rhsState = useSelector(getRhsState);
|
const {url} = useRouteMatch();
|
||||||
|
const isDraftUrlMatch = useRouteMatch('/:team/drafts');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (syncedDraftsAllowedAndEnabled) {
|
if (syncedDraftsAllowedAndEnabled) {
|
||||||
dispatch(getDrafts(teamId));
|
dispatch(getDrafts(teamId));
|
||||||
}
|
}
|
||||||
}, [teamId, dispatch, syncedDraftsAllowedAndEnabled]);
|
}, [teamId, syncedDraftsAllowedAndEnabled]);
|
||||||
|
|
||||||
const openDrafts = useCallback((e) => {
|
if (!localDraftsEnabled || (!count && !isDraftUrlMatch)) {
|
||||||
e.stopPropagation();
|
|
||||||
if (rhsOpen && rhsState === RHSStates.EDIT_HISTORY) {
|
|
||||||
dispatch(closeRightHandSide());
|
|
||||||
}
|
|
||||||
}, [rhsOpen, rhsState]);
|
|
||||||
|
|
||||||
if (!localDraftsEnabled || (!count && !match)) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,10 +46,9 @@ function DraftsLink() {
|
|||||||
<li
|
<li
|
||||||
className='SidebarChannel'
|
className='SidebarChannel'
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
id={'sidebar-drafts-button'}
|
id='sidebar-drafts-button'
|
||||||
>
|
>
|
||||||
<NavLink
|
<NavLink
|
||||||
onClick={openDrafts}
|
|
||||||
to={`${url}/drafts`}
|
to={`${url}/drafts`}
|
||||||
id='sidebarItem_drafts'
|
id='sidebarItem_drafts'
|
||||||
activeClassName='active'
|
activeClassName='active'
|
||||||
@@ -76,7 +62,10 @@ function DraftsLink() {
|
|||||||
/>
|
/>
|
||||||
<div className='SidebarChannelLinkLabel_wrapper'>
|
<div className='SidebarChannelLinkLabel_wrapper'>
|
||||||
<span className='SidebarChannelLinkLabel sidebar-item__name'>
|
<span className='SidebarChannelLinkLabel sidebar-item__name'>
|
||||||
{formatMessage({id: 'drafts.sidebarLink', defaultMessage: 'Drafts'})}
|
<FormattedMessage
|
||||||
|
id='drafts.sidebarLink'
|
||||||
|
defaultMessage='Drafts'
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{count > 0 && (
|
{count > 0 && (
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ type Props = {
|
|||||||
type: 'channel' | 'thread';
|
type: 'channel' | 'thread';
|
||||||
user: UserProfile;
|
user: UserProfile;
|
||||||
value: PostDraft;
|
value: PostDraft;
|
||||||
isRemote: boolean;
|
isRemote?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ThreadDraft({
|
function ThreadDraft({
|
||||||
|
|||||||
34
webapp/channels/src/components/post/actions.ts
Обычный файл
34
webapp/channels/src/components/post/actions.ts
Обычный файл
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import {DispatchFunc} from 'mattermost-redux/types/actions';
|
||||||
|
import {ExtendedPost, removePost} from 'mattermost-redux/actions/posts';
|
||||||
|
|
||||||
|
import {isThreadOpen} from 'selectors/views/threads';
|
||||||
|
import {getGlobalItem} from 'selectors/storage';
|
||||||
|
|
||||||
|
import {removeDraft} from 'actions/views/drafts';
|
||||||
|
import {closeRightHandSide} from 'actions/views/rhs';
|
||||||
|
|
||||||
|
import {StoragePrefixes} from 'utils/constants';
|
||||||
|
|
||||||
|
import {GlobalState} from 'types/store';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This action is called when the deleted post which is shown as 'deleted' in the RHS is then removed from the channel manually.
|
||||||
|
* @param post Deleted post
|
||||||
|
*/
|
||||||
|
export function removePostCloseRHSDeleteDraft(post: ExtendedPost) {
|
||||||
|
return (dispatch: DispatchFunc, getState: () => GlobalState) => {
|
||||||
|
if (isThreadOpen(getState(), post.id)) {
|
||||||
|
dispatch(closeRightHandSide());
|
||||||
|
}
|
||||||
|
|
||||||
|
const draftKey = `${StoragePrefixes.COMMENT_DRAFT}${post.id}`;
|
||||||
|
if (getGlobalItem(getState(), draftKey, null)) {
|
||||||
|
dispatch(removeDraft(draftKey, post.channel_id, post.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return dispatch(removePost(post));
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,10 +4,12 @@
|
|||||||
import {connect, ConnectedProps} from 'react-redux';
|
import {connect, ConnectedProps} from 'react-redux';
|
||||||
import {AnyAction, bindActionCreators, Dispatch} from 'redux';
|
import {AnyAction, bindActionCreators, Dispatch} from 'redux';
|
||||||
|
|
||||||
|
import {Emoji} from '@mattermost/types/emojis';
|
||||||
|
import {Post} from '@mattermost/types/posts';
|
||||||
|
|
||||||
import {setActionsMenuInitialisationState} from 'mattermost-redux/actions/preferences';
|
import {setActionsMenuInitialisationState} from 'mattermost-redux/actions/preferences';
|
||||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||||
import {getPost, makeGetCommentCountForPost, makeIsPostCommentMention, isPostAcknowledgementsEnabled, isPostPriorityEnabled, UserActivityPost} from 'mattermost-redux/selectors/entities/posts';
|
import {getPost, makeGetCommentCountForPost, makeIsPostCommentMention, isPostAcknowledgementsEnabled, isPostPriorityEnabled, UserActivityPost} from 'mattermost-redux/selectors/entities/posts';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
get,
|
get,
|
||||||
getBool,
|
getBool,
|
||||||
@@ -15,11 +17,10 @@ import {
|
|||||||
} from 'mattermost-redux/selectors/entities/preferences';
|
} from 'mattermost-redux/selectors/entities/preferences';
|
||||||
import {getCurrentTeam, getTeam, getTeamMemberships} from 'mattermost-redux/selectors/entities/teams';
|
import {getCurrentTeam, getTeam, getTeamMemberships} from 'mattermost-redux/selectors/entities/teams';
|
||||||
import {getCurrentUserId, getUser} from 'mattermost-redux/selectors/entities/users';
|
import {getCurrentUserId, getUser} from 'mattermost-redux/selectors/entities/users';
|
||||||
|
import {getDirectTeammate} from 'mattermost-redux/selectors/entities/channels';
|
||||||
|
import {General} from 'mattermost-redux/constants';
|
||||||
|
|
||||||
import {Emoji} from '@mattermost/types/emojis';
|
|
||||||
import {Post} from '@mattermost/types/posts';
|
|
||||||
import {closeRightHandSide, selectPost, setRhsExpanded, selectPostCard, selectPostFromRightHandSideSearch} from 'actions/views/rhs';
|
import {closeRightHandSide, selectPost, setRhsExpanded, selectPostCard, selectPostFromRightHandSideSearch} from 'actions/views/rhs';
|
||||||
|
|
||||||
import {markPostAsUnread, emitShortcutReactToLastPostFrom} from 'actions/post_actions';
|
import {markPostAsUnread, emitShortcutReactToLastPostFrom} from 'actions/post_actions';
|
||||||
|
|
||||||
import {getShortcutReactToLastPostEmittedFrom, getOneClickReactionEmojis} from 'selectors/emojis';
|
import {getShortcutReactToLastPostEmittedFrom, getOneClickReactionEmojis} from 'selectors/emojis';
|
||||||
@@ -32,17 +33,10 @@ import {GlobalState} from 'types/store';
|
|||||||
import {isArchivedChannel} from 'utils/channel_utils';
|
import {isArchivedChannel} from 'utils/channel_utils';
|
||||||
import {areConsecutivePostsBySameUser, canDeletePost, shouldShowActionsMenu, shouldShowDotMenu} from 'utils/post_utils';
|
import {areConsecutivePostsBySameUser, canDeletePost, shouldShowActionsMenu, shouldShowDotMenu} from 'utils/post_utils';
|
||||||
import {Locations, Preferences, RHSStates} from 'utils/constants';
|
import {Locations, Preferences, RHSStates} from 'utils/constants';
|
||||||
|
|
||||||
import {ExtendedPost, removePost} from 'mattermost-redux/actions/posts';
|
|
||||||
import {DispatchFunc, GetStateFunc} from 'mattermost-redux/types/actions';
|
|
||||||
import {isThreadOpen} from 'selectors/views/threads';
|
|
||||||
|
|
||||||
import {General} from 'mattermost-redux/constants';
|
|
||||||
|
|
||||||
import {getDisplayNameByUser} from 'utils/utils';
|
import {getDisplayNameByUser} from 'utils/utils';
|
||||||
import {getDirectTeammate} from 'mattermost-redux/selectors/entities/channels';
|
|
||||||
|
|
||||||
import PostComponent from './post_component';
|
import PostComponent from './post_component';
|
||||||
|
import {removePostCloseRHSDeleteDraft} from './actions';
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
post?: Post | UserActivityPost;
|
post?: Post | UserActivityPost;
|
||||||
@@ -84,16 +78,6 @@ function isConsecutivePost(state: GlobalState, ownProps: OwnProps) {
|
|||||||
return consecutivePost;
|
return consecutivePost;
|
||||||
}
|
}
|
||||||
|
|
||||||
function removePostAndCloseRHS(post: ExtendedPost) {
|
|
||||||
return (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
|
||||||
const state = getState() as GlobalState;
|
|
||||||
if (isThreadOpen(state, post.id)) {
|
|
||||||
dispatch(closeRightHandSide());
|
|
||||||
}
|
|
||||||
return dispatch(removePost(post));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeMapStateToProps() {
|
function makeMapStateToProps() {
|
||||||
const isPostCommentMention = makeIsPostCommentMention();
|
const isPostCommentMention = makeIsPostCommentMention();
|
||||||
const getReplyCount = makeGetCommentCountForPost();
|
const getReplyCount = makeGetCommentCountForPost();
|
||||||
@@ -242,7 +226,7 @@ function mapDispatchToProps(dispatch: Dispatch<AnyAction>) {
|
|||||||
selectPost,
|
selectPost,
|
||||||
selectPostFromRightHandSideSearch,
|
selectPostFromRightHandSideSearch,
|
||||||
setRhsExpanded,
|
setRhsExpanded,
|
||||||
removePost: removePostAndCloseRHS,
|
removePost: removePostCloseRHSDeleteDraft,
|
||||||
closeRightHandSide,
|
closeRightHandSide,
|
||||||
selectPostCard,
|
selectPostCard,
|
||||||
}, dispatch),
|
}, dispatch),
|
||||||
|
|||||||
@@ -621,12 +621,10 @@ const PostComponent = (props: Props): JSX.Element => {
|
|||||||
<PostOptions
|
<PostOptions
|
||||||
{...props}
|
{...props}
|
||||||
teamId={teamId}
|
teamId={teamId}
|
||||||
setActionsMenuInitialisationState={props.actions.setActionsMenuInitialisationState}
|
|
||||||
handleDropdownOpened={handleDropdownOpened}
|
handleDropdownOpened={handleDropdownOpened}
|
||||||
handleCommentClick={handleCommentClick}
|
handleCommentClick={handleCommentClick}
|
||||||
hover={hover || a11yActive}
|
hover={hover || a11yActive}
|
||||||
removePost={props.actions.removePost}
|
removePost={props.actions.removePost}
|
||||||
isSearchResultsItem={Boolean(isSearchResultItem)}
|
|
||||||
handleJumpClick={handleJumpClick}
|
handleJumpClick={handleJumpClick}
|
||||||
isPostHeaderVisible={getPostHeaderVisible()}
|
isPostHeaderVisible={getPostHeaderVisible()}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ type Props = {
|
|||||||
enableEmojiPicker?: boolean;
|
enableEmojiPicker?: boolean;
|
||||||
isReadOnly?: boolean;
|
isReadOnly?: boolean;
|
||||||
channelIsArchived?: boolean;
|
channelIsArchived?: boolean;
|
||||||
setActionsMenuInitialisationState: (initializationState: Record<string, boolean>) => void;
|
|
||||||
handleCommentClick?: (e: React.MouseEvent) => void;
|
handleCommentClick?: (e: React.MouseEvent) => void;
|
||||||
handleJumpClick?: (e: React.MouseEvent) => void;
|
handleJumpClick?: (e: React.MouseEvent) => void;
|
||||||
handleDropdownOpened?: (e: boolean) => void;
|
handleDropdownOpened?: (e: boolean) => void;
|
||||||
@@ -42,7 +41,6 @@ type Props = {
|
|||||||
isMobileView: boolean;
|
isMobileView: boolean;
|
||||||
hasReplies?: boolean;
|
hasReplies?: boolean;
|
||||||
isFirstReply?: boolean;
|
isFirstReply?: boolean;
|
||||||
isSearchResultsItem?: boolean;
|
|
||||||
canReply?: boolean;
|
canReply?: boolean;
|
||||||
replyCount?: number;
|
replyCount?: number;
|
||||||
location: keyof typeof Locations;
|
location: keyof typeof Locations;
|
||||||
@@ -85,7 +83,9 @@ const PostOptions = (props: Props): JSX.Element => {
|
|||||||
const systemMessage = isSystemMessage(post);
|
const systemMessage = isSystemMessage(post);
|
||||||
const isFromAutoResponder = fromAutoResponder(post);
|
const isFromAutoResponder = fromAutoResponder(post);
|
||||||
|
|
||||||
const removePost = () => props.removePost(props.post);
|
function removePost() {
|
||||||
|
props.removePost(props.post);
|
||||||
|
}
|
||||||
|
|
||||||
const toggleEmojiPicker = () => {
|
const toggleEmojiPicker = () => {
|
||||||
setShowEmojiPicker(!showEmojiPicker);
|
setShowEmojiPicker(!showEmojiPicker);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {getThread} from 'mattermost-redux/selectors/entities/threads';
|
|||||||
import {isCollapsedThreadsEnabled} from 'mattermost-redux/selectors/entities/preferences';
|
import {isCollapsedThreadsEnabled} from 'mattermost-redux/selectors/entities/preferences';
|
||||||
import {appsEnabled} from 'mattermost-redux/selectors/entities/apps';
|
import {appsEnabled} from 'mattermost-redux/selectors/entities/apps';
|
||||||
|
|
||||||
import {removePost, getNewestPostThread, getPostThread} from 'mattermost-redux/actions/posts';
|
import {getNewestPostThread, getPostThread} from 'mattermost-redux/actions/posts';
|
||||||
import {getThread as fetchThread, updateThreadRead} from 'mattermost-redux/actions/threads';
|
import {getThread as fetchThread, updateThreadRead} from 'mattermost-redux/actions/threads';
|
||||||
|
|
||||||
import {GenericAction} from 'mattermost-redux/types/actions';
|
import {GenericAction} from 'mattermost-redux/types/actions';
|
||||||
@@ -78,7 +78,6 @@ function mapDispatchToProps(dispatch: Dispatch<GenericAction>) {
|
|||||||
getNewestPostThread,
|
getNewestPostThread,
|
||||||
getPostThread,
|
getPostThread,
|
||||||
getThread: fetchThread,
|
getThread: fetchThread,
|
||||||
removePost,
|
|
||||||
selectPostCard,
|
selectPostCard,
|
||||||
updateThreadLastOpened,
|
updateThreadLastOpened,
|
||||||
updateThreadRead,
|
updateThreadRead,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import React, {HTMLAttributes} from 'react';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import {ActionFunc} from 'mattermost-redux/types/actions';
|
import {ActionFunc} from 'mattermost-redux/types/actions';
|
||||||
import {ExtendedPost} from 'mattermost-redux/actions/posts';
|
|
||||||
|
|
||||||
import deferComponentRender from 'components/deferComponentRender';
|
import deferComponentRender from 'components/deferComponentRender';
|
||||||
import FileUploadOverlay from 'components/file_upload_overlay';
|
import FileUploadOverlay from 'components/file_upload_overlay';
|
||||||
@@ -31,7 +30,6 @@ export type Props = Attrs & {
|
|||||||
userThread?: UserThread | null;
|
userThread?: UserThread | null;
|
||||||
channel: Channel | null;
|
channel: Channel | null;
|
||||||
selected: Post | FakePost;
|
selected: Post | FakePost;
|
||||||
previousRhsState?: string;
|
|
||||||
currentUserId: string;
|
currentUserId: string;
|
||||||
currentTeamId: string;
|
currentTeamId: string;
|
||||||
socketConnectionStatus: boolean;
|
socketConnectionStatus: boolean;
|
||||||
@@ -40,7 +38,6 @@ export type Props = Attrs & {
|
|||||||
getNewestPostThread: (rootId: string) => Promise<any>|ActionFunc;
|
getNewestPostThread: (rootId: string) => Promise<any>|ActionFunc;
|
||||||
getPostThread: (rootId: string, fetchThreads: boolean) => Promise<any>|ActionFunc;
|
getPostThread: (rootId: string, fetchThreads: boolean) => Promise<any>|ActionFunc;
|
||||||
getThread: (userId: string, teamId: string, threadId: string, extended: boolean) => Promise<any>|ActionFunc;
|
getThread: (userId: string, teamId: string, threadId: string, extended: boolean) => Promise<any>|ActionFunc;
|
||||||
removePost: (post: ExtendedPost) => void;
|
|
||||||
selectPostCard: (post: Post) => void;
|
selectPostCard: (post: Post) => void;
|
||||||
updateThreadLastOpened: (threadId: string, lastViewedAt: number) => unknown;
|
updateThreadLastOpened: (threadId: string, lastViewedAt: number) => unknown;
|
||||||
updateThreadRead: (userId: string, teamId: string, threadId: string, timestamp: number) => unknown;
|
updateThreadRead: (userId: string, teamId: string, threadId: string, timestamp: number) => unknown;
|
||||||
|
|||||||
@@ -4,39 +4,39 @@
|
|||||||
import {AnyAction} from 'redux';
|
import {AnyAction} from 'redux';
|
||||||
import {batchActions} from 'redux-batched-actions';
|
import {batchActions} from 'redux-batched-actions';
|
||||||
|
|
||||||
import {Client4, DEFAULT_LIMIT_AFTER, DEFAULT_LIMIT_BEFORE} from 'mattermost-redux/client';
|
|
||||||
|
|
||||||
import {PostTypes, ChannelTypes, FileTypes, IntegrationTypes} from 'mattermost-redux/action_types';
|
|
||||||
|
|
||||||
import {getCurrentChannelId, getMyChannelMember as getMyChannelMemberSelector} from 'mattermost-redux/selectors/entities/channels';
|
|
||||||
import {getCustomEmojisByName as selectCustomEmojisByName} from 'mattermost-redux/selectors/entities/emojis';
|
|
||||||
import * as Selectors from 'mattermost-redux/selectors/entities/posts';
|
|
||||||
import {getCurrentUserId, getUsersByUsername} from 'mattermost-redux/selectors/entities/users';
|
|
||||||
|
|
||||||
import {isCombinedUserActivityPost} from 'mattermost-redux/utils/post_list';
|
|
||||||
|
|
||||||
import {ActionResult, DispatchFunc, GetStateFunc} from 'mattermost-redux/types/actions';
|
|
||||||
|
|
||||||
import {getUnreadScrollPositionPreference, isCollapsedThreadsEnabled} from 'mattermost-redux/selectors/entities/preferences';
|
|
||||||
|
|
||||||
import {General, Preferences, Posts} from '../constants';
|
|
||||||
import {UserProfile} from '@mattermost/types/users';
|
import {UserProfile} from '@mattermost/types/users';
|
||||||
import {Reaction} from '@mattermost/types/reactions';
|
import {Reaction} from '@mattermost/types/reactions';
|
||||||
import {Post, PostList, PostAcknowledgement} from '@mattermost/types/posts';
|
import {Post, PostList, PostAcknowledgement} from '@mattermost/types/posts';
|
||||||
import {GlobalState} from '@mattermost/types/store';
|
import {GlobalState} from '@mattermost/types/store';
|
||||||
import {ChannelUnread} from '@mattermost/types/channels';
|
import {Channel, ChannelUnread} from '@mattermost/types/channels';
|
||||||
import {FetchPaginatedThreadOptions} from '@mattermost/types/client4';
|
import {FetchPaginatedThreadOptions} from '@mattermost/types/client4';
|
||||||
|
|
||||||
import {getProfilesByIds, getProfilesByUsernames, getStatusesByIds} from './users';
|
import {Client4, DEFAULT_LIMIT_AFTER, DEFAULT_LIMIT_BEFORE} from 'mattermost-redux/client';
|
||||||
|
|
||||||
|
import {PostTypes, ChannelTypes, FileTypes, IntegrationTypes} from 'mattermost-redux/action_types';
|
||||||
|
|
||||||
|
import {ActionResult, DispatchFunc, GetStateFunc} from 'mattermost-redux/types/actions';
|
||||||
|
|
||||||
|
import {getCurrentChannelId, getMyChannelMember as getMyChannelMemberSelector} from 'mattermost-redux/selectors/entities/channels';
|
||||||
|
import {getCustomEmojisByName as selectCustomEmojisByName} from 'mattermost-redux/selectors/entities/emojis';
|
||||||
|
import * as PostSelectors from 'mattermost-redux/selectors/entities/posts';
|
||||||
|
import {getCurrentUserId, getUsersByUsername} from 'mattermost-redux/selectors/entities/users';
|
||||||
|
import {getUnreadScrollPositionPreference, isCollapsedThreadsEnabled} from 'mattermost-redux/selectors/entities/preferences';
|
||||||
|
|
||||||
|
import {isCombinedUserActivityPost} from 'mattermost-redux/utils/post_list';
|
||||||
|
|
||||||
|
import {General, Preferences, Posts} from 'mattermost-redux/constants';
|
||||||
|
|
||||||
|
import {getProfilesByIds, getProfilesByUsernames, getStatusesByIds} from 'mattermost-redux/actions/users';
|
||||||
import {
|
import {
|
||||||
deletePreferences,
|
deletePreferences,
|
||||||
savePreferences,
|
savePreferences,
|
||||||
} from './preferences';
|
} from 'mattermost-redux/actions/preferences';
|
||||||
import {bindClientFunc, forceLogoutIfNecessary} from './helpers';
|
import {bindClientFunc, forceLogoutIfNecessary} from 'mattermost-redux/actions/helpers';
|
||||||
import {logError} from './errors';
|
import {logError} from './errors';
|
||||||
import {systemEmojis, getCustomEmojiByName, getCustomEmojisByName} from './emojis';
|
import {systemEmojis, getCustomEmojiByName, getCustomEmojisByName} from 'mattermost-redux/actions/emojis';
|
||||||
import {selectChannel} from './channels';
|
import {selectChannel} from 'mattermost-redux/actions/channels';
|
||||||
import {decrementThreadCounts} from './threads';
|
import {decrementThreadCounts} from 'mattermost-redux/actions/threads';
|
||||||
|
|
||||||
// receivedPost should be dispatched after a single post from the server. This typically happens when an existing post
|
// receivedPost should be dispatched after a single post from the server. This typically happens when an existing post
|
||||||
// is updated.
|
// is updated.
|
||||||
@@ -174,7 +174,7 @@ export function createPost(post: Post, files: any[] = []) {
|
|||||||
const pendingPostId = post.pending_post_id || `${currentUserId}:${timestamp}`;
|
const pendingPostId = post.pending_post_id || `${currentUserId}:${timestamp}`;
|
||||||
let actions: AnyAction[] = [];
|
let actions: AnyAction[] = [];
|
||||||
|
|
||||||
if (Selectors.isPostIdSending(state, pendingPostId)) {
|
if (PostSelectors.isPostIdSending(state, pendingPostId)) {
|
||||||
return {data: true};
|
return {data: true};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ export function createPost(post: Post, files: any[] = []) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (post.root_id) {
|
if (post.root_id) {
|
||||||
newPost.reply_count = Selectors.getPostRepliesCount(state, post.root_id) + 1;
|
newPost.reply_count = PostSelectors.getPostRepliesCount(state, post.root_id) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We are retrying a pending post that had files
|
// We are retrying a pending post that had files
|
||||||
@@ -307,7 +307,7 @@ export function createPostImmediately(post: Post, files: any[] = []) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (post.root_id) {
|
if (post.root_id) {
|
||||||
newPost.reply_count = Selectors.getPostRepliesCount(state, post.root_id) + 1;
|
newPost.reply_count = PostSelectors.getPostRepliesCount(state, post.root_id) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (files.length) {
|
if (files.length) {
|
||||||
@@ -401,7 +401,7 @@ export function deletePost(post: ExtendedPost) {
|
|||||||
}
|
}
|
||||||
if (delPost.type === Posts.POST_TYPES.COMBINED_USER_ACTIVITY && delPost.system_post_ids) {
|
if (delPost.type === Posts.POST_TYPES.COMBINED_USER_ACTIVITY && delPost.system_post_ids) {
|
||||||
delPost.system_post_ids.forEach((systemPostId) => {
|
delPost.system_post_ids.forEach((systemPostId) => {
|
||||||
const systemPost = Selectors.getPost(state, systemPostId);
|
const systemPost = PostSelectors.getPost(state, systemPostId);
|
||||||
if (systemPost) {
|
if (systemPost) {
|
||||||
dispatch(deletePost(systemPost));
|
dispatch(deletePost(systemPost));
|
||||||
}
|
}
|
||||||
@@ -466,7 +466,7 @@ function getUnreadPostData(unreadChan: ChannelUnread, state: GlobalState) {
|
|||||||
export function setUnreadPost(userId: string, postId: string) {
|
export function setUnreadPost(userId: string, postId: string) {
|
||||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||||
let state = getState();
|
let state = getState();
|
||||||
const post = Selectors.getPost(state, postId);
|
const post = PostSelectors.getPost(state, postId);
|
||||||
let unreadChan;
|
let unreadChan;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -523,7 +523,7 @@ export function pinPost(postId: string) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const post = Selectors.getPost(state, postId);
|
const post = PostSelectors.getPost(state, postId);
|
||||||
if (post) {
|
if (post) {
|
||||||
actions.push(
|
actions.push(
|
||||||
receivedPost({
|
receivedPost({
|
||||||
@@ -544,6 +544,16 @@ export function pinPost(postId: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrements the pinned post count for a channel by 1
|
||||||
|
*/
|
||||||
|
export function decrementPinnedPostCount(channelId: Channel['id']) {
|
||||||
|
return {
|
||||||
|
type: ChannelTypes.DECREMENT_PINNED_POST_COUNT,
|
||||||
|
id: channelId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function unpinPost(postId: string) {
|
export function unpinPost(postId: string) {
|
||||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||||
dispatch({type: PostTypes.EDIT_POST_REQUEST});
|
dispatch({type: PostTypes.EDIT_POST_REQUEST});
|
||||||
@@ -565,7 +575,7 @@ export function unpinPost(postId: string) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const post = Selectors.getPost(state, postId);
|
const post = PostSelectors.getPost(state, postId);
|
||||||
if (post) {
|
if (post) {
|
||||||
actions.push(
|
actions.push(
|
||||||
receivedPost({
|
receivedPost({
|
||||||
@@ -573,10 +583,7 @@ export function unpinPost(postId: string) {
|
|||||||
is_pinned: false,
|
is_pinned: false,
|
||||||
update_at: Date.now(),
|
update_at: Date.now(),
|
||||||
}, isCollapsedThreadsEnabled(state)),
|
}, isCollapsedThreadsEnabled(state)),
|
||||||
{
|
decrementPinnedPostCount(post.channel_id),
|
||||||
type: ChannelTypes.DECREMENT_PINNED_POST_COUNT,
|
|
||||||
id: post.channel_id,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -781,7 +788,7 @@ export function getPostThread(rootId: string, fetchThreads = true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getNewestPostThread(rootId: string) {
|
export function getNewestPostThread(rootId: string) {
|
||||||
const getPostsForThread = Selectors.makeGetPostsForThread();
|
const getPostsForThread = PostSelectors.makeGetPostsForThread();
|
||||||
|
|
||||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||||
dispatch({type: PostTypes.GET_POST_THREAD_REQUEST});
|
dispatch({type: PostTypes.GET_POST_THREAD_REQUEST});
|
||||||
@@ -1016,7 +1023,7 @@ export function getThreadsForPosts(posts: Post[], fetchThreads = true) {
|
|||||||
if (!post.root_id) {
|
if (!post.root_id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const rootPost = Selectors.getPost(state, post.root_id);
|
const rootPost = PostSelectors.getPost(state, post.root_id);
|
||||||
|
|
||||||
if (!rootPost) {
|
if (!rootPost) {
|
||||||
rootsSet.add(post.root_id);
|
rootsSet.add(post.root_id);
|
||||||
@@ -1200,7 +1207,7 @@ export function removePost(post: ExtendedPost) {
|
|||||||
if (post.type === Posts.POST_TYPES.COMBINED_USER_ACTIVITY && post.system_post_ids) {
|
if (post.type === Posts.POST_TYPES.COMBINED_USER_ACTIVITY && post.system_post_ids) {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
for (const systemPostId of post.system_post_ids) {
|
for (const systemPostId of post.system_post_ids) {
|
||||||
const systemPost = Selectors.getPost(state, systemPostId);
|
const systemPost = PostSelectors.getPost(state, systemPostId);
|
||||||
|
|
||||||
if (systemPost) {
|
if (systemPost) {
|
||||||
dispatch(removePost(systemPost as any) as any);
|
dispatch(removePost(systemPost as any) as any);
|
||||||
@@ -1209,12 +1216,7 @@ export function removePost(post: ExtendedPost) {
|
|||||||
} else {
|
} else {
|
||||||
dispatch(postRemoved(post));
|
dispatch(postRemoved(post));
|
||||||
if (post.is_pinned) {
|
if (post.is_pinned) {
|
||||||
dispatch(
|
dispatch(decrementPinnedPostCount(post.channel_id));
|
||||||
{
|
|
||||||
type: ChannelTypes.DECREMENT_PINNED_POST_COUNT,
|
|
||||||
id: post.channel_id,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {data: true};
|
return {data: true};
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user