mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
Add support for actions in the unreads bar from plugins (#24265)
* Add support for actions in the unreads bar from plugins * Adding channelId as parameter * Fixing linter errors * Changing the extensibility to the new messages separator * Making everything work with the plugin * Fixing linter and types errors * Fixing unit test * Tiny improvement in the styles --------- Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
parent
f46694037d
commit
4145fd2f4e
@ -10,7 +10,11 @@ import NewMessageSeparator from './new_message_separator';
|
||||
describe('components/post_view/new_message_separator', () => {
|
||||
test('should render new_message_separator', () => {
|
||||
renderWithIntl(
|
||||
<NewMessageSeparator separatorId='1234'/>,
|
||||
<NewMessageSeparator
|
||||
separatorId='1234'
|
||||
newMessagesSeparatorActions={[]}
|
||||
lastViewedAt={0}
|
||||
/>,
|
||||
);
|
||||
|
||||
const newMessage = screen.getByText('New Messages');
|
||||
|
@ -3,16 +3,38 @@
|
||||
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import {PluginComponent} from 'types/store/plugins';
|
||||
|
||||
import NotificationSeparator from 'components/widgets/separator/notification-separator';
|
||||
|
||||
type Props = {
|
||||
separatorId: string;
|
||||
wrapperRef?: React.RefObject<HTMLDivElement>;
|
||||
newMessagesSeparatorActions: PluginComponent[];
|
||||
lastViewedAt: number;
|
||||
channelId?: string;
|
||||
threadId?: string;
|
||||
}
|
||||
|
||||
export default class NewMessageSeparator extends React.PureComponent<Props> {
|
||||
render(): JSX.Element {
|
||||
const pluginItems = this.props.newMessagesSeparatorActions?.
|
||||
map((item) => {
|
||||
if (!item.component) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const Component = item.component as any;
|
||||
return (
|
||||
<Component
|
||||
key={item.id}
|
||||
lastViewedAt={this.props.lastViewedAt}
|
||||
channelId={this.props.channelId}
|
||||
threadId={this.props.threadId}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={this.props.wrapperRef}
|
||||
@ -23,7 +45,7 @@ export default class NewMessageSeparator extends React.PureComponent<Props> {
|
||||
id='posts_view.newMsg'
|
||||
defaultMessage='New Messages'
|
||||
/>
|
||||
|
||||
{pluginItems}
|
||||
</NotificationSeparator>
|
||||
</div>
|
||||
);
|
||||
|
@ -94,6 +94,9 @@ exports[`components/post_view/post_list_row should render more messages loading
|
||||
|
||||
exports[`components/post_view/post_list_row should render new messages line 1`] = `
|
||||
<NewMessageSeparator
|
||||
channelId="channel_id_1"
|
||||
lastViewedAt={0}
|
||||
newMessagesSeparatorActions={Array []}
|
||||
separatorId="start-of-new-messages"
|
||||
/>
|
||||
`;
|
||||
|
@ -29,10 +29,11 @@ function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
|
||||
const limitsLoaded = getCloudLimitsLoaded(state);
|
||||
const post = getPost(state, ownProps.listId);
|
||||
const currentUserId = getCurrentUserId(state);
|
||||
const newMessagesSeparatorActions = state.plugins.components.NewMessagesSeparatorAction;
|
||||
|
||||
const props: Pick<
|
||||
PostListRowProps,
|
||||
'shortcutReactToLastPostEmittedFrom' | 'usage' | 'limits' | 'limitsLoaded' | 'exceededLimitChannelId' | 'firstInaccessiblePostTime' | 'post' | 'currentUserId'
|
||||
'shortcutReactToLastPostEmittedFrom' | 'usage' | 'limits' | 'limitsLoaded' | 'exceededLimitChannelId' | 'firstInaccessiblePostTime' | 'post' | 'currentUserId' | 'newMessagesSeparatorActions'
|
||||
> = {
|
||||
shortcutReactToLastPostEmittedFrom,
|
||||
usage,
|
||||
@ -40,6 +41,7 @@ function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
|
||||
limitsLoaded,
|
||||
post,
|
||||
currentUserId,
|
||||
newMessagesSeparatorActions,
|
||||
};
|
||||
if ((ownProps.listId === PostListRowListIds.OLDER_MESSAGES_LOADER || ownProps.listId === PostListRowListIds.CHANNEL_INTRO_MESSAGE) && limitsLoaded) {
|
||||
const currentChannelId = getCurrentChannelId(state);
|
||||
|
@ -40,6 +40,9 @@ describe('components/post_view/post_list_row', () => {
|
||||
usage: {} as CloudUsage,
|
||||
post: TestHelper.getPostMock({id: 'post_id_1'}),
|
||||
currentUserId: 'user_id_1',
|
||||
newMessagesSeparatorActions: [],
|
||||
lastViewedAt: 0,
|
||||
channelId: 'channel_id_1',
|
||||
};
|
||||
|
||||
test('should render more messages loading indicator', () => {
|
||||
|
@ -12,6 +12,7 @@ import {CloudUsage, Limits} from '@mattermost/types/cloud';
|
||||
import type {emitShortcutReactToLastPostFrom} from 'actions/post_actions';
|
||||
|
||||
import CombinedUserActivityPost from 'components/post_view/combined_user_activity_post';
|
||||
import {PluginComponent} from 'types/store/plugins';
|
||||
import {Post} from '@mattermost/types/posts';
|
||||
import DateSeparator from 'components/post_view/date_separator';
|
||||
import NewMessageSeparator from 'components/post_view/new_message_separator/new_message_separator';
|
||||
@ -54,6 +55,10 @@ export type PostListRowProps = {
|
||||
limitsLoaded: boolean;
|
||||
exceededLimitChannelId?: string;
|
||||
firstInaccessiblePostTime?: number;
|
||||
lastViewedAt: number;
|
||||
channelId: string;
|
||||
|
||||
newMessagesSeparatorActions: PluginComponent[];
|
||||
|
||||
actions: {
|
||||
|
||||
@ -109,7 +114,12 @@ export default class PostListRow extends React.PureComponent<PostListRowProps> {
|
||||
|
||||
if (PostListUtils.isStartOfNewMessages(listId)) {
|
||||
return (
|
||||
<NewMessageSeparator separatorId={listId}/>
|
||||
<NewMessageSeparator
|
||||
separatorId={listId}
|
||||
newMessagesSeparatorActions={this.props.newMessagesSeparatorActions}
|
||||
channelId={this.props.channelId}
|
||||
lastViewedAt={this.props.lastViewedAt}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -380,6 +380,8 @@ export default class PostList extends React.PureComponent<Props, State> {
|
||||
isLastPost={isLastPost}
|
||||
loadingNewerPosts={this.props.loadingNewerPosts}
|
||||
loadingOlderPosts={this.props.loadingOlderPosts}
|
||||
lastViewedAt={this.props.lastViewedAt}
|
||||
channelId={this.props.channelId}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -46,12 +46,15 @@ function makeMapStateToProps() {
|
||||
showDate: !useRelativeTimestamp,
|
||||
lastViewedAt: collapsedThreads ? lastViewedAt : undefined,
|
||||
});
|
||||
const newMessagesSeparatorActions = state.plugins.components.NewMessagesSeparatorAction;
|
||||
|
||||
return {
|
||||
currentUserId,
|
||||
directTeammate,
|
||||
lastPost,
|
||||
replyListIds,
|
||||
lastViewedAt,
|
||||
newMessagesSeparatorActions,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import CombinedUserActivityPost from 'components/post_view/combined_user_activit
|
||||
import DateSeparator from 'components/post_view/date_separator';
|
||||
import NewMessageSeparator from 'components/post_view/new_message_separator/new_message_separator';
|
||||
import {Props as TimestampProps} from 'components/timestamp/timestamp';
|
||||
import {PluginComponent} from 'types/store/plugins';
|
||||
|
||||
import PostComponent from 'components/post';
|
||||
|
||||
@ -26,6 +27,9 @@ type Props = {
|
||||
onCardClick: (post: Post) => void;
|
||||
previousPostId: string;
|
||||
timestampProps?: Partial<TimestampProps>;
|
||||
lastViewedAt: number;
|
||||
threadId: string;
|
||||
newMessagesSeparatorActions: PluginComponent[];
|
||||
};
|
||||
|
||||
function noop() {}
|
||||
@ -38,6 +42,9 @@ function ThreadViewerRow({
|
||||
onCardClick,
|
||||
previousPostId,
|
||||
timestampProps,
|
||||
lastViewedAt,
|
||||
threadId,
|
||||
newMessagesSeparatorActions,
|
||||
}: Props) {
|
||||
switch (true) {
|
||||
case PostListUtils.isDateLine(listId): {
|
||||
@ -51,7 +58,14 @@ function ThreadViewerRow({
|
||||
}
|
||||
|
||||
case PostListUtils.isStartOfNewMessages(listId):
|
||||
return <NewMessageSeparator separatorId={listId}/>;
|
||||
return (
|
||||
<NewMessageSeparator
|
||||
separatorId={listId}
|
||||
lastViewedAt={lastViewedAt}
|
||||
threadId={threadId}
|
||||
newMessagesSeparatorActions={newMessagesSeparatorActions}
|
||||
/>
|
||||
);
|
||||
|
||||
case isRootPost:
|
||||
return (
|
||||
|
@ -63,6 +63,8 @@ describe('components/threading/VirtualizedThreadViewer', () => {
|
||||
teamId: '',
|
||||
useRelativeTimestamp: true,
|
||||
isThreadView: true,
|
||||
lastViewedAt: 0,
|
||||
newMessagesSeparatorActions: [],
|
||||
};
|
||||
test('should scroll to the bottom when the current user makes a new post in the thread', () => {
|
||||
const scrollToBottom = jest.fn();
|
||||
|
@ -20,6 +20,7 @@ import {getNewMessageIndex, getPreviousPostId, getLatestPostId} from 'utils/post
|
||||
import NewRepliesBanner from 'components/new_replies_banner';
|
||||
import FloatingTimestamp from 'components/post_view/floating_timestamp';
|
||||
import {THREADING_TIME as BASE_THREADING_TIME} from 'components/threading/common/options';
|
||||
import {PluginComponent} from 'types/store/plugins';
|
||||
|
||||
import CreateComment from './create_comment';
|
||||
import Row from './thread_viewer_row';
|
||||
@ -36,6 +37,8 @@ type Props = {
|
||||
selected: Post | FakePost;
|
||||
useRelativeTimestamp: boolean;
|
||||
isThreadView: boolean;
|
||||
lastViewedAt: number;
|
||||
newMessagesSeparatorActions: PluginComponent[];
|
||||
}
|
||||
|
||||
type State = {
|
||||
@ -386,6 +389,9 @@ class ThreadViewerVirtualized extends PureComponent<Props, State> {
|
||||
onCardClick={this.props.onCardClick}
|
||||
previousPostId={getPreviousPostId(data, index)}
|
||||
timestampProps={this.props.useRelativeTimestamp ? THREADING_TIME : undefined}
|
||||
lastViewedAt={this.props.lastViewedAt}
|
||||
threadId={this.props.selected.id}
|
||||
newMessagesSeparatorActions={this.props.newMessagesSeparatorActions}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -6,6 +6,7 @@
|
||||
}
|
||||
|
||||
.separator__text {
|
||||
display: inline-flex;
|
||||
color: #f80;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
@ -508,6 +508,12 @@ export default class PluginRegistry {
|
||||
return dispatchPluginComponentAction('PostEditorAction', this.id, component);
|
||||
});
|
||||
|
||||
// Register a component to the add to the new messages separator.
|
||||
// Accepts a React component. Returns a unique identifier.
|
||||
registerNewMessagesSeparatorActionComponent = reArg(['component'], ({component}: DPluginComponentProp) => {
|
||||
return dispatchPluginComponentAction('NewMessagesSeparatorAction', this.id, component);
|
||||
});
|
||||
|
||||
// Register a post menu list item by providing some text and an action function.
|
||||
// Accepts the following:
|
||||
// - text - A string or React element to display in the menu
|
||||
|
@ -183,6 +183,7 @@ const initialComponents: PluginsState['components'] = {
|
||||
PostDropdownMenu: [],
|
||||
PostAction: [],
|
||||
PostEditorAction: [],
|
||||
NewMessagesSeparatorAction: [],
|
||||
Product: [],
|
||||
RightHandSidebarComponent: [],
|
||||
UserGuideDropdownItem: [],
|
||||
|
@ -29,6 +29,7 @@ export type PluginsState = {
|
||||
PostDropdownMenu: PluginComponent[];
|
||||
PostAction: PluginComponent[];
|
||||
PostEditorAction: PluginComponent[];
|
||||
NewMessagesSeparatorAction: PluginComponent[];
|
||||
FilePreview: PluginComponent[];
|
||||
MainMenu: PluginComponent[];
|
||||
LinkTooltip: PluginComponent[];
|
||||
|
Loading…
Reference in New Issue
Block a user