Voice Chats: Indicators and service messages (#1134)

This commit is contained in:
Alexander Zinchuk 2021-06-11 01:19:03 +03:00
parent 6f9b6ba3df
commit 23529106b3
9 changed files with 158 additions and 10 deletions

View File

@ -40,6 +40,8 @@ function buildApiChatFieldsFromPeerEntity(
&& { username: peerEntity.username }
),
...(('verified' in peerEntity) && { isVerified: peerEntity.verified }),
...(('callActive' in peerEntity) && { isCallActive: peerEntity.callActive }),
...(('callNotEmpty' in peerEntity) && { isCallNotEmpty: peerEntity.callNotEmpty }),
...((peerEntity instanceof GramJs.Chat || peerEntity instanceof GramJs.Channel) && {
...(peerEntity.participantsCount && { membersCount: peerEntity.participantsCount }),
joinDate: peerEntity.date,

View File

@ -599,6 +599,13 @@ function buildAction(
const currencySign = getCurrencySign(action.currency);
const amount = (Number(action.totalAmount) / 100).toFixed(2);
text = `You successfully transferred ${currencySign}${amount} to shop for %product%`;
} else if (action instanceof GramJs.MessageActionGroupCall) {
if (action.duration) {
const mins = Math.max(Math.round(action.duration / 60), 1);
text = `Voice chat ended (${mins} min${mins > 1 ? 's' : ''})`;
} else {
text = 'Voice chat started';
}
} else {
text = '%ACTION_NOT_IMPLEMENTED%';
}

View File

@ -355,6 +355,7 @@ async function getFullChannelInfo(
canViewParticipants,
linkedChatId,
hiddenPrehistory,
call,
} = result.fullChat;
const inviteLink = exportedInvite instanceof GramJs.ChatInviteExported
@ -387,6 +388,7 @@ async function getFullChannelInfo(
members,
kickedMembers,
adminMembers,
groupCallId: call ? call.id.toString() : undefined,
linkedChatId: linkedChatId ? getApiChatIdFromMtpPeer({ chatId: linkedChatId } as GramJs.TypePeer) : undefined,
},
users: [...(users || []), ...(bannedUsers || []), ...(adminUsers || [])],

View File

@ -30,6 +30,10 @@ export interface ApiChat {
isSupport?: boolean;
photos?: ApiPhoto[];
// Calls
isCallActive?: boolean;
isCallNotEmpty?: boolean;
// Current user permissions
isNotJoined?: boolean;
isCreator?: boolean;
@ -56,6 +60,21 @@ export interface ApiTypingStatus {
timestamp: number;
}
export interface ApiTypeGroupCall {
joinMuted?: true;
canChangeJoinMuted?: true;
joinDateAsc?: true;
scheduleStartSubscribed?: true;
id: number;
participantsCount: number;
params?: any;
title?: string;
streamDcId?: number;
recordStartDate?: number;
scheduleDate?: number;
version: number;
}
export interface ApiChatFullInfo {
about?: string;
onlineCount?: number;
@ -65,6 +84,7 @@ export interface ApiChatFullInfo {
canViewMembers?: boolean;
isPreHistoryHidden?: boolean;
inviteLink?: string;
groupCallId?: string;
slowMode?: {
seconds: number;
nextSendDate?: number;

View File

@ -52,6 +52,11 @@
}
}
.status {
position: relative;
flex-shrink: 0;
}
.info {
.title, .subtitle {
padding-right: .125rem;

View File

@ -40,6 +40,7 @@ import useEnsureMessage from '../../../hooks/useEnsureMessage';
import useChatContextActions from '../../../hooks/useChatContextActions';
import useFlag from '../../../hooks/useFlag';
import useMedia from '../../../hooks/useMedia';
import ChatCallStatus from './ChatCallStatus';
import { ChatAnimationTypes } from './hooks';
import Avatar from '../../common/Avatar';
@ -248,13 +249,18 @@ const Chat: FC<OwnProps & StateProps & DispatchProps> = ({
contextActions={contextActions}
onClick={handleClick}
>
<Avatar
chat={chat}
user={privateChatUser}
withOnlineStatus
isSavedMessages={privateChatUser && privateChatUser.isSelf}
lastSyncTime={lastSyncTime}
/>
<div className="status">
<Avatar
chat={chat}
user={privateChatUser}
withOnlineStatus
isSavedMessages={privateChatUser && privateChatUser.isSelf}
lastSyncTime={lastSyncTime}
/>
{chat.isCallActive && (
<ChatCallStatus isSelected={isSelected} isActive={animationLevel !== 0} />
)}
</div>
<div className="info">
<div className="title">
<h3>{renderText(getChatTitle(lang, chat, privateChatUser))}</h3>

View File

@ -0,0 +1,80 @@
@keyframes bar-animation-transform-1 {
0% { transform: scaleY(0.33); }
12.5% { transform: scaleY(1.66); }
25% { transform: scaleY(0.33); }
37.5% { transform: scaleY(1); }
50% { transform: scaleY(0.33); }
62.5% { transform: scaleY(1.66); }
75% { transform: scaleY(0.33); }
87.5% { transform: scaleY(1.66); }
100% { transform: scaleY(0.33); }
}
@keyframes bar-animation-transform-2 {
0% { transform: scaleY(1); }
12.5% { transform: scaleY(0.33); }
25% { transform: scaleY(1.66); }
37.5% { transform: scaleY(0.33); }
50% { transform: scaleY(1); }
62.5% { transform: scaleY(0.33); }
75% { transform: scaleY(1.66); }
87.5% { transform: scaleY(0.33); }
100% { transform: scaleY(1); }
}
.ChatCallStatus {
position: absolute;
right: 6px;
bottom: 0;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #0ac630;
border: 2px solid var(--color-background);
overflow: hidden;
.indicator {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
& > div {
width: 2px;
height: 6px;
background: var(--color-background);
border-radius: 1px;
margin: 1px;
will-change: transform;
transform: translateZ(0);
}
& > div:nth-child(odd) {
transform: scaleY(0.8);
}
& > div:nth-child(even) {
transform: scaleY(1.33);
}
}
&.selected {
background-color: var(--color-white);
border-color: var(--color-chat-active);
.indicator div{
background-color: var(--color-chat-active);
}
}
&.active .indicator {
div:nth-child(odd) {
animation: bar-animation-transform-2 3.2s normal infinite;
}
div:nth-child(even) {
animation: bar-animation-transform-1 3.2s normal infinite;
}
}
}

View File

@ -0,0 +1,26 @@
import React, { FC, memo } from '../../../lib/teact/teact';
import buildClassName from '../../../util/buildClassName';
import './ChatCallStatus.scss';
type OwnProps = {
isSelected?: boolean;
isActive?: boolean;
};
const ChatCallStatus: FC<OwnProps> = ({
isSelected,
isActive,
}) => {
return (
<div className={buildClassName('ChatCallStatus', isActive && 'active', isSelected && 'selected')}>
<div className="indicator">
<div />
<div />
<div />
</div>
</div>
);
};
export default memo(ChatCallStatus);

View File

@ -285,7 +285,7 @@ namespace Api {
export type TypeAccessPointRule = AccessPointRule;
export type TypeTlsClientHello = TlsClientHello;
export type TypeTlsBlock = TlsBlockString | TlsBlockRandom | TlsBlockZero | TlsBlockDomain | TlsBlockGrease | TlsBlockScope;
export namespace storage {
export type TypeFileType = storage.FileUnknown | storage.FilePartial | storage.FileJpeg | storage.FileGif | storage.FilePng | storage.FilePdf | storage.FileMp3 | storage.FileMov | storage.FileMp4 | storage.FileWebp;
@ -7001,7 +7001,7 @@ namespace Api {
}> {
entries: Api.TypeTlsBlock[];
};
export namespace storage {
export class FileUnknown extends VirtualClass<void> {};
@ -8500,7 +8500,7 @@ namespace Api {
}>, Api.TypeDestroySessionRes> {
sessionId: long;
};
export namespace auth {
export class SendCode extends Request<Partial<{