diff --git a/ts/components/conversation/MissedCallNotification.tsx b/ts/components/conversation/MissedCallNotification.tsx
new file mode 100644
index 000000000..226868a74
--- /dev/null
+++ b/ts/components/conversation/MissedCallNotification.tsx
@@ -0,0 +1,36 @@
+import React from 'react';
+import { useSelector } from 'react-redux';
+import styled from 'styled-components';
+import { PubKey } from '../../session/types';
+
+import { PropsForMissedCallNotification } from '../../state/ducks/conversations';
+import { getSelectedConversation } from '../../state/selectors/conversations';
+import { ReadableMessage } from './ReadableMessage';
+
+const MissedCallContent = styled.div`
+ background-color: red;
+ width: 100%;
+ height: 30px;
+`;
+
+export const MissedCallNotification = (props: PropsForMissedCallNotification) => {
+ const { messageId, receivedAt, isUnread } = props;
+
+ const selectedConvoProps = useSelector(getSelectedConversation);
+
+ const displayName =
+ selectedConvoProps?.profileName ||
+ selectedConvoProps?.name ||
+ (selectedConvoProps?.id && PubKey.shorten(selectedConvoProps?.id));
+
+ return (
+
+ {window.i18n('callMissed', displayName)}
+
+ );
+};
diff --git a/ts/components/session/conversation/SessionMessagesList.tsx b/ts/components/session/conversation/SessionMessagesList.tsx
index b555301a4..5bcbafbc7 100644
--- a/ts/components/session/conversation/SessionMessagesList.tsx
+++ b/ts/components/session/conversation/SessionMessagesList.tsx
@@ -5,6 +5,7 @@ import {
PropsForExpirationTimer,
PropsForGroupInvitation,
PropsForGroupUpdate,
+ PropsForMissedCallNotification,
} from '../../../state/ducks/conversations';
import { getSortedMessagesTypesOfSelectedConversation } from '../../../state/selectors/conversations';
import { DataExtractionNotification } from '../../conversation/DataExtractionNotification';
@@ -12,6 +13,7 @@ import { GroupInvitation } from '../../conversation/GroupInvitation';
import { GroupNotification } from '../../conversation/GroupNotification';
import { Message } from '../../conversation/Message';
import { MessageDateBreak } from '../../conversation/message/DateBreak';
+import { MissedCallNotification } from '../../conversation/MissedCallNotification';
import { TimerNotification } from '../../conversation/TimerNotification';
import { SessionLastSeenIndicator } from './SessionLastSeenIndicator';
@@ -62,6 +64,16 @@ export const SessionMessagesList = (props: {
return [, dateBreak, unreadIndicator];
}
+ if (messageProps.message?.messageType === 'missed-call-notification') {
+ const msgProps = messageProps.message.props as PropsForMissedCallNotification;
+
+ return [
+ ,
+ dateBreak,
+ unreadIndicator,
+ ];
+ }
+
if (!messageProps) {
return null;
}
diff --git a/ts/models/message.ts b/ts/models/message.ts
index 8164af8fb..ce61f6b59 100644
--- a/ts/models/message.ts
+++ b/ts/models/message.ts
@@ -88,6 +88,7 @@ export class MessageModel extends Backbone.Model {
const propsForGroupInvitation = this.getPropsForGroupInvitation();
const propsForGroupNotification = this.getPropsForGroupNotification();
const propsForTimerNotification = this.getPropsForTimerNotification();
+ const isMissedCall = this.get('isMissedCall');
const messageProps: MessageModelPropsWithoutConvoProps = {
propsForMessage: this.getPropsForMessage(),
};
@@ -103,6 +104,15 @@ export class MessageModel extends Backbone.Model {
if (propsForTimerNotification) {
messageProps.propsForTimerNotification = propsForTimerNotification;
}
+
+ if (isMissedCall) {
+ messageProps.propsForMissedCall = {
+ isMissedCall,
+ messageId: this.id,
+ receivedAt: this.get('received_at') || Date.now(),
+ isUnread: this.isUnread(),
+ };
+ }
perfEnd(`getPropsMessage-${this.id}`, 'getPropsMessage');
return messageProps;
}
diff --git a/ts/models/messageType.ts b/ts/models/messageType.ts
index 6e7509cdc..f880be5be 100644
--- a/ts/models/messageType.ts
+++ b/ts/models/messageType.ts
@@ -108,6 +108,8 @@ export interface MessageAttributes {
* This field is used for unsending messages and used in sending unsend message requests.
*/
isDeleted?: boolean;
+
+ isMissedCall?: boolean;
}
export interface DataExtractionNotificationMsg {
@@ -177,6 +179,7 @@ export interface MessageAttributesOptionals {
direction?: any;
messageHash?: string;
isDeleted?: boolean;
+ isMissedCall?: boolean;
}
/**
diff --git a/ts/session/utils/CallManager.ts b/ts/session/utils/CallManager.ts
index 412e27ac7..5d2afbb0d 100644
--- a/ts/session/utils/CallManager.ts
+++ b/ts/session/utils/CallManager.ts
@@ -674,7 +674,11 @@ export async function handleCallTypeOffer(
async function handleMissedCall(sender: string, incomingOfferTimestamp: number) {
const incomingCallConversation = await getConversationById(sender);
- ToastUtils.pushedMissedCall(incomingCallConversation?.getNickname() || 'Unknown');
+ ToastUtils.pushedMissedCall(
+ incomingCallConversation?.getNickname() ||
+ incomingCallConversation?.getProfileName() ||
+ 'Unknown'
+ );
await incomingCallConversation?.addSingleMessage({
conversationId: incomingCallConversation.id,
@@ -683,7 +687,7 @@ async function handleMissedCall(sender: string, incomingOfferTimestamp: number)
sent_at: incomingOfferTimestamp,
received_at: Date.now(),
expireTimer: 0,
- body: 'Missed call',
+ isMissedCall: true,
unread: 1,
});
incomingCallConversation?.updateLastMessage();
diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts
index f31be1a8a..b4492c517 100644
--- a/ts/state/ducks/conversations.ts
+++ b/ts/state/ducks/conversations.ts
@@ -18,12 +18,20 @@ import { QuotedAttachmentType } from '../../components/conversation/Quote';
import { perfEnd, perfStart } from '../../session/utils/Performance';
import { omit } from 'lodash';
+export type PropsForMissedCallNotification = {
+ isMissedCall: boolean;
+ messageId: string;
+ receivedAt: number;
+ isUnread: boolean;
+};
+
export type MessageModelPropsWithoutConvoProps = {
propsForMessage: PropsForMessageWithoutConvoProps;
propsForGroupInvitation?: PropsForGroupInvitation;
propsForTimerNotification?: PropsForExpirationTimer;
propsForDataExtractionNotification?: PropsForDataExtractionNotification;
propsForGroupNotification?: PropsForGroupUpdate;
+ propsForMissedCall?: PropsForMissedCallNotification;
};
export type MessageModelPropsWithConvoProps = SortedMessageModelProps & {
diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts
index 2a01c8a38..1e2783942 100644
--- a/ts/state/selectors/conversations.ts
+++ b/ts/state/selectors/conversations.ts
@@ -184,7 +184,8 @@ export type MessagePropsType =
| 'data-extraction'
| 'timer-notification'
| 'regular-message'
- | 'unread-indicator';
+ | 'unread-indicator'
+ | 'missed-call-notification';
export const getSortedMessagesTypesOfSelectedConversation = createSelector(
getSortedMessagesOfSelectedConversation,
@@ -251,6 +252,20 @@ export const getSortedMessagesTypesOfSelectedConversation = createSelector(
};
}
+ if (msg.propsForMissedCall) {
+ return {
+ showUnreadIndicator: isFirstUnread,
+ showDateBreak,
+ message: {
+ messageType: 'missed-call-notification',
+ props: {
+ ...msg.propsForMissedCall,
+ messageId: msg.propsForMessage.id,
+ },
+ },
+ };
+ }
+
return {
showUnreadIndicator: isFirstUnread,
showDateBreak,