Add group calling events to the message timeline
This commit is contained in:
parent
a2f285d243
commit
0c039bf431
29 changed files with 1275 additions and 239 deletions
|
@ -2,9 +2,76 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { noop } from 'lodash';
|
||||
import { Manager, Reference, Popper } from 'react-popper';
|
||||
import { Theme, themeClassName } from '../util/theme';
|
||||
|
||||
interface EventWrapperPropsType {
|
||||
children: React.ReactNode;
|
||||
onHoverChanged: (_: boolean) => void;
|
||||
}
|
||||
|
||||
// React doesn't reliably fire `onMouseLeave` or `onMouseOut` events if wrapping a
|
||||
// disabled button. This uses native browser events to avoid that.
|
||||
//
|
||||
// See <https://lecstor.com/react-disabled-button-onmouseleave/>.
|
||||
const TooltipEventWrapper = React.forwardRef<
|
||||
HTMLSpanElement,
|
||||
EventWrapperPropsType
|
||||
>(({ onHoverChanged, children }, ref) => {
|
||||
const wrapperRef = React.useRef<HTMLSpanElement | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
const wrapperEl = wrapperRef.current;
|
||||
|
||||
if (!wrapperEl) {
|
||||
return noop;
|
||||
}
|
||||
|
||||
const on = () => {
|
||||
onHoverChanged(true);
|
||||
};
|
||||
const off = () => {
|
||||
onHoverChanged(false);
|
||||
};
|
||||
|
||||
wrapperEl.addEventListener('focus', on);
|
||||
wrapperEl.addEventListener('blur', off);
|
||||
wrapperEl.addEventListener('mouseenter', on);
|
||||
wrapperEl.addEventListener('mouseleave', off);
|
||||
|
||||
return () => {
|
||||
wrapperEl.removeEventListener('focus', on);
|
||||
wrapperEl.removeEventListener('blur', off);
|
||||
wrapperEl.removeEventListener('mouseenter', on);
|
||||
wrapperEl.removeEventListener('mouseleave', off);
|
||||
};
|
||||
}, [onHoverChanged]);
|
||||
|
||||
return (
|
||||
<span
|
||||
// This is a forward ref that also needs a ref of its own, so we set both here.
|
||||
ref={el => {
|
||||
wrapperRef.current = el;
|
||||
|
||||
// This is a simplified version of [what React does][0] to set a ref.
|
||||
// [0]: https://github.com/facebook/react/blob/29b7b775f2ecf878eaf605be959d959030598b07/packages/react-reconciler/src/ReactFiberCommitWork.js#L661-L677
|
||||
if (typeof ref === 'function') {
|
||||
ref(el);
|
||||
} else if (ref) {
|
||||
// I believe the types for `ref` are wrong in this case, as `ref.current` should
|
||||
// not be `readonly`. That's why we do this cast. See [the React source][1].
|
||||
// [1]: https://github.com/facebook/react/blob/29b7b775f2ecf878eaf605be959d959030598b07/packages/shared/ReactTypes.js#L78-L80
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
(ref as React.MutableRefObject<HTMLSpanElement | null>).current = el;
|
||||
}
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
export enum TooltipPlacement {
|
||||
Top = 'top',
|
||||
Right = 'right',
|
||||
|
@ -26,8 +93,9 @@ export const Tooltip: React.FC<PropsType> = ({
|
|||
sticky,
|
||||
theme,
|
||||
}) => {
|
||||
const isSticky = Boolean(sticky);
|
||||
const [showTooltip, setShowTooltip] = React.useState(isSticky);
|
||||
const [isHovering, setIsHovering] = React.useState(false);
|
||||
|
||||
const showTooltip = isHovering || Boolean(sticky);
|
||||
|
||||
const tooltipTheme = theme ? themeClassName(theme) : undefined;
|
||||
|
||||
|
@ -35,31 +103,9 @@ export const Tooltip: React.FC<PropsType> = ({
|
|||
<Manager>
|
||||
<Reference>
|
||||
{({ ref }) => (
|
||||
<span
|
||||
onBlur={() => {
|
||||
if (!isSticky) {
|
||||
setShowTooltip(false);
|
||||
}
|
||||
}}
|
||||
onFocus={() => {
|
||||
if (!isSticky) {
|
||||
setShowTooltip(true);
|
||||
}
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
if (!isSticky) {
|
||||
setShowTooltip(true);
|
||||
}
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
if (!isSticky) {
|
||||
setShowTooltip(false);
|
||||
}
|
||||
}}
|
||||
ref={ref}
|
||||
>
|
||||
<TooltipEventWrapper ref={ref} onHoverChanged={setIsHovering}>
|
||||
{children}
|
||||
</span>
|
||||
</TooltipEventWrapper>
|
||||
)}
|
||||
</Reference>
|
||||
<Popper placement={direction}>
|
||||
|
|
|
@ -1,90 +1,168 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import Measure from 'react-measure';
|
||||
|
||||
import { Timestamp } from './Timestamp';
|
||||
import { LocalizerType } from '../../types/Util';
|
||||
import { CallHistoryDetailsType } from '../../types/Calling';
|
||||
import { CallMode } from '../../types/Calling';
|
||||
import {
|
||||
CallingNotificationType,
|
||||
getCallingNotificationText,
|
||||
} from '../../util/callingNotification';
|
||||
import { missingCaseError } from '../../util/missingCaseError';
|
||||
import { Tooltip, TooltipPlacement } from '../Tooltip';
|
||||
|
||||
export type PropsData = {
|
||||
// Can be undefined because it comes from JS.
|
||||
callHistoryDetails?: CallHistoryDetailsType;
|
||||
};
|
||||
export interface PropsActionsType {
|
||||
messageSizeChanged: (messageId: string, conversationId: string) => void;
|
||||
returnToActiveCall: () => void;
|
||||
startCallingLobby: (_: {
|
||||
conversationId: string;
|
||||
isVideoCall: boolean;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
type PropsHousekeeping = {
|
||||
i18n: LocalizerType;
|
||||
conversationId: string;
|
||||
messageId: string;
|
||||
};
|
||||
|
||||
type Props = PropsData & PropsHousekeeping;
|
||||
type PropsType = CallingNotificationType & PropsActionsType & PropsHousekeeping;
|
||||
|
||||
export function getCallingNotificationText(
|
||||
callHistoryDetails: CallHistoryDetailsType,
|
||||
i18n: LocalizerType
|
||||
): string {
|
||||
const {
|
||||
wasIncoming,
|
||||
wasVideoCall,
|
||||
wasDeclined,
|
||||
acceptedTime,
|
||||
} = callHistoryDetails;
|
||||
const wasAccepted = Boolean(acceptedTime);
|
||||
export const CallingNotification: React.FC<PropsType> = React.memo(props => {
|
||||
const { conversationId, i18n, messageId, messageSizeChanged } = props;
|
||||
|
||||
if (wasIncoming) {
|
||||
if (wasDeclined) {
|
||||
if (wasVideoCall) {
|
||||
return i18n('declinedIncomingVideoCall');
|
||||
}
|
||||
return i18n('declinedIncomingAudioCall');
|
||||
}
|
||||
if (wasAccepted) {
|
||||
if (wasVideoCall) {
|
||||
return i18n('acceptedIncomingVideoCall');
|
||||
}
|
||||
return i18n('acceptedIncomingAudioCall');
|
||||
}
|
||||
if (wasVideoCall) {
|
||||
return i18n('missedIncomingVideoCall');
|
||||
}
|
||||
return i18n('missedIncomingAudioCall');
|
||||
}
|
||||
if (wasAccepted) {
|
||||
if (wasVideoCall) {
|
||||
return i18n('acceptedOutgoingVideoCall');
|
||||
}
|
||||
return i18n('acceptedOutgoingAudioCall');
|
||||
}
|
||||
if (wasVideoCall) {
|
||||
return i18n('missedOrDeclinedOutgoingVideoCall');
|
||||
}
|
||||
return i18n('missedOrDeclinedOutgoingAudioCall');
|
||||
}
|
||||
const previousHeightRef = useRef<null | number>(null);
|
||||
const [height, setHeight] = useState<null | number>(null);
|
||||
|
||||
export const CallingNotification = (props: Props): JSX.Element | null => {
|
||||
const { callHistoryDetails, i18n } = props;
|
||||
if (!callHistoryDetails) {
|
||||
useEffect(() => {
|
||||
if (height === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
previousHeightRef.current !== null &&
|
||||
height !== previousHeightRef.current
|
||||
) {
|
||||
messageSizeChanged(messageId, conversationId);
|
||||
}
|
||||
|
||||
previousHeightRef.current = height;
|
||||
}, [height, conversationId, messageId, messageSizeChanged]);
|
||||
|
||||
let timestamp: number;
|
||||
let callType: 'audio' | 'video';
|
||||
switch (props.callMode) {
|
||||
case CallMode.Direct:
|
||||
timestamp = props.acceptedTime || props.endedTime;
|
||||
callType = props.wasVideoCall ? 'video' : 'audio';
|
||||
break;
|
||||
case CallMode.Group:
|
||||
timestamp = props.startedTime;
|
||||
callType = 'video';
|
||||
break;
|
||||
default:
|
||||
window.log.error(missingCaseError(props));
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Measure
|
||||
bounds
|
||||
onResize={({ bounds }) => {
|
||||
if (!bounds) {
|
||||
window.log.error('We should be measuring the bounds');
|
||||
return;
|
||||
}
|
||||
setHeight(bounds.height);
|
||||
}}
|
||||
>
|
||||
{({ measureRef }) => (
|
||||
<div
|
||||
className={`module-message-calling--notification module-message-calling--${callType}`}
|
||||
ref={measureRef}
|
||||
>
|
||||
<div className={`module-message-calling--${callType}__icon`} />
|
||||
{getCallingNotificationText(props, i18n)}
|
||||
<div>
|
||||
<Timestamp
|
||||
i18n={i18n}
|
||||
timestamp={timestamp}
|
||||
extended
|
||||
direction="outgoing"
|
||||
withImageNoCaption={false}
|
||||
withSticker={false}
|
||||
withTapToViewExpired={false}
|
||||
module="module-message__metadata__date"
|
||||
/>
|
||||
</div>
|
||||
<CallingNotificationButton {...props} />
|
||||
</div>
|
||||
)}
|
||||
</Measure>
|
||||
);
|
||||
});
|
||||
|
||||
function CallingNotificationButton(props: PropsType) {
|
||||
if (props.callMode !== CallMode.Group || props.ended) {
|
||||
return null;
|
||||
}
|
||||
const { acceptedTime, endedTime, wasVideoCall } = callHistoryDetails;
|
||||
const callType = wasVideoCall ? 'video' : 'audio';
|
||||
return (
|
||||
<div
|
||||
className={`module-message-calling--notification module-message-calling--${callType}`}
|
||||
|
||||
const {
|
||||
activeCallConversationId,
|
||||
conversationId,
|
||||
deviceCount,
|
||||
i18n,
|
||||
maxDevices,
|
||||
returnToActiveCall,
|
||||
startCallingLobby,
|
||||
} = props;
|
||||
|
||||
let buttonText: string;
|
||||
let disabledTooltipText: undefined | string;
|
||||
let onClick: undefined | (() => void);
|
||||
if (activeCallConversationId) {
|
||||
if (activeCallConversationId === conversationId) {
|
||||
buttonText = i18n('calling__return');
|
||||
onClick = returnToActiveCall;
|
||||
} else {
|
||||
buttonText = i18n('calling__join');
|
||||
disabledTooltipText = i18n(
|
||||
'calling__call-notification__button__in-another-call-tooltip'
|
||||
);
|
||||
}
|
||||
} else if (deviceCount >= maxDevices) {
|
||||
buttonText = i18n('calling__call-is-full');
|
||||
disabledTooltipText = i18n(
|
||||
'calling__call-notification__button__call-full-tooltip',
|
||||
[String(deviceCount)]
|
||||
);
|
||||
} else {
|
||||
buttonText = i18n('calling__join');
|
||||
onClick = () => {
|
||||
startCallingLobby({ conversationId, isVideoCall: true });
|
||||
};
|
||||
}
|
||||
|
||||
const button = (
|
||||
<button
|
||||
className="module-message-calling--notification__button"
|
||||
disabled={Boolean(disabledTooltipText)}
|
||||
onClick={onClick}
|
||||
type="button"
|
||||
>
|
||||
<div className={`module-message-calling--${callType}__icon`} />
|
||||
{getCallingNotificationText(callHistoryDetails, i18n)}
|
||||
<div>
|
||||
<Timestamp
|
||||
i18n={i18n}
|
||||
timestamp={acceptedTime || endedTime}
|
||||
extended
|
||||
direction="outgoing"
|
||||
withImageNoCaption={false}
|
||||
withSticker={false}
|
||||
withTapToViewExpired={false}
|
||||
module="module-message__metadata__date"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{buttonText}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
if (disabledTooltipText) {
|
||||
return (
|
||||
<Tooltip content={disabledTooltipText} direction={TooltipPlacement.Top}>
|
||||
{button}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
return button;
|
||||
}
|
||||
|
|
|
@ -247,6 +247,10 @@ const actions = () => ({
|
|||
showIdentity: action('showIdentity'),
|
||||
|
||||
downloadNewVersion: action('downloadNewVersion'),
|
||||
|
||||
messageSizeChanged: action('messageSizeChanged'),
|
||||
startCallingLobby: action('startCallingLobby'),
|
||||
returnToActiveCall: action('returnToActiveCall'),
|
||||
});
|
||||
|
||||
const renderItem = (id: string) => (
|
||||
|
|
|
@ -10,6 +10,7 @@ import { EmojiPicker } from '../emoji/EmojiPicker';
|
|||
import { setup as setupI18n } from '../../../js/modules/i18n';
|
||||
import enMessages from '../../../_locales/en/messages.json';
|
||||
import { PropsType as TimelineItemProps, TimelineItem } from './TimelineItem';
|
||||
import { CallMode } from '../../types/Calling';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
|
@ -61,6 +62,9 @@ const getDefaultProps = () => ({
|
|||
scrollToQuotedMessage: action('scrollToQuotedMessage'),
|
||||
downloadNewVersion: action('downloadNewVersion'),
|
||||
showIdentity: action('showIdentity'),
|
||||
messageSizeChanged: action('messageSizeChanged'),
|
||||
startCallingLobby: action('startCallingLobby'),
|
||||
returnToActiveCall: action('returnToActiveCall'),
|
||||
|
||||
renderContact,
|
||||
renderEmojiPicker,
|
||||
|
@ -95,149 +99,253 @@ storiesOf('Components/Conversation/TimelineItem', module)
|
|||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
callHistoryDetails: {
|
||||
// declined incoming audio
|
||||
wasDeclined: true,
|
||||
wasIncoming: true,
|
||||
wasVideoCall: false,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
// declined incoming audio
|
||||
callMode: CallMode.Direct,
|
||||
wasDeclined: true,
|
||||
wasIncoming: true,
|
||||
wasVideoCall: false,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
callHistoryDetails: {
|
||||
// declined incoming video
|
||||
wasDeclined: true,
|
||||
wasIncoming: true,
|
||||
wasVideoCall: true,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
// declined incoming video
|
||||
callMode: CallMode.Direct,
|
||||
wasDeclined: true,
|
||||
wasIncoming: true,
|
||||
wasVideoCall: true,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
callHistoryDetails: {
|
||||
// accepted incoming audio
|
||||
acceptedTime: Date.now() - 300,
|
||||
wasDeclined: false,
|
||||
wasIncoming: true,
|
||||
wasVideoCall: false,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
// accepted incoming audio
|
||||
callMode: CallMode.Direct,
|
||||
acceptedTime: Date.now() - 300,
|
||||
wasDeclined: false,
|
||||
wasIncoming: true,
|
||||
wasVideoCall: false,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
callHistoryDetails: {
|
||||
// accepted incoming video
|
||||
acceptedTime: Date.now() - 400,
|
||||
wasDeclined: false,
|
||||
wasIncoming: true,
|
||||
wasVideoCall: true,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
// accepted incoming video
|
||||
callMode: CallMode.Direct,
|
||||
acceptedTime: Date.now() - 400,
|
||||
wasDeclined: false,
|
||||
wasIncoming: true,
|
||||
wasVideoCall: true,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
callHistoryDetails: {
|
||||
// missed (neither accepted nor declined) incoming audio
|
||||
wasDeclined: false,
|
||||
wasIncoming: true,
|
||||
wasVideoCall: false,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
// missed (neither accepted nor declined) incoming audio
|
||||
callMode: CallMode.Direct,
|
||||
wasDeclined: false,
|
||||
wasIncoming: true,
|
||||
wasVideoCall: false,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
callHistoryDetails: {
|
||||
// missed (neither accepted nor declined) incoming video
|
||||
wasDeclined: false,
|
||||
wasIncoming: true,
|
||||
wasVideoCall: true,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
// missed (neither accepted nor declined) incoming video
|
||||
callMode: CallMode.Direct,
|
||||
wasDeclined: false,
|
||||
wasIncoming: true,
|
||||
wasVideoCall: true,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
callHistoryDetails: {
|
||||
// accepted outgoing audio
|
||||
acceptedTime: Date.now() - 200,
|
||||
wasDeclined: false,
|
||||
wasIncoming: false,
|
||||
wasVideoCall: false,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
// accepted outgoing audio
|
||||
callMode: CallMode.Direct,
|
||||
acceptedTime: Date.now() - 200,
|
||||
wasDeclined: false,
|
||||
wasIncoming: false,
|
||||
wasVideoCall: false,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
callHistoryDetails: {
|
||||
// accepted outgoing video
|
||||
acceptedTime: Date.now() - 200,
|
||||
wasDeclined: false,
|
||||
wasIncoming: false,
|
||||
wasVideoCall: true,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
// accepted outgoing video
|
||||
callMode: CallMode.Direct,
|
||||
acceptedTime: Date.now() - 200,
|
||||
wasDeclined: false,
|
||||
wasIncoming: false,
|
||||
wasVideoCall: true,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
callHistoryDetails: {
|
||||
// declined outgoing audio
|
||||
wasDeclined: true,
|
||||
wasIncoming: false,
|
||||
wasVideoCall: false,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
// declined outgoing audio
|
||||
callMode: CallMode.Direct,
|
||||
wasDeclined: true,
|
||||
wasIncoming: false,
|
||||
wasVideoCall: false,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
callHistoryDetails: {
|
||||
// declined outgoing video
|
||||
wasDeclined: true,
|
||||
wasIncoming: false,
|
||||
wasVideoCall: true,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
// declined outgoing video
|
||||
callMode: CallMode.Direct,
|
||||
wasDeclined: true,
|
||||
wasIncoming: false,
|
||||
wasVideoCall: true,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
callHistoryDetails: {
|
||||
// missed (neither accepted nor declined) outgoing audio
|
||||
wasDeclined: false,
|
||||
wasIncoming: false,
|
||||
wasVideoCall: false,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
// missed (neither accepted nor declined) outgoing audio
|
||||
callMode: CallMode.Direct,
|
||||
wasDeclined: false,
|
||||
wasIncoming: false,
|
||||
wasVideoCall: false,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
callHistoryDetails: {
|
||||
// missed (neither accepted nor declined) outgoing video
|
||||
wasDeclined: false,
|
||||
wasIncoming: false,
|
||||
wasVideoCall: true,
|
||||
endedTime: Date.now(),
|
||||
// missed (neither accepted nor declined) outgoing video
|
||||
callMode: CallMode.Direct,
|
||||
wasDeclined: false,
|
||||
wasIncoming: false,
|
||||
wasVideoCall: true,
|
||||
endedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
// ongoing group call
|
||||
callMode: CallMode.Group,
|
||||
conversationId: 'abc123',
|
||||
creator: {
|
||||
firstName: 'Luigi',
|
||||
isMe: false,
|
||||
title: 'Luigi Mario',
|
||||
},
|
||||
ended: false,
|
||||
deviceCount: 1,
|
||||
maxDevices: 16,
|
||||
startedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
// ongoing group call started by you
|
||||
callMode: CallMode.Group,
|
||||
conversationId: 'abc123',
|
||||
creator: {
|
||||
firstName: 'Peach',
|
||||
isMe: true,
|
||||
title: 'Princess Peach',
|
||||
},
|
||||
ended: false,
|
||||
deviceCount: 1,
|
||||
maxDevices: 16,
|
||||
startedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
// ongoing group call, creator unknown
|
||||
callMode: CallMode.Group,
|
||||
conversationId: 'abc123',
|
||||
ended: false,
|
||||
deviceCount: 1,
|
||||
maxDevices: 16,
|
||||
startedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
// ongoing and active group call
|
||||
callMode: CallMode.Group,
|
||||
activeCallConversationId: 'abc123',
|
||||
conversationId: 'abc123',
|
||||
creator: {
|
||||
firstName: 'Luigi',
|
||||
isMe: false,
|
||||
title: 'Luigi Mario',
|
||||
},
|
||||
ended: false,
|
||||
deviceCount: 1,
|
||||
maxDevices: 16,
|
||||
startedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
// ongoing group call, but you're in another one
|
||||
callMode: CallMode.Group,
|
||||
activeCallConversationId: 'abc123',
|
||||
conversationId: 'xyz987',
|
||||
creator: {
|
||||
firstName: 'Luigi',
|
||||
isMe: false,
|
||||
title: 'Luigi Mario',
|
||||
},
|
||||
ended: false,
|
||||
deviceCount: 1,
|
||||
maxDevices: 16,
|
||||
startedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
// ongoing full group call
|
||||
callMode: CallMode.Group,
|
||||
conversationId: 'abc123',
|
||||
creator: {
|
||||
firstName: 'Luigi',
|
||||
isMe: false,
|
||||
title: 'Luigi Mario',
|
||||
},
|
||||
ended: false,
|
||||
deviceCount: 16,
|
||||
maxDevices: 16,
|
||||
startedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
// finished call
|
||||
callMode: CallMode.Group,
|
||||
conversationId: 'abc123',
|
||||
creator: {
|
||||
firstName: 'Luigi',
|
||||
isMe: false,
|
||||
title: 'Luigi Mario',
|
||||
},
|
||||
ended: true,
|
||||
deviceCount: 0,
|
||||
maxDevices: 16,
|
||||
startedTime: Date.now(),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
@ -13,8 +13,9 @@ import {
|
|||
|
||||
import {
|
||||
CallingNotification,
|
||||
PropsData as CallingNotificationProps,
|
||||
PropsActionsType as CallingNotificationActionsType,
|
||||
} from './CallingNotification';
|
||||
import { CallingNotificationType } from '../../util/callingNotification';
|
||||
import { InlineNotificationWrapper } from './InlineNotificationWrapper';
|
||||
import {
|
||||
PropsActions as UnsupportedMessageActionsType,
|
||||
|
@ -55,7 +56,7 @@ import {
|
|||
|
||||
type CallHistoryType = {
|
||||
type: 'callHistory';
|
||||
data: CallingNotificationProps;
|
||||
data: CallingNotificationType;
|
||||
};
|
||||
type LinkNotificationType = {
|
||||
type: 'linkNotification';
|
||||
|
@ -128,6 +129,7 @@ type PropsLocalType = {
|
|||
};
|
||||
|
||||
type PropsActionsType = MessageActionsType &
|
||||
CallingNotificationActionsType &
|
||||
UnsupportedMessageActionsType &
|
||||
SafetyNumberActionsType;
|
||||
|
||||
|
@ -143,8 +145,11 @@ export class TimelineItem extends React.PureComponent<PropsType> {
|
|||
isSelected,
|
||||
item,
|
||||
i18n,
|
||||
messageSizeChanged,
|
||||
renderContact,
|
||||
returnToActiveCall,
|
||||
selectMessage,
|
||||
startCallingLobby,
|
||||
} = this.props;
|
||||
|
||||
if (!item) {
|
||||
|
@ -164,7 +169,17 @@ export class TimelineItem extends React.PureComponent<PropsType> {
|
|||
<UnsupportedMessage {...this.props} {...item.data} i18n={i18n} />
|
||||
);
|
||||
} else if (item.type === 'callHistory') {
|
||||
notification = <CallingNotification i18n={i18n} {...item.data} />;
|
||||
notification = (
|
||||
<CallingNotification
|
||||
conversationId={conversationId}
|
||||
i18n={i18n}
|
||||
messageId={id}
|
||||
messageSizeChanged={messageSizeChanged}
|
||||
returnToActiveCall={returnToActiveCall}
|
||||
startCallingLobby={startCallingLobby}
|
||||
{...item.data}
|
||||
/>
|
||||
);
|
||||
} else if (item.type === 'linkNotification') {
|
||||
notification = (
|
||||
<div className="module-message-unsynced">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue