From 4145fd2f4e094e801157296e9e8ffad0f5eb58b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Thu, 24 Aug 2023 12:20:29 +0200 Subject: [PATCH] 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 --- .../new_message_separator.test.tsx | 6 ++++- .../new_message_separator.tsx | 24 ++++++++++++++++++- .../__snapshots__/post_list_row.test.tsx.snap | 3 +++ .../post_view/post_list_row/index.ts | 4 +++- .../post_list_row/post_list_row.test.tsx | 3 +++ .../post_view/post_list_row/post_list_row.tsx | 12 +++++++++- .../post_list_virtualized.tsx | 2 ++ .../virtualized_thread_viewer/index.ts | 3 +++ .../thread_viewer_row.tsx | 16 ++++++++++++- .../virtualized_thread_viewer.test.tsx | 2 ++ .../virtualized_thread_viewer.tsx | 6 +++++ .../separator/notification-separator.scss | 1 + webapp/channels/src/plugins/registry.ts | 6 +++++ webapp/channels/src/reducers/plugins/index.ts | 1 + webapp/channels/src/types/store/plugins.ts | 1 + 15 files changed, 85 insertions(+), 5 deletions(-) diff --git a/webapp/channels/src/components/post_view/new_message_separator/new_message_separator.test.tsx b/webapp/channels/src/components/post_view/new_message_separator/new_message_separator.test.tsx index e47f7b9fb9..039636a5f1 100644 --- a/webapp/channels/src/components/post_view/new_message_separator/new_message_separator.test.tsx +++ b/webapp/channels/src/components/post_view/new_message_separator/new_message_separator.test.tsx @@ -10,7 +10,11 @@ import NewMessageSeparator from './new_message_separator'; describe('components/post_view/new_message_separator', () => { test('should render new_message_separator', () => { renderWithIntl( - , + , ); const newMessage = screen.getByText('New Messages'); diff --git a/webapp/channels/src/components/post_view/new_message_separator/new_message_separator.tsx b/webapp/channels/src/components/post_view/new_message_separator/new_message_separator.tsx index 67c583fe5a..1998daef1a 100644 --- a/webapp/channels/src/components/post_view/new_message_separator/new_message_separator.tsx +++ b/webapp/channels/src/components/post_view/new_message_separator/new_message_separator.tsx @@ -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; + newMessagesSeparatorActions: PluginComponent[]; + lastViewedAt: number; + channelId?: string; + threadId?: string; } export default class NewMessageSeparator extends React.PureComponent { render(): JSX.Element { + const pluginItems = this.props.newMessagesSeparatorActions?. + map((item) => { + if (!item.component) { + return null; + } + + const Component = item.component as any; + return ( + + ); + }); + return (
{ id='posts_view.newMsg' defaultMessage='New Messages' /> - + {pluginItems}
); diff --git a/webapp/channels/src/components/post_view/post_list_row/__snapshots__/post_list_row.test.tsx.snap b/webapp/channels/src/components/post_view/post_list_row/__snapshots__/post_list_row.test.tsx.snap index 081343fdeb..c5959855b3 100644 --- a/webapp/channels/src/components/post_view/post_list_row/__snapshots__/post_list_row.test.tsx.snap +++ b/webapp/channels/src/components/post_view/post_list_row/__snapshots__/post_list_row.test.tsx.snap @@ -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`] = ` `; diff --git a/webapp/channels/src/components/post_view/post_list_row/index.ts b/webapp/channels/src/components/post_view/post_list_row/index.ts index ce41bb470f..71858eda84 100644 --- a/webapp/channels/src/components/post_view/post_list_row/index.ts +++ b/webapp/channels/src/components/post_view/post_list_row/index.ts @@ -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); diff --git a/webapp/channels/src/components/post_view/post_list_row/post_list_row.test.tsx b/webapp/channels/src/components/post_view/post_list_row/post_list_row.test.tsx index 5650c41989..e4fe8916e4 100644 --- a/webapp/channels/src/components/post_view/post_list_row/post_list_row.test.tsx +++ b/webapp/channels/src/components/post_view/post_list_row/post_list_row.test.tsx @@ -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', () => { diff --git a/webapp/channels/src/components/post_view/post_list_row/post_list_row.tsx b/webapp/channels/src/components/post_view/post_list_row/post_list_row.tsx index 3acecdccc3..aa3738beec 100644 --- a/webapp/channels/src/components/post_view/post_list_row/post_list_row.tsx +++ b/webapp/channels/src/components/post_view/post_list_row/post_list_row.tsx @@ -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 { if (PostListUtils.isStartOfNewMessages(listId)) { return ( - + ); } diff --git a/webapp/channels/src/components/post_view/post_list_virtualized/post_list_virtualized.tsx b/webapp/channels/src/components/post_view/post_list_virtualized/post_list_virtualized.tsx index 98b7a28379..843696d8c9 100644 --- a/webapp/channels/src/components/post_view/post_list_virtualized/post_list_virtualized.tsx +++ b/webapp/channels/src/components/post_view/post_list_virtualized/post_list_virtualized.tsx @@ -380,6 +380,8 @@ export default class PostList extends React.PureComponent { isLastPost={isLastPost} loadingNewerPosts={this.props.loadingNewerPosts} loadingOlderPosts={this.props.loadingOlderPosts} + lastViewedAt={this.props.lastViewedAt} + channelId={this.props.channelId} /> ); diff --git a/webapp/channels/src/components/threading/virtualized_thread_viewer/index.ts b/webapp/channels/src/components/threading/virtualized_thread_viewer/index.ts index e786684366..97ee36a111 100644 --- a/webapp/channels/src/components/threading/virtualized_thread_viewer/index.ts +++ b/webapp/channels/src/components/threading/virtualized_thread_viewer/index.ts @@ -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, }; }; } diff --git a/webapp/channels/src/components/threading/virtualized_thread_viewer/thread_viewer_row.tsx b/webapp/channels/src/components/threading/virtualized_thread_viewer/thread_viewer_row.tsx index 1f6479b357..c5b71deba1 100644 --- a/webapp/channels/src/components/threading/virtualized_thread_viewer/thread_viewer_row.tsx +++ b/webapp/channels/src/components/threading/virtualized_thread_viewer/thread_viewer_row.tsx @@ -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; + 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 ; + return ( + + ); case isRootPost: return ( diff --git a/webapp/channels/src/components/threading/virtualized_thread_viewer/virtualized_thread_viewer.test.tsx b/webapp/channels/src/components/threading/virtualized_thread_viewer/virtualized_thread_viewer.test.tsx index 0c8faa4afa..aea2a803c8 100644 --- a/webapp/channels/src/components/threading/virtualized_thread_viewer/virtualized_thread_viewer.test.tsx +++ b/webapp/channels/src/components/threading/virtualized_thread_viewer/virtualized_thread_viewer.test.tsx @@ -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(); diff --git a/webapp/channels/src/components/threading/virtualized_thread_viewer/virtualized_thread_viewer.tsx b/webapp/channels/src/components/threading/virtualized_thread_viewer/virtualized_thread_viewer.tsx index 47ca3d60ed..b0ad23b8af 100644 --- a/webapp/channels/src/components/threading/virtualized_thread_viewer/virtualized_thread_viewer.tsx +++ b/webapp/channels/src/components/threading/virtualized_thread_viewer/virtualized_thread_viewer.tsx @@ -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 { 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} /> ); diff --git a/webapp/channels/src/components/widgets/separator/notification-separator.scss b/webapp/channels/src/components/widgets/separator/notification-separator.scss index 2e4854f043..15edd7897c 100644 --- a/webapp/channels/src/components/widgets/separator/notification-separator.scss +++ b/webapp/channels/src/components/widgets/separator/notification-separator.scss @@ -6,6 +6,7 @@ } .separator__text { + display: inline-flex; color: #f80; font-weight: normal; } diff --git a/webapp/channels/src/plugins/registry.ts b/webapp/channels/src/plugins/registry.ts index 1702723007..46f7c224c7 100644 --- a/webapp/channels/src/plugins/registry.ts +++ b/webapp/channels/src/plugins/registry.ts @@ -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 diff --git a/webapp/channels/src/reducers/plugins/index.ts b/webapp/channels/src/reducers/plugins/index.ts index b072e74d2d..8b6dcc1023 100644 --- a/webapp/channels/src/reducers/plugins/index.ts +++ b/webapp/channels/src/reducers/plugins/index.ts @@ -183,6 +183,7 @@ const initialComponents: PluginsState['components'] = { PostDropdownMenu: [], PostAction: [], PostEditorAction: [], + NewMessagesSeparatorAction: [], Product: [], RightHandSidebarComponent: [], UserGuideDropdownItem: [], diff --git a/webapp/channels/src/types/store/plugins.ts b/webapp/channels/src/types/store/plugins.ts index 9143dcd4c4..633a07b23b 100644 --- a/webapp/channels/src/types/store/plugins.ts +++ b/webapp/channels/src/types/store/plugins.ts @@ -29,6 +29,7 @@ export type PluginsState = { PostDropdownMenu: PluginComponent[]; PostAction: PluginComponent[]; PostEditorAction: PluginComponent[]; + NewMessagesSeparatorAction: PluginComponent[]; FilePreview: PluginComponent[]; MainMenu: PluginComponent[]; LinkTooltip: PluginComponent[];