diff --git a/src/message/__tests__/messageActionSheet-test.js b/src/message/__tests__/messageActionSheet-test.js index f6e1bc32521..c5804915d70 100644 --- a/src/message/__tests__/messageActionSheet-test.js +++ b/src/message/__tests__/messageActionSheet-test.js @@ -6,6 +6,8 @@ import { streamNameOfStreamMessage } from '../../utils/recipient'; import * as eg from '../../__tests__/lib/exampleData'; import { constructMessageActionButtons, constructTopicActionButtons } from '../messageActionSheet'; import { getStreamsById } from '../../selectors'; +import { reducer } from '../../unread/unreadModel'; +import { initialState } from '../../unread/__tests__/unread-testlib'; const baseBackgroundData = deepFreeze({ alertWords: [], @@ -65,6 +67,34 @@ describe('constructTopicActionButtons', () => { const topic = streamMessage.subject; const streamId = streamMessage.stream_id; + const baseState = (() => { + const r = (state, action) => reducer(state, action, eg.plusReduxState); + let state = initialState; + state = r(state, eg.mkActionEventNewMessage(streamMessage)); + return state; + })(); + + test('show mark as read if topic is unread', () => { + const unreadStreams = baseState.streams; + const buttons = constructTopicActionButtons({ + backgroundData: { ...baseBackgroundData, unreadStreams }, + streamName, + streamId, + topic, + }); + expect(buttonTitles(buttons)).toContain('Mark topic as read'); + }); + + test('do not show mark as read if topic is read', () => { + const buttons = constructTopicActionButtons({ + backgroundData: baseBackgroundData, + streamName, + streamId, + topic, + }); + expect(buttonTitles(buttons)).not.toContain('Mark topic as read'); + }); + test('show Unmute topic option if topic is muted', () => { const mute = deepFreeze([[streamName, topic]]); const buttons = constructTopicActionButtons({ diff --git a/src/message/messageActionSheet.js b/src/message/messageActionSheet.js index 19b0aeba6b5..e0d9aabb685 100644 --- a/src/message/messageActionSheet.js +++ b/src/message/messageActionSheet.js @@ -121,6 +121,12 @@ const deleteMessage = async ({ auth, message, dispatch }) => { deleteMessage.title = 'Delete message'; deleteMessage.errorMessage = 'Failed to delete message'; +const markTopicAsRead = async ({ auth, streamId, topic, subscriptions }) => { + await api.markTopicAsRead(auth, streamId, topic); +}; +markTopicAsRead.title = 'Mark topic as read'; +markTopicAsRead.errorMessage = 'Failed to mark topic as read'; + const unmuteTopic = async ({ auth, streamId, topic }) => { await api.setTopicMute(auth, streamId, topic, false); }; @@ -242,6 +248,10 @@ export const constructTopicActionButtons = ({ if (ownUser.is_admin) { buttons.push(deleteTopic); } + const unreadCount = unreadStreams.get(streamId)?.get(topic)?.size; + if (unreadCount !== undefined && unreadCount > 0) { + buttons.push(markTopicAsRead); + } if (isTopicMuted(streamName, topic, mute)) { buttons.push(unmuteTopic); } else { diff --git a/static/translations/messages_en.json b/static/translations/messages_en.json index c52ffc5f884..7fcda476124 100644 --- a/static/translations/messages_en.json +++ b/static/translations/messages_en.json @@ -242,5 +242,6 @@ "Signed out": "Signed out", "Remove account?": "Remove account?", "This will make the mobile app on this device forget {email} on {realmUrl}.": "This will make the mobile app on this device forget {email} on {realmUrl}.", - "Remove account": "Remove account" + "Remove account": "Remove account", + "Failed to mark topic as read": "Failed to mark topic as read" }