@@ -23,6 +23,7 @@ import {QualifiedId} from '@wireapp/api-client/lib/user';
23
23
import cx from 'classnames' ;
24
24
import ko from 'knockout' ;
25
25
26
+ import { Icon } from 'Components/Icon' ;
26
27
import { ReadIndicator } from 'Components/MessagesList/Message/ReadIndicator' ;
27
28
import { Conversation } from 'src/script/entity/Conversation' ;
28
29
import { CompositeMessage } from 'src/script/entity/message/CompositeMessage' ;
@@ -31,8 +32,11 @@ import {useRelativeTimestamp} from 'src/script/hooks/useRelativeTimestamp';
31
32
import { StatusType } from 'src/script/message/StatusType' ;
32
33
import { useKoSubscribableChildren } from 'Util/ComponentUtil' ;
33
34
import { getMessageAriaLabel } from 'Util/conversationMessages' ;
35
+ import { t } from 'Util/LocalizerUtil' ;
36
+ import { checkIsMessageDelivered } from 'Util/util' ;
34
37
35
38
import { ContentAsset } from './asset' ;
39
+ import { deliveredMessageIndicator , messageBodyWrapper } from './ContentMessage.styles' ;
36
40
import { MessageActionsMenu } from './MessageActions/MessageActions' ;
37
41
import { useMessageActionsState } from './MessageActions/MessageActions.state' ;
38
42
import { MessageReactionsList } from './MessageActions/MessageReactions/MessageReactionsList' ;
@@ -48,6 +52,7 @@ import {ContextMenuEntry} from '../../../../ui/ContextMenu';
48
52
import { EphemeralTimer } from '../EphemeralTimer' ;
49
53
import { MessageTime } from '../MessageTime' ;
50
54
import { useMessageFocusedTabIndex } from '../util' ;
55
+
51
56
export interface ContentMessageProps extends Omit < MessageActions , 'onClickResetSession' > {
52
57
contextMenu : { entries : ko . Subscribable < ContextMenuEntry [ ] > } ;
53
58
conversation : Conversation ;
@@ -104,6 +109,7 @@ export const ContentMessageComponent = ({
104
109
status,
105
110
quote,
106
111
isObfuscated,
112
+ readReceipts,
107
113
} = useKoSubscribableChildren ( message , [
108
114
'senderName' ,
109
115
'timestamp' ,
@@ -116,6 +122,7 @@ export const ContentMessageComponent = ({
116
122
'status' ,
117
123
'quote' ,
118
124
'isObfuscated' ,
125
+ 'readReceipts' ,
119
126
] ) ;
120
127
121
128
const timeAgo = useRelativeTimestamp ( message . timestamp ( ) ) ;
@@ -159,6 +166,8 @@ export const ContentMessageComponent = ({
159
166
}
160
167
} ;
161
168
169
+ const showDeliveredMessageIcon = checkIsMessageDelivered ( isLastDeliveredMessage , readReceipts ) ;
170
+
162
171
// Closing another ActionMenu on outside click
163
172
useClickOutside ( messageRef , hideActionMenuVisibility ) ;
164
173
@@ -194,73 +203,77 @@ export const ContentMessageComponent = ({
194
203
</ MessageHeader >
195
204
) }
196
205
197
- < div
198
- className = { cx ( 'message-body' , {
199
- 'message-asset' : isAssetMessage ,
200
- 'message-quoted' : ! ! quote ,
201
- 'ephemeral-asset-expired' : isObfuscated && isAssetMessage ,
202
- 'icon-file' : isObfuscated && isFileMessage ,
203
- 'icon-movie' : isObfuscated && isVideoMessage ,
204
- } ) }
205
- { ...( ephemeralCaption && { title : ephemeralCaption } ) }
206
- >
207
- { ephemeral_status === EphemeralStatusType . ACTIVE && (
208
- < div className = "message-ephemeral-timer" >
209
- < EphemeralTimer message = { message } />
210
- </ div >
211
- ) }
206
+ < div css = { messageBodyWrapper } >
207
+ < div
208
+ className = { cx ( 'message-body' , {
209
+ 'message-asset' : isAssetMessage ,
210
+ 'message-quoted' : ! ! quote ,
211
+ 'ephemeral-asset-expired' : isObfuscated && isAssetMessage ,
212
+ 'icon-file' : isObfuscated && isFileMessage ,
213
+ 'icon-movie' : isObfuscated && isVideoMessage ,
214
+ } ) }
215
+ { ...( ephemeralCaption && { title : ephemeralCaption } ) }
216
+ >
217
+ { ephemeral_status === EphemeralStatusType . ACTIVE && (
218
+ < div className = "message-ephemeral-timer" >
219
+ < EphemeralTimer message = { message } />
220
+ </ div >
221
+ ) }
222
+
223
+ { quote && (
224
+ < Quote
225
+ conversation = { conversation }
226
+ quote = { quote }
227
+ selfId = { selfId }
228
+ findMessage = { findMessage }
229
+ showDetail = { onClickImage }
230
+ focusMessage = { onClickTimestamp }
231
+ handleClickOnMessage = { onClickMessage }
232
+ showUserDetails = { onClickAvatar }
233
+ isMessageFocused = { msgFocusState }
234
+ />
235
+ ) }
212
236
213
- { quote && (
214
- < Quote
215
- conversation = { conversation }
216
- quote = { quote }
217
- selfId = { selfId }
218
- findMessage = { findMessage }
219
- showDetail = { onClickImage }
220
- focusMessage = { onClickTimestamp }
221
- handleClickOnMessage = { onClickMessage }
222
- showUserDetails = { onClickAvatar }
223
- isMessageFocused = { msgFocusState }
224
- />
225
- ) }
237
+ { assets . map ( asset => (
238
+ < ContentAsset
239
+ key = { asset . type }
240
+ asset = { asset }
241
+ message = { message }
242
+ selfId = { selfId }
243
+ onClickButton = { onClickButton }
244
+ onClickImage = { onClickImage }
245
+ onClickMessage = { onClickMessage }
246
+ isMessageFocused = { msgFocusState }
247
+ is1to1Conversation = { conversation . is1to1 ( ) }
248
+ onClickDetails = { ( ) => onClickDetails ( message ) }
249
+ />
250
+ ) ) }
226
251
227
- { assets . map ( asset => (
228
- < ContentAsset
229
- key = { asset . type }
230
- asset = { asset }
231
- message = { message }
232
- selfId = { selfId }
233
- onClickButton = { onClickButton }
234
- onClickImage = { onClickImage }
235
- onClickMessage = { onClickMessage }
236
- isMessageFocused = { msgFocusState }
237
- is1to1Conversation = { conversation . is1to1 ( ) }
238
- isLastDeliveredMessage = { isLastDeliveredMessage }
239
- onClickDetails = { ( ) => onClickDetails ( message ) }
240
- />
241
- ) ) }
252
+ { isAssetMessage && (
253
+ < ReadIndicator message = { message } is1to1Conversation = { conversation . is1to1 ( ) } onClick = { onClickDetails } />
254
+ ) }
242
255
243
- { isAssetMessage && (
244
- < ReadIndicator
245
- message = { message }
246
- is1to1Conversation = { conversation . is1to1 ( ) }
247
- isLastDeliveredMessage = { isLastDeliveredMessage }
248
- onClick = { onClickDetails }
249
- />
250
- ) }
256
+ { ! isConversationReadonly && isActionMenuVisible && (
257
+ < MessageActionsMenu
258
+ isMsgWithHeader = { ! hideHeader }
259
+ message = { message }
260
+ handleActionMenuVisibility = { setActionMenuVisibility }
261
+ contextMenu = { contextMenu }
262
+ isMessageFocused = { msgFocusState }
263
+ handleReactionClick = { onClickReaction }
264
+ reactionsTotalCount = { reactions . length }
265
+ isRemovedFromConversation = { conversation . removed_from_conversation ( ) }
266
+ />
267
+ ) }
268
+ </ div >
251
269
252
- { ! isConversationReadonly && isActionMenuVisible && (
253
- < MessageActionsMenu
254
- isMsgWithHeader = { ! hideHeader }
255
- message = { message }
256
- handleActionMenuVisibility = { setActionMenuVisibility }
257
- contextMenu = { contextMenu }
258
- isMessageFocused = { msgFocusState }
259
- handleReactionClick = { onClickReaction }
260
- reactionsTotalCount = { reactions . length }
261
- isRemovedFromConversation = { conversation . removed_from_conversation ( ) }
262
- />
263
- ) }
270
+ < div css = { deliveredMessageIndicator } >
271
+ { showDeliveredMessageIcon && (
272
+ < div data-uie-name = "status-message-read-receipt-delivered" title = { t ( 'conversationMessageDelivered' ) } >
273
+ < Icon . OutlineCheck />
274
+ </ div >
275
+ ) }
276
+ </ div >
264
277
</ div >
265
278
266
279
{ [ StatusType . FAILED , StatusType . FEDERATION_ERROR ] . includes ( status ) && (
0 commit comments