Update API scheme to layer 125.

This commit is contained in:
John Preston 2021-03-03 19:29:33 +04:00
parent 2734cab3f2
commit 02517f7221
16 changed files with 331 additions and 231 deletions

View file

@ -92,6 +92,7 @@ inputPhotoFileLocation#40181ffe id:long access_hash:long file_reference:bytes th
inputPhotoLegacyFileLocation#d83466f3 id:long access_hash:long file_reference:bytes volume_id:long local_id:int secret:long = InputFileLocation;
inputPeerPhotoFileLocation#27d69997 flags:# big:flags.0?true peer:InputPeer volume_id:long local_id:int = InputFileLocation;
inputStickerSetThumb#dbaeae9 stickerset:InputStickerSet volume_id:long local_id:int = InputFileLocation;
inputGroupCallStream#d1cc2a5f call:InputGroupCall date:int = InputFileLocation;
peerUser#9db1bc6d user_id:int = Peer;
peerChat#bad0e5bb chat_id:int = Peer;
@ -127,8 +128,8 @@ chatForbidden#7328bdb id:int title:string = Chat;
channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#f06c4018 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int = ChatFull;
channelFull#2548c037 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> = ChatFull;
chatFull#8a1e2983 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer = ChatFull;
channelFull#548c3f93 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer = ChatFull;
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
@ -370,9 +371,9 @@ updateChat#1330a196 chat_id:int = Update;
updateGroupCallParticipants#f2ebdb4e call:InputGroupCall participants:Vector<GroupCallParticipant> version:int = Update;
updateGroupCall#a45eb99b chat_id:int call:GroupCall = Update;
updatePeerHistoryTTL#bb9bb9a5 flags:# peer:Peer ttl_period:flags.0?int = Update;
updateChatParticipant#609a6ed4 flags:# chat_id:int date:int user_id:int prev_participant:flags.0?ChatParticipant new_participant:flags.1?ChatParticipant qts:int = Update;
updateChannelParticipant#65d2b464 flags:# channel_id:int date:int user_id:int prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant qts:int = Update;
updateBotStopped#30ec6ebc user_id:int stopped:Bool qts:int = Update;
updateChatParticipant#f3b3781f flags:# chat_id:int date:int actor_id:int user_id:int prev_participant:flags.0?ChatParticipant new_participant:flags.1?ChatParticipant invite:flags.2?ExportedChatInvite qts:int = Update;
updateChannelParticipant#7fecb1ec flags:# channel_id:int date:int actor_id:int user_id:int prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant invite:flags.2?ExportedChatInvite qts:int = Update;
updateBotStopped#7f9488a user_id:int date:int stopped:Bool qts:int = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -1202,15 +1203,15 @@ peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked;
stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats;
groupCallDiscarded#7780bcb4 id:long access_hash:long duration:int = GroupCall;
groupCall#55903081 flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true id:long access_hash:long participants_count:int params:flags.0?DataJSON version:int = GroupCall;
groupCall#c0c2052e flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true id:long access_hash:long participants_count:int params:flags.0?DataJSON title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int version:int = GroupCall;
inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall;
groupCallParticipant#64c62a15 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int = GroupCallParticipant;
groupCallParticipant#d27d3adf flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true self:flags.12?true peer:Peer date:int active_date:flags.3?int source:int volume:flags.7?int about:flags.11?string = GroupCallParticipant;
phone.groupCall#66ab0bfc call:GroupCall participants:Vector<GroupCallParticipant> participants_next_offset:string users:Vector<User> = phone.GroupCall;
phone.groupCall#9e727aad call:GroupCall participants:Vector<GroupCallParticipant> participants_next_offset:string chats:Vector<Chat> users:Vector<User> = phone.GroupCall;
phone.groupParticipants#9cfeb92d count:int participants:Vector<GroupCallParticipant> next_offset:string users:Vector<User> version:int = phone.GroupParticipants;
phone.groupParticipants#f47751b6 count:int participants:Vector<GroupCallParticipant> next_offset:string chats:Vector<Chat> users:Vector<User> version:int = phone.GroupParticipants;
inlineQueryPeerTypeSameBotPM#3081ed9d = InlineQueryPeerType;
inlineQueryPeerTypePM#833c0fac = InlineQueryPeerType;
@ -1237,6 +1238,8 @@ chatAdminWithInvites#dfd2330f admin_id:int invites_count:int revoked_invites_cou
messages.chatAdminsWithInvites#b69b72d7 admins:Vector<ChatAdminWithInvites> users:Vector<User> = messages.ChatAdminsWithInvites;
messages.checkedHistoryImportPeer#a24de717 confirm_text:string = messages.CheckedHistoryImportPeer;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1469,8 +1472,8 @@ messages.getEmojiKeywordsDifference#1508b6af lang_code:string from_version:int =
messages.getEmojiKeywordsLanguages#4e9963b2 lang_codes:Vector<string> = Vector<EmojiLanguage>;
messages.getEmojiURL#d5b10c26 lang_code:string = EmojiURL;
messages.getSearchCounters#732eef00 peer:InputPeer filters:Vector<MessagesFilter> = Vector<messages.SearchCounter>;
messages.requestUrlAuth#e33f5613 peer:InputPeer msg_id:int button_id:int = UrlAuthResult;
messages.acceptUrlAuth#f729ea98 flags:# write_allowed:flags.0?true peer:InputPeer msg_id:int button_id:int = UrlAuthResult;
messages.requestUrlAuth#198fb446 flags:# peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string = UrlAuthResult;
messages.acceptUrlAuth#b12c7125 flags:# write_allowed:flags.0?true peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string = UrlAuthResult;
messages.hidePeerSettingsBar#4facb138 peer:InputPeer = Bool;
messages.getScheduledHistory#e2c2685b peer:InputPeer hash:int = messages.Messages;
messages.getScheduledMessages#bdbb0464 peer:InputPeer id:Vector<int> = messages.Messages;
@ -1494,12 +1497,14 @@ messages.initHistoryImport#34090c3b peer:InputPeer file:InputFile media_count:in
messages.uploadImportedMedia#2a862092 peer:InputPeer import_id:long file_name:string media:InputMedia = MessageMedia;
messages.startHistoryImport#b43df344 peer:InputPeer import_id:long = Bool;
messages.getExportedChatInvites#a2b5a3f6 flags:# revoked:flags.3?true peer:InputPeer admin_id:InputUser offset_date:flags.2?int offset_link:flags.2?string limit:int = messages.ExportedChatInvites;
messages.getExportedChatInvite#73746f5c peer:InputPeer link:string = messages.ExportedChatInvite;
messages.editExportedChatInvite#2e4ffbe flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int = messages.ExportedChatInvite;
messages.deleteRevokedExportedChatInvites#56987bd5 peer:InputPeer admin_id:InputUser = Bool;
messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool;
messages.getAdminsWithInvites#3920e6ef peer:InputPeer = messages.ChatAdminsWithInvites;
messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters;
messages.setHistoryTTL#b80e5fe4 peer:InputPeer period:int = Updates;
messages.checkHistoryImportPeer#5dc60f03 peer:InputPeer = messages.CheckedHistoryImportPeer;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@ -1563,7 +1568,7 @@ channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> =
channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
channels.exportMessageLink#e63fadeb flags:# grouped:flags.0?true thread:flags.1?true channel:InputChannel id:int = ExportedMessageLink;
channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
channels.getAdminedPublicChannels#f8b036af flags:# by_location:flags.0?true check_limit:flags.1?true = messages.Chats;
channels.getAdminedPublicChannels#f8b036af flags:# by_location:flags.0?true check_limit:flags.1?true for_groupcall:flags.2?true = messages.Chats;
channels.editBanned#72796912 channel:InputChannel user_id:InputUser banned_rights:ChatBannedRights = Updates;
channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<InputUser> max_id:long min_id:long limit:int = channels.AdminLogResults;
channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool;
@ -1606,16 +1611,18 @@ phone.discardCall#b2cbc1c0 flags:# video:flags.0?true peer:InputPhoneCall durati
phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates;
phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;
phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool;
phone.createGroupCall#bd3dabe0 peer:InputPeer random_id:int = Updates;
phone.joinGroupCall#5f9c8e62 flags:# muted:flags.0?true call:InputGroupCall params:DataJSON = Updates;
phone.createGroupCall#1fd59252 flags:# peer:InputPeer join_as:flags.0?InputPeer random_id:int = Updates;
phone.joinGroupCall#2e8166b8 flags:# muted:flags.0?true call:InputGroupCall join_as:flags.1?InputPeer params:DataJSON = Updates;
phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates;
phone.editGroupCallMember#a5e76cd8 flags:# muted:flags.0?true call:InputGroupCall user_id:InputUser volume:flags.1?int = Updates;
phone.inviteToGroupCall#7b393160 call:InputGroupCall users:Vector<InputUser> = Updates;
phone.discardGroupCall#7a777135 call:InputGroupCall = Updates;
phone.toggleGroupCallSettings#74bbb43d flags:# call:InputGroupCall join_muted:flags.0?Bool = Updates;
phone.getGroupCall#c7cb017 call:InputGroupCall = phone.GroupCall;
phone.getGroupParticipants#c9f1d285 call:InputGroupCall ids:Vector<int> sources:Vector<int> offset:string limit:int = phone.GroupParticipants;
phone.checkGroupCall#b74a7bea call:InputGroupCall source:int = Bool;
phone.toggleGroupCallRecord#c02a66d7 flags:# start:flags.0?true call:InputGroupCall title:flags.1?string = Updates;
phone.editGroupCallParticipant#4713e7a3 flags:# muted:flags.0?true call:InputGroupCall participant:InputPeer volume:flags.1?int = Updates;
phone.editGroupCallTitle#1ca6ac0a call:InputGroupCall title:string = Updates;
langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference;
langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector<string> = Vector<LangPackString>;
@ -1632,4 +1639,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
// LAYER 124
// LAYER 125

View file

@ -41,10 +41,13 @@ void UrlAuthBox::Activate(
const auto buttonId = button->buttonId;
const auto url = QString::fromUtf8(button->data);
using Flag = MTPmessages_RequestUrlAuth::Flag;
button->requestId = session->api().request(MTPmessages_RequestUrlAuth(
MTP_flags(Flag::f_peer | Flag::f_msg_id | Flag::f_button_id),
inputPeer,
MTP_int(itemId.msg),
MTP_int(buttonId)
MTP_int(buttonId),
MTPstring() // #TODO auth url
)).done([=](const MTPUrlAuthResult &result) {
const auto button = HistoryMessageMarkupButton::Get(
&session->data(),
@ -111,11 +114,14 @@ void UrlAuthBox::Request(
} else if (const auto msg = session->data().message(itemId)) {
const auto allowWrite = (result == Result::AuthAndAllowWrite);
using Flag = MTPmessages_AcceptUrlAuth::Flag;
const auto flags = (allowWrite ? Flag::f_write_allowed : Flag(0))
| (Flag::f_peer | Flag::f_msg_id | Flag::f_button_id);
session->api().request(MTPmessages_AcceptUrlAuth(
MTP_flags(allowWrite ? Flag::f_write_allowed : Flag(0)),
MTP_flags(flags),
inputPeer,
MTP_int(itemId.msg),
MTP_int(buttonId)
MTP_int(buttonId),
MTPstring() // #TODO auth url
)).done([=](const MTPUrlAuthResult &result) {
const auto to = result.match(
[&](const MTPDurlAuthResultAccepted &data) {

View file

@ -57,18 +57,18 @@ constexpr auto kPlayConnectingEach = crl::time(1056) + 2 * crl::time(1000);
}
[[nodiscard]] const Data::GroupCall::Participant *LookupParticipant(
not_null<PeerData*> chat,
not_null<PeerData*> peer,
uint64 id,
not_null<UserData*> user) {
const auto call = chat->groupCall();
not_null<PeerData*> participantPeer) {
const auto call = peer->groupCall();
if (!id || !call || call->id() != id) {
return nullptr;
}
const auto &participants = call->participants();
const auto i = ranges::find(
participants,
user,
&Data::GroupCall::Participant::user);
participantPeer,
&Data::GroupCall::Participant::peer);
return (i != end(participants)) ? &*i : nullptr;
}
@ -76,7 +76,11 @@ constexpr auto kPlayConnectingEach = crl::time(1056) + 2 * crl::time(1000);
[[nodiscard]] bool IsGroupCallAdmin(
not_null<PeerData*> peer,
not_null<UserData*> user) {
not_null<PeerData*> participantPeer) {
const auto user = participantPeer->asUser();
if (!user) {
return false; // #TODO calls
}
if (const auto chat = peer->asChat()) {
return chat->admins.contains(user)
|| (chat->creator == user->bareId());
@ -240,7 +244,9 @@ void GroupCall::playConnectingSoundOnce() {
void GroupCall::start() {
_createRequestId = _api.request(MTPphone_CreateGroupCall(
MTP_flags(0),
_peer->input,
MTPInputPeer(), // #TODO calls join_as
MTP_int(openssl::RandomValue<int32>())
)).done([=](const MTPUpdates &result) {
_acceptFields = true;
@ -343,11 +349,13 @@ void GroupCall::rejoin() {
const auto json = QJsonDocument(root).toJson(
QJsonDocument::Compact);
const auto wasMuteState = muted();
using Flag = MTPphone_JoinGroupCall::Flag;
_api.request(MTPphone_JoinGroupCall(
MTP_flags((wasMuteState != MuteState::Active)
? MTPphone_JoinGroupCall::Flag::f_muted
: MTPphone_JoinGroupCall::Flag(0)),
? Flag::f_muted
: Flag(0)),
inputCall(),
MTPInputPeer(), // #TODO calls join_as
MTP_dataJSON(MTP_bytes(json))
)).done([=](const MTPUpdates &updates) {
_mySsrc = ssrc;
@ -392,7 +400,7 @@ void GroupCall::applySelfInCallLocally() {
const auto i = ranges::find(
participants,
self,
&Data::GroupCall::Participant::user);
&Data::GroupCall::Participant::peer);
const auto date = (i != end(participants))
? i->date
: base::unixtime::now();
@ -406,6 +414,7 @@ void GroupCall::applySelfInCallLocally() {
const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0))
| (lastActive ? Flag::f_active_date : Flag(0))
| (_mySsrc ? Flag(0) : Flag::f_left)
| Flag::f_self
| Flag::f_volume // Without flag the volume is reset to 100%.
| Flag::f_volume_by_admin // Self volume can only be set by admin.
| ((muted() != MuteState::Active) ? Flag::f_muted : Flag(0));
@ -416,19 +425,20 @@ void GroupCall::applySelfInCallLocally() {
1,
MTP_groupCallParticipant(
MTP_flags(flags),
MTP_int(self->bareId()),
peerToMTP(self->id), // #TODO calls channel or self
MTP_int(date),
MTP_int(lastActive),
MTP_int(_mySsrc),
MTP_int(volume))),
MTP_int(volume),
MTPstring())), // #TODO calls about
MTP_int(0)).c_updateGroupCallParticipants());
}
void GroupCall::applyParticipantLocally(
not_null<UserData*> user,
not_null<PeerData*> participantPeer,
bool mute,
std::optional<int> volume) {
const auto participant = LookupParticipant(_peer, _id, user);
const auto participant = LookupParticipant(_peer, _id, participantPeer);
if (!participant || !participant->ssrc) {
return;
}
@ -436,7 +446,7 @@ void GroupCall::applyParticipantLocally(
const auto isMuted = participant->muted || (mute && canManageCall);
const auto canSelfUnmute = !canManageCall
? participant->canSelfUnmute
: (!mute || IsGroupCallAdmin(_peer, user));
: (!mute || IsGroupCallAdmin(_peer, participantPeer));
const auto isMutedByYou = mute && !canManageCall;
const auto mutedCount = 0/*participant->mutedCount*/;
using Flag = MTPDgroupCallParticipant::Flag;
@ -447,7 +457,7 @@ void GroupCall::applyParticipantLocally(
: Flag(0))
| (participant->lastActive ? Flag::f_active_date : Flag(0))
| (isMuted ? Flag::f_muted : Flag(0))
| (isMutedByYou ? Flag::f_muted_by_you : Flag(0));
| (isMutedByYou ? Flag::f_muted_by_you : Flag(0)); // #TODO calls self?
_peer->groupCall()->applyUpdateChecked(
MTP_updateGroupCallParticipants(
inputCall(),
@ -455,11 +465,12 @@ void GroupCall::applyParticipantLocally(
1,
MTP_groupCallParticipant(
MTP_flags(flags),
MTP_int(user->bareId()),
peerToMTP(participantPeer->id),
MTP_int(participant->date),
MTP_int(participant->lastActive),
MTP_int(participant->ssrc),
MTP_int(volume.value_or(participant->volume)))),
MTP_int(volume.value_or(participant->volume)),
MTPstring())), // #TODO calls about
MTP_int(0)).c_updateGroupCallParticipants());
}
@ -631,22 +642,25 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
// No real information about mutedByMe or my custom volume.
return;
}
const auto user = _peer->owner().user(data.vuser_id().v);
const auto participant = LookupParticipant(_peer, _id, user);
const auto participantPeer = _peer->owner().peer(
peerFromMTP(data.vpeer()));
const auto participant = LookupParticipant(
_peer,
_id,
participantPeer);
if (!participant) {
return;
}
_otherParticipantStateValue.fire(Group::ParticipantState{
.user = user,
.peer = participantPeer,
.volume = data.vvolume().value_or_empty(),
.mutedByMe = data.is_muted_by_you(),
});
};
const auto self = _peer->session().userId();
for (const auto &participant : data.vparticipants().v) {
participant.match([&](const MTPDgroupCallParticipant &data) {
if (data.vuser_id().v != self) {
if (!data.is_self()) {
handleOtherParticipants(data);
return;
}
@ -891,12 +905,11 @@ void GroupCall::maybeSendMutedUpdate(MuteState previous) {
void GroupCall::sendMutedUpdate() {
_api.request(_updateMuteRequestId).cancel();
_updateMuteRequestId = _api.request(MTPphone_EditGroupCallMember(
MTP_flags((muted() != MuteState::Active)
? MTPphone_EditGroupCallMember::Flag::f_muted
: MTPphone_EditGroupCallMember::Flag(0)),
using Flag = MTPphone_EditGroupCallParticipant::Flag;
_updateMuteRequestId = _api.request(MTPphone_EditGroupCallParticipant(
MTP_flags((muted() != MuteState::Active) ? Flag::f_muted : Flag(0)),
inputCall(),
MTP_inputUserSelf(),
MTP_inputPeerSelf(),
MTP_int(100000) // volume
)).done([=](const MTPUpdates &result) {
_updateMuteRequestId = 0;
@ -930,37 +943,37 @@ void GroupCall::setCurrentAudioDevice(bool input, const QString &deviceId) {
void GroupCall::toggleMute(const Group::MuteRequest &data) {
if (data.locallyOnly) {
applyParticipantLocally(data.user, data.mute, std::nullopt);
applyParticipantLocally(data.peer, data.mute, std::nullopt);
} else {
editParticipant(data.user, data.mute, std::nullopt);
editParticipant(data.peer, data.mute, std::nullopt);
}
}
void GroupCall::changeVolume(const Group::VolumeRequest &data) {
if (data.locallyOnly) {
applyParticipantLocally(data.user, false, data.volume);
applyParticipantLocally(data.peer, false, data.volume);
} else {
editParticipant(data.user, false, data.volume);
editParticipant(data.peer, false, data.volume);
}
}
void GroupCall::editParticipant(
not_null<UserData*> user,
not_null<PeerData*> participantPeer,
bool mute,
std::optional<int> volume) {
const auto participant = LookupParticipant(_peer, _id, user);
const auto participant = LookupParticipant(_peer, _id, participantPeer);
if (!participant) {
return;
}
applyParticipantLocally(user, mute, volume);
applyParticipantLocally(participantPeer, mute, volume);
using Flag = MTPphone_EditGroupCallMember::Flag;
using Flag = MTPphone_EditGroupCallParticipant::Flag;
const auto flags = (mute ? Flag::f_muted : Flag(0))
| (volume.has_value() ? Flag::f_volume : Flag(0));
_api.request(MTPphone_EditGroupCallMember(
_api.request(MTPphone_EditGroupCallParticipant(
MTP_flags(flags),
inputCall(),
user->inputUser,
participantPeer->input,
MTP_int(std::clamp(volume.value_or(0), 1, Group::kMaxVolume))
)).done([=](const MTPUpdates &result) {
_peer->session().api().applyUpdates(result);
@ -987,7 +1000,7 @@ std::variant<int, not_null<UserData*>> GroupCall::inviteUsers(
return !invited.contains(user) && !ranges::contains(
participants,
user,
&Data::GroupCall::Participant::user);
&Data::GroupCall::Participant::peer);
});
auto count = 0;

View file

@ -56,7 +56,7 @@ enum class MuteState {
[[nodiscard]] bool IsGroupCallAdmin(
not_null<PeerData*> peer,
not_null<UserData*> user);
not_null<PeerData*> participantPeer);
struct LevelUpdate {
uint32 ssrc = 0;
@ -194,11 +194,11 @@ private:
void playConnectingSoundOnce();
void editParticipant(
not_null<UserData*> user,
not_null<PeerData*> participantPeer,
bool mute,
std::optional<int> volume);
void applyParticipantLocally(
not_null<UserData*> user,
not_null<PeerData*> participantPeer,
bool mute,
std::optional<int> volume);

View file

@ -15,20 +15,20 @@ constexpr auto kDefaultVolume = 10000;
constexpr auto kMaxVolume = 20000;
struct MuteRequest {
not_null<UserData*> user;
not_null<PeerData*> peer;
bool mute = false;
bool locallyOnly = false;
};
struct VolumeRequest {
not_null<UserData*> user;
not_null<PeerData*> peer;
int volume = kDefaultVolume;
bool finalized = true;
bool locallyOnly = false;
};
struct ParticipantState {
not_null<UserData*> user;
not_null<PeerData*> peer;
std::optional<int> volume;
bool mutedByMe = false;
bool locallyOnly = false;

View file

@ -91,7 +91,9 @@ public:
class Row final : public PeerListRow {
public:
Row(not_null<RowDelegate*> delegate, not_null<UserData*> user);
Row(
not_null<RowDelegate*> delegate,
not_null<PeerData*> participantPeer);
enum class State {
Active,
@ -266,8 +268,8 @@ public:
}
[[nodiscard]] rpl::producer<MuteRequest> toggleMuteRequests() const;
[[nodiscard]] rpl::producer<VolumeRequest> changeVolumeRequests() const;
[[nodiscard]] auto kickMemberRequests() const
-> rpl::producer<not_null<UserData*>>;
[[nodiscard]] auto kickParticipantRequests() const
-> rpl::producer<not_null<PeerData*>>;
bool rowCanMuteMembers() override;
void rowUpdateRow(not_null<Row*> row) override;
@ -284,7 +286,7 @@ private:
[[nodiscard]] std::unique_ptr<Row> createRow(
const Data::GroupCall::Participant &participant);
[[nodiscard]] std::unique_ptr<Row> createInvitedRow(
not_null<UserData*> user);
not_null<PeerData*> participantPeer);
void prepareRows(not_null<Data::GroupCall*> real);
//void repaintByTimer();
@ -294,8 +296,8 @@ private:
not_null<PeerListRow*> row);
void addMuteActionsToContextMenu(
not_null<Ui::PopupMenu*> menu,
not_null<UserData*> user,
bool userIsCallAdmin,
not_null<PeerData*> participantPeer,
bool participantIsCallAdmin,
not_null<Row*> row);
void setupListChangeViewers(not_null<GroupCall*> call);
void subscribeToChanges(not_null<Data::GroupCall*> real);
@ -308,7 +310,7 @@ private:
void removeRow(not_null<Row*> row);
void updateRowLevel(not_null<Row*> row, float level);
void checkSpeakingRowPosition(not_null<Row*> row);
Row *findRow(not_null<UserData*> user) const;
Row *findRow(not_null<PeerData*> participantPeer) const;
[[nodiscard]] Data::GroupCall *resolvedRealCall() const;
void appendInvitedUsers();
@ -323,7 +325,7 @@ private:
rpl::event_stream<MuteRequest> _toggleMuteRequests;
rpl::event_stream<VolumeRequest> _changeVolumeRequests;
rpl::event_stream<not_null<UserData*>> _kickMemberRequests;
rpl::event_stream<not_null<PeerData*>> _kickParticipantRequests;
rpl::variable<int> _fullCount = 1;
rpl::variable<int> _fullCountMin = 0;
rpl::variable<int> _fullCountMax = std::numeric_limits<int>::max();
@ -345,8 +347,10 @@ private:
};
Row::Row(not_null<RowDelegate*> delegate, not_null<UserData*> user)
: PeerListRow(user)
Row::Row(
not_null<RowDelegate*> delegate,
not_null<PeerData*> participantPeer)
: PeerListRow(participantPeer)
, _delegate(delegate) {
refreshStatus();
}
@ -872,11 +876,13 @@ void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
) | rpl::start_with_next([=](const Update &update) {
Expects(update.was.has_value() || update.now.has_value());
const auto user = update.was ? update.was->user : update.now->user;
const auto participantPeer = update.was
? update.was->peer
: update.now->peer;
if (!update.now) {
if (const auto row = findRow(user)) {
const auto owner = &user->owner();
if (user->isSelf()) {
if (const auto row = findRow(participantPeer)) {
const auto owner = &participantPeer->owner();
if (participantPeer->isSelf()) {
updateRow(row, nullptr);
} else {
removeRow(row);
@ -918,7 +924,7 @@ void MembersController::updateRow(
const Data::GroupCall::Participant &now) {
auto reorderIfInvitedBeforeIndex = 0;
auto countChange = 0;
if (const auto row = findRow(now.user)) {
if (const auto row = findRow(now.peer)) {
if (now.speaking && (!was || !was->speaking)) {
checkSpeakingRowPosition(row);
}
@ -1047,8 +1053,9 @@ void MembersController::updateRowLevel(
row->updateLevel(level);
}
Row *MembersController::findRow(not_null<UserData*> user) const {
return static_cast<Row*>(delegate()->peerListFindRow(user->id));
Row *MembersController::findRow(not_null<PeerData*> participantPeer) const {
return static_cast<Row*>(
delegate()->peerListFindRow(participantPeer->id));
}
Data::GroupCall *MembersController::resolvedRealCall() const {
@ -1094,16 +1101,16 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
auto count = delegate()->peerListFullRowsCount();
for (auto i = 0; i != count;) {
auto row = delegate()->peerListRowAt(i);
auto user = row->peer()->asUser();
if (user->isSelf()) {
auto participantPeer = row->peer();
if (participantPeer->isSelf()) { // #TODO calls add self even if channel
foundSelf = true;
++i;
continue;
}
const auto contains = ranges::contains(
participants,
not_null{ user },
&Data::GroupCall::Participant::user);
participantPeer,
&Data::GroupCall::Participant::peer);
if (contains) {
++fullCountMin;
++i;
@ -1117,8 +1124,8 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
const auto self = _peer->session().user();
const auto i = ranges::find(
participants,
_peer->session().user(),
&Data::GroupCall::Participant::user);
self,
&Data::GroupCall::Participant::peer);
auto row = (i != end(participants)) ? createRow(*i) : createSelfRow();
if (row) {
if (row->state() != Row::State::Invited) {
@ -1223,9 +1230,9 @@ void MembersController::rowPaintIcon(
_inactiveCrossLine.paint(p, left, top, crossProgress, iconColor);
}
auto MembersController::kickMemberRequests() const
-> rpl::producer<not_null<UserData*>>{
return _kickMemberRequests.events();
auto MembersController::kickParticipantRequests() const
-> rpl::producer<not_null<PeerData*>>{
return _kickParticipantRequests.events();
}
void MembersController::rowClicked(not_null<PeerListRow*> row) {
@ -1235,7 +1242,7 @@ void MembersController::rowClicked(not_null<PeerListRow*> row) {
}
auto saved = base::take(_menu);
for (const auto peer : base::take(_menuCheckRowsAfterHidden)) {
if (const auto row = findRow(peer->asUser())) {
if (const auto row = findRow(peer)) {
if (row->speaking()) {
checkSpeakingRowPosition(row);
}
@ -1270,21 +1277,19 @@ base::unique_qptr<Ui::PopupMenu> MembersController::rowContextMenu(
base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
QWidget *parent,
not_null<PeerListRow*> row) {
Expects(row->peer()->isUser());
const auto participantPeer = row->peer();
const auto real = static_cast<Row*>(row.get());
if (row->peer()->isSelf()
if (participantPeer->isSelf()
&& (!_peer->canManageGroupCall() || !real->ssrc())) {
return nullptr;
}
const auto user = row->peer()->asUser();
auto result = base::make_unique_q<Ui::PopupMenu>(
parent,
st::groupCallPopupMenu);
const auto muteState = real->state();
const auto admin = IsGroupCallAdmin(_peer, user);
const auto session = &user->session();
const auto admin = IsGroupCallAdmin(_peer, participantPeer);
const auto session = &_peer->session();
const auto getCurrentWindow = [=]() -> Window::SessionController* {
if (const auto window = Core::App().activeWindow()) {
if (const auto controller = window->sessionController()) {
@ -1319,25 +1324,25 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
};
const auto showProfile = [=] {
performOnMainWindow([=](not_null<Window::SessionController*> window) {
window->showPeerInfo(user);
window->showPeerInfo(participantPeer);
});
};
const auto showHistory = [=] {
performOnMainWindow([=](not_null<Window::SessionController*> window) {
window->showPeerHistory(
user,
participantPeer,
Window::SectionShow::Way::Forward);
});
};
const auto removeFromGroup = crl::guard(this, [=] {
_kickMemberRequests.fire_copy(user);
_kickParticipantRequests.fire_copy(participantPeer);
});
if (real->ssrc() != 0) {
addMuteActionsToContextMenu(result, user, admin, real);
addMuteActionsToContextMenu(result, participantPeer, admin, real);
}
if (!user->isSelf()) {
if (!participantPeer->isSelf()) { // #TODO calls correct check self
result->addAction(
tr::lng_context_view_profile(tr::now),
showProfile);
@ -1345,13 +1350,17 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
tr::lng_context_send_message(tr::now),
showHistory);
const auto canKick = [&] {
const auto user = participantPeer->asUser();
if (static_cast<Row*>(row.get())->state() == Row::State::Invited) {
return false;
} else if (const auto chat = _peer->asChat()) {
return chat->amCreator()
|| (chat->canBanMembers() && !chat->admins.contains(user));
|| (user
&& chat->canBanMembers()
&& !chat->admins.contains(user)); // #TODO calls can kick
} else if (const auto group = _peer->asMegagroup()) {
return group->canRestrictUser(user);
return group->amCreator()
|| (user && group->canRestrictUser(user)); // #TODO calls can kick
}
return false;
}();
@ -1366,8 +1375,8 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
void MembersController::addMuteActionsToContextMenu(
not_null<Ui::PopupMenu*> menu,
not_null<UserData*> user,
bool userIsCallAdmin,
not_null<PeerData*> participantPeer,
bool participantIsCallAdmin,
not_null<Row*> row) {
const auto muteString = [=] {
return (_peer->canManageGroupCall()
@ -1383,7 +1392,7 @@ void MembersController::addMuteActionsToContextMenu(
const auto toggleMute = crl::guard(this, [=](bool mute, bool local) {
_toggleMuteRequests.fire(Group::MuteRequest{
.user = user,
.peer = participantPeer,
.mute = mute,
.locallyOnly = local,
});
@ -1392,7 +1401,7 @@ void MembersController::addMuteActionsToContextMenu(
int volume,
bool local) {
_changeVolumeRequests.fire(Group::VolumeRequest{
.user = user,
.peer = participantPeer,
.volume = std::clamp(volume, 1, Group::kMaxVolume),
.locallyOnly = local,
});
@ -1404,12 +1413,12 @@ void MembersController::addMuteActionsToContextMenu(
auto mutesFromVolume = rpl::never<bool>() | rpl::type_erased();
if (!isMuted || user->isSelf()) {
if (!isMuted || participantPeer->isSelf()) {
const auto call = _call.get();
auto otherParticipantStateValue = call
? call->otherParticipantStateValue(
) | rpl::filter([=](const Group::ParticipantState &data) {
return data.user == user;
return data.peer == participantPeer;
})
: rpl::never<Group::ParticipantState>() | rpl::type_erased();
@ -1437,7 +1446,7 @@ void MembersController::addMuteActionsToContextMenu(
volumeItem->toggleMuteLocallyRequests(
) | rpl::start_with_next([=](bool muted) {
if (!user->isSelf()) {
if (!participantPeer->isSelf()) { // #TODO calls check self
toggleMute(muted, true);
}
}, volumeItem->lifetime());
@ -1449,7 +1458,7 @@ void MembersController::addMuteActionsToContextMenu(
volumeItem->changeVolumeLocallyRequests(
) | rpl::start_with_next([=](int volume) {
if (!user->isSelf()) {
if (!participantPeer->isSelf()) { // #TODO calls check self
changeVolume(volume, true);
}
}, volumeItem->lifetime());
@ -1459,9 +1468,9 @@ void MembersController::addMuteActionsToContextMenu(
const auto muteAction = [&]() -> QAction* {
if (muteState == Row::State::Invited
|| user->isSelf()
|| participantPeer->isSelf() // #TODO calls check self
|| (muteState == Row::State::Muted
&& userIsCallAdmin
&& participantIsCallAdmin
&& _peer->canManageGroupCall())) {
return nullptr;
}
@ -1486,7 +1495,7 @@ void MembersController::addMuteActionsToContextMenu(
}
std::unique_ptr<Row> MembersController::createSelfRow() {
const auto self = _peer->session().user();
const auto self = _peer->session().user(); // #TODO calls check self
auto result = std::make_unique<Row>(this, self);
updateRow(result.get(), nullptr);
return result;
@ -1494,17 +1503,17 @@ std::unique_ptr<Row> MembersController::createSelfRow() {
std::unique_ptr<Row> MembersController::createRow(
const Data::GroupCall::Participant &participant) {
auto result = std::make_unique<Row>(this, participant.user);
auto result = std::make_unique<Row>(this, participant.peer);
updateRow(result.get(), &participant);
return result;
}
std::unique_ptr<Row> MembersController::createInvitedRow(
not_null<UserData*> user) {
if (findRow(user)) {
not_null<PeerData*> participantPeer) {
if (findRow(participantPeer)) {
return nullptr;
}
auto result = std::make_unique<Row>(this, user);
auto result = std::make_unique<Row>(this, participantPeer);
updateRow(result.get(), nullptr);
return result;
}
@ -1538,10 +1547,10 @@ auto GroupMembers::changeVolumeRequests() const
_listController.get())->changeVolumeRequests();
}
auto GroupMembers::kickMemberRequests() const
-> rpl::producer<not_null<UserData*>> {
auto GroupMembers::kickParticipantRequests() const
-> rpl::producer<not_null<PeerData*>> {
return static_cast<MembersController*>(
_listController.get())->kickMemberRequests();
_listController.get())->kickParticipantRequests();
}
int GroupMembers::desiredHeight() const {

View file

@ -42,8 +42,8 @@ public:
-> rpl::producer<Group::MuteRequest>;
[[nodiscard]] auto changeVolumeRequests() const
-> rpl::producer<Group::VolumeRequest>;
[[nodiscard]] auto kickMemberRequests() const
-> rpl::producer<not_null<UserData*>>;
[[nodiscard]] auto kickParticipantRequests() const
-> rpl::producer<not_null<PeerData*>>;
[[nodiscard]] rpl::producer<> addMembersRequests() const {
return _addMemberRequests.events();
}

View file

@ -520,9 +520,11 @@ void GroupPanel::initWithCall(GroupCall *call) {
}
}, _callLifetime);
_members->kickMemberRequests(
) | rpl::start_with_next([=](not_null<UserData*> user) {
kickMember(user);
_members->kickParticipantRequests(
) | rpl::start_with_next([=](not_null<PeerData*> participantPeer) {
if (const auto user = participantPeer->asUser()) {
kickMember(user); // #TODO calls kick
}
}, _callLifetime);
_members->addMembersRequests(
@ -570,7 +572,9 @@ void GroupPanel::addMembers() {
}
auto alreadyIn = _peer->owner().invitedToCallUsers(real->id());
for (const auto &participant : real->participants()) {
alreadyIn.emplace(participant.user);
if (const auto user = participant.peer->asUser()) {
alreadyIn.emplace(user);
}
}
alreadyIn.emplace(_peer->session().user());
auto controller = std::make_unique<InviteController>(

View file

@ -39,7 +39,7 @@ GroupCall::GroupCall(
}
GroupCall::~GroupCall() {
api().request(_unknownUsersRequestId).cancel();
api().request(_unknownParticipantPeersRequestId).cancel();
api().request(_participantsRequestId).cancel();
api().request(_reloadRequestId).cancel();
}
@ -87,6 +87,7 @@ void GroupCall::requestParticipants() {
result.match([&](const MTPDphone_groupParticipants &data) {
_nextOffset = qs(data.vnext_offset());
_peer->owner().processUsers(data.vusers());
_peer->owner().processChats(data.vchats());
applyParticipantsSlice(
data.vparticipants().v,
ApplySliceSource::SliceLoaded);
@ -154,9 +155,9 @@ bool GroupCall::participantsLoaded() const {
return _allReceived;
}
UserData *GroupCall::userBySsrc(uint32 ssrc) const {
const auto i = _userBySsrc.find(ssrc);
return (i != end(_userBySsrc)) ? i->second.get() : nullptr;
PeerData *GroupCall::participantPeerBySsrc(uint32 ssrc) const {
const auto i = _participantPeerBySsrc.find(ssrc);
return (i != end(_participantPeerBySsrc)) ? i->second.get() : nullptr;
}
rpl::producer<> GroupCall::participantsSliceAdded() {
@ -216,9 +217,10 @@ void GroupCall::reload() {
).done([=](const MTPphone_GroupCall &result) {
result.match([&](const MTPDphone_groupCall &data) {
_peer->owner().processUsers(data.vusers());
_peer->owner().processChats(data.vchats());
_participants.clear();
_speakingByActiveFinishes.clear();
_userBySsrc.clear();
_participantPeerBySsrc.clear();
applyParticipantsSlice(
data.vparticipants().v,
ApplySliceSource::SliceLoaded);
@ -242,19 +244,20 @@ void GroupCall::applyParticipantsSlice(
auto changedCount = _fullCount.current();
for (const auto &participant : list) {
participant.match([&](const MTPDgroupCallParticipant &data) {
const auto userId = data.vuser_id().v;
const auto user = _peer->owner().user(userId);
const auto participantPeerId = peerFromMTP(data.vpeer());
const auto participantPeer = _peer->owner().peer(
participantPeerId);
const auto i = ranges::find(
_participants,
user,
&Participant::user);
participantPeer,
&Participant::peer);
if (data.is_left()) {
if (i != end(_participants)) {
auto update = ParticipantUpdate{
.was = *i,
};
_userBySsrc.erase(i->ssrc);
_speakingByActiveFinishes.remove(user);
_participantPeerBySsrc.erase(i->ssrc);
_speakingByActiveFinishes.remove(participantPeer);
_participants.erase(i);
if (sliceSource != ApplySliceSource::SliceLoaded) {
_participantUpdates.fire(std::move(update));
@ -290,7 +293,7 @@ void GroupCall::applyParticipantsSlice(
const auto onlyMinLoaded = data.is_min()
&& (!was || was->onlyMinLoaded);
const auto value = Participant{
.user = user,
.peer = participantPeer,
.date = data.vdate().v,
.lastActive = lastActive,
.ssrc = uint32(data.vsource().v),
@ -303,13 +306,17 @@ void GroupCall::applyParticipantsSlice(
.onlyMinLoaded = onlyMinLoaded,
};
if (i == end(_participants)) {
_userBySsrc.emplace(value.ssrc, user);
_participantPeerBySsrc.emplace(value.ssrc, participantPeer);
_participants.push_back(value);
_peer->owner().unregisterInvitedToCallUser(_id, user);
if (const auto user = participantPeer->asUser()) {
_peer->owner().unregisterInvitedToCallUser(_id, user);
}
} else {
if (i->ssrc != value.ssrc) {
_userBySsrc.erase(i->ssrc);
_userBySsrc.emplace(value.ssrc, user);
_participantPeerBySsrc.erase(i->ssrc);
_participantPeerBySsrc.emplace(
value.ssrc,
participantPeer);
}
*i = value;
}
@ -334,16 +341,19 @@ void GroupCall::applyLastSpoke(
uint32 ssrc,
LastSpokeTimes when,
crl::time now) {
const auto i = _userBySsrc.find(ssrc);
if (i == end(_userBySsrc)) {
const auto i = _participantPeerBySsrc.find(ssrc);
if (i == end(_participantPeerBySsrc)) {
_unknownSpokenSsrcs[ssrc] = when;
requestUnknownParticipants();
return;
}
const auto j = ranges::find(_participants, i->second, &Participant::user);
const auto j = ranges::find(
_participants,
i->second,
&Participant::peer);
Assert(j != end(_participants));
_speakingByActiveFinishes.remove(j->user);
_speakingByActiveFinishes.remove(j->peer);
const auto sounding = (when.anything + kSoundStatusKeptFor >= now)
&& j->canSelfUnmute;
const auto speaking = sounding
@ -360,22 +370,22 @@ void GroupCall::applyLastSpoke(
}
void GroupCall::applyActiveUpdate(
UserId userId,
PeerId participantPeerId,
LastSpokeTimes when,
UserData *userLoaded) {
PeerData *participantPeerLoaded) {
if (inCall()) {
return;
}
const auto i = userLoaded
const auto i = participantPeerLoaded
? ranges::find(
_participants,
not_null{ userLoaded },
&Participant::user)
not_null{ participantPeerLoaded },
&Participant::peer)
: _participants.end();
const auto notFound = (i == end(_participants));
const auto loadByUserId = notFound || i->onlyMinLoaded;
if (loadByUserId) {
_unknownSpokenUids[userId] = when;
_unknownSpokenPeerIds[participantPeerId] = when;
requestUnknownParticipants();
}
if (notFound || !i->canSelfUnmute) {
@ -389,7 +399,7 @@ void GroupCall::applyActiveUpdate(
if (lastActive <= i->lastActive || finishes <= now) {
return;
}
_speakingByActiveFinishes[i->user] = finishes;
_speakingByActiveFinishes[i->peer] = finishes;
if (!_speakingByActiveFinishTimer.isActive()) {
_speakingByActiveFinishTimer.callOnce(finishes - now);
}
@ -408,8 +418,9 @@ void GroupCall::applyActiveUpdate(
void GroupCall::checkFinishSpeakingByActive() {
const auto now = crl::now();
auto nearest = 0;
auto stop = std::vector<not_null<UserData*>>();
for (auto i = begin(_speakingByActiveFinishes); i != end(_speakingByActiveFinishes);) {
auto stop = std::vector<not_null<PeerData*>>();
for (auto i = begin(_speakingByActiveFinishes)
; i != end(_speakingByActiveFinishes);) {
const auto when = i->second;
if (now >= when) {
stop.push_back(i->first);
@ -421,8 +432,11 @@ void GroupCall::checkFinishSpeakingByActive() {
++i;
}
}
for (const auto user : stop) {
const auto i = ranges::find(_participants, user, &Participant::user);
for (const auto participantPeer : stop) {
const auto i = ranges::find(
_participants,
participantPeer,
&Participant::peer);
if (i->speaking) {
const auto was = *i;
i->speaking = false;
@ -438,8 +452,8 @@ void GroupCall::checkFinishSpeakingByActive() {
}
void GroupCall::requestUnknownParticipants() {
if (_unknownUsersRequestId
|| (_unknownSpokenSsrcs.empty() && _unknownSpokenUids.empty())) {
if (_unknownParticipantPeersRequestId
|| (_unknownSpokenSsrcs.empty() && _unknownSpokenPeerIds.empty())) {
return;
}
const auto ssrcs = [&] {
@ -455,18 +469,18 @@ void GroupCall::requestUnknownParticipants() {
}
return result;
}();
const auto uids = [&] {
if (_unknownSpokenUids.size() + ssrcs.size() < kRequestPerPage) {
return base::take(_unknownSpokenUids);
const auto peerIds = [&] {
if (_unknownSpokenPeerIds.size() + ssrcs.size() < kRequestPerPage) {
return base::take(_unknownSpokenPeerIds);
}
auto result = base::flat_map<UserId, LastSpokeTimes>();
auto result = base::flat_map<PeerId, LastSpokeTimes>();
const auto available = (kRequestPerPage - int(ssrcs.size()));
if (available > 0) {
result.reserve(available);
while (result.size() < available) {
const auto [userId, when] = _unknownSpokenUids.back();
const auto [userId, when] = _unknownSpokenPeerIds.back();
result.emplace(userId, when);
_unknownSpokenUids.erase(_unknownSpokenUids.end() - 1);
_unknownSpokenPeerIds.erase(_unknownSpokenPeerIds.end() - 1);
}
}
return result;
@ -477,62 +491,65 @@ void GroupCall::requestUnknownParticipants() {
ssrcInputs.push_back(MTP_int(ssrc));
}
auto uidInputs = QVector<MTPint>();
uidInputs.reserve(uids.size());
for (const auto [userId, when] : uids) {
uidInputs.push_back(MTP_int(userId));
uidInputs.reserve(peerIds.size());
for (const auto [peerId, when] : peerIds) {
Assert(peerIsUser(peerId)); // #TODO calls
uidInputs.push_back(MTP_int(peerToUser(peerId)));
}
_unknownUsersRequestId = api().request(MTPphone_GetGroupParticipants(
input(),
MTP_vector<MTPint>(uidInputs),
MTP_vector<MTPint>(ssrcInputs),
MTP_string(QString()),
MTP_int(kRequestPerPage)
)).done([=](const MTPphone_GroupParticipants &result) {
_unknownParticipantPeersRequestId = api().request(
MTPphone_GetGroupParticipants(
input(),
MTP_vector<MTPint>(uidInputs),
MTP_vector<MTPint>(ssrcInputs),
MTP_string(QString()),
MTP_int(kRequestPerPage)
)
).done([=](const MTPphone_GroupParticipants &result) {
result.match([&](const MTPDphone_groupParticipants &data) {
_peer->owner().processUsers(data.vusers());
applyParticipantsSlice(
data.vparticipants().v,
ApplySliceSource::UnknownLoaded);
});
_unknownUsersRequestId = 0;
_unknownParticipantPeersRequestId = 0;
const auto now = crl::now();
for (const auto [ssrc, when] : ssrcs) {
applyLastSpoke(ssrc, when, now);
_unknownSpokenSsrcs.remove(ssrc);
}
for (const auto [userId, when] : uids) {
if (const auto user = _peer->owner().userLoaded(userId)) {
for (const auto [peerId, when] : peerIds) {
if (const auto participantPeer = _peer->owner().peerLoaded(peerId)) {
const auto isParticipant = ranges::contains(
_participants,
not_null{ user },
&Participant::user);
not_null{ participantPeer },
&Participant::peer);
if (isParticipant) {
applyActiveUpdate(userId, when, user);
applyActiveUpdate(peerId, when, participantPeer);
}
}
_unknownSpokenUids.remove(userId);
_unknownSpokenPeerIds.remove(peerId);
}
requestUnknownParticipants();
}).fail([=](const RPCError &error) {
_unknownUsersRequestId = 0;
_unknownParticipantPeersRequestId = 0;
for (const auto [ssrc, when] : ssrcs) {
_unknownSpokenSsrcs.remove(ssrc);
}
for (const auto [userId, when] : uids) {
_unknownSpokenUids.remove(userId);
for (const auto [peerId, when] : peerIds) {
_unknownSpokenPeerIds.remove(peerId);
}
requestUnknownParticipants();
}).send();
}
void GroupCall::setInCall() {
_unknownSpokenUids.clear();
_unknownSpokenPeerIds.clear();
if (_speakingByActiveFinishes.empty()) {
return;
}
auto restartTimer = true;
const auto latest = crl::now() + kActiveAfterJoined;
for (auto &[user, when] : _speakingByActiveFinishes) {
for (auto &[peer, when] : _speakingByActiveFinishes) {
if (when > latest) {
when = latest;
} else {

View file

@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer.h"
class UserData;
class PeerData;
class ApiWrap;
@ -33,7 +32,7 @@ public:
void setPeer(not_null<PeerData*> peer);
struct Participant {
not_null<UserData*> user;
not_null<PeerData*> peer;
TimeId date = 0;
TimeId lastActive = 0;
uint32 ssrc = 0;
@ -57,7 +56,7 @@ public:
-> const std::vector<Participant> &;
void requestParticipants();
[[nodiscard]] bool participantsLoaded() const;
[[nodiscard]] UserData *userBySsrc(uint32 ssrc) const;
[[nodiscard]] PeerData *participantPeerBySsrc(uint32 ssrc) const;
[[nodiscard]] rpl::producer<> participantsSliceAdded();
[[nodiscard]] rpl::producer<ParticipantUpdate> participantUpdated() const;
@ -68,9 +67,9 @@ public:
const MTPDupdateGroupCallParticipants &update);
void applyLastSpoke(uint32 ssrc, LastSpokeTimes when, crl::time now);
void applyActiveUpdate(
UserId userId,
PeerId participantPeerId,
LastSpokeTimes when,
UserData *userLoaded);
PeerData *participantPeerLoaded);
[[nodiscard]] int fullCount() const;
[[nodiscard]] rpl::producer<int> fullCountValue() const;
@ -108,15 +107,15 @@ private:
mtpRequestId _reloadRequestId = 0;
std::vector<Participant> _participants;
base::flat_map<uint32, not_null<UserData*>> _userBySsrc;
base::flat_map<not_null<UserData*>, crl::time> _speakingByActiveFinishes;
base::flat_map<uint32, not_null<PeerData*>> _participantPeerBySsrc;
base::flat_map<not_null<PeerData*>, crl::time> _speakingByActiveFinishes;
base::Timer _speakingByActiveFinishTimer;
QString _nextOffset;
rpl::variable<int> _fullCount = 0;
base::flat_map<uint32, LastSpokeTimes> _unknownSpokenSsrcs;
base::flat_map<UserId, LastSpokeTimes> _unknownSpokenUids;
mtpRequestId _unknownUsersRequestId = 0;
base::flat_map<PeerId, LastSpokeTimes> _unknownSpokenPeerIds;
mtpRequestId _unknownParticipantPeersRequestId = 0;
rpl::event_stream<ParticipantUpdate> _participantUpdates;
rpl::event_stream<> _participantsSliceAdded;

View file

@ -832,7 +832,7 @@ void Session::registerInvitedToCallUser(
const auto inCall = ranges::contains(
call->participants(),
user,
&Data::GroupCall::Participant::user);
&Data::GroupCall::Participant::peer);
if (inCall) {
return;
}

View file

@ -88,6 +88,8 @@ LocationKey ComputeLocationKey(const Data::FileLocation &value) {
result.type |= (9ULL << 24);
result.type |= (uint64(uint32(data.vlocal_id().v)) << 32);
result.id = data.vvolume_id().v;
}, [&](const MTPDinputGroupCallStream &data) {
result.type = (10ULL << 24);
});
return result;
}

View file

@ -946,9 +946,9 @@ void GenerateItems(
addSimpleServiceMessage(text);
};
auto groupCallParticipantUser = [&](const MTPGroupCallParticipant &data) {
auto groupCallParticipantPeer = [&](const MTPGroupCallParticipant &data) {
return data.match([&](const MTPDgroupCallParticipant &data) {
return history->owner().user(data.vuser_id().v);
return history->owner().peer(peerFromMTP(data.vpeer()));
});
};
@ -966,29 +966,29 @@ void GenerateItems(
};
auto createParticipantMute = [&](const MTPDchannelAdminLogEventActionParticipantMute &data) {
const auto user = groupCallParticipantUser(data.vparticipant());
const auto userLink = user->createOpenLink();
const auto userLinkText = textcmdLink(2, user->name);
const auto participantPeer = groupCallParticipantPeer(data.vparticipant());
const auto participantPeerLink = participantPeer->createOpenLink();
const auto participantPeerLinkText = textcmdLink(2, participantPeer->name);
auto text = tr::lng_admin_log_muted_participant(
tr::now,
lt_from,
fromLinkText,
lt_user,
userLinkText);
addServiceMessageWithLink(text, userLink);
participantPeerLinkText);
addServiceMessageWithLink(text, participantPeerLink);
};
auto createParticipantUnmute = [&](const MTPDchannelAdminLogEventActionParticipantUnmute &data) {
const auto user = groupCallParticipantUser(data.vparticipant());
const auto userLink = user->createOpenLink();
const auto userLinkText = textcmdLink(2, user->name);
const auto participantPeer = groupCallParticipantPeer(data.vparticipant());
const auto participantPeerLink = participantPeer->createOpenLink();
const auto participantPeerLinkText = textcmdLink(2, participantPeer->name);
auto text = tr::lng_admin_log_unmuted_participant(
tr::now,
lt_from,
fromLinkText,
lt_user,
userLinkText);
addServiceMessageWithLink(text, userLink);
participantPeerLinkText);
addServiceMessageWithLink(text, participantPeerLink);
};
auto createToggleGroupCallSetting = [&](const MTPDchannelAdminLogEventActionToggleGroupCallSetting &data) {
@ -1069,9 +1069,9 @@ void GenerateItems(
};
auto createParticipantVolume = [&](const MTPDchannelAdminLogEventActionParticipantVolume &data) {
const auto user = groupCallParticipantUser(data.vparticipant());
const auto userLink = user->createOpenLink();
const auto userLinkText = textcmdLink(2, user->name);
const auto participantPeer = groupCallParticipantPeer(data.vparticipant());
const auto participantPeerLink = participantPeer->createOpenLink();
const auto participantPeerLinkText = textcmdLink(2, participantPeer->name);
const auto volume = data.vparticipant().match([&](
const MTPDgroupCallParticipant &data) {
return data.vvolume().value_or(10000);
@ -1082,10 +1082,10 @@ void GenerateItems(
lt_from,
fromLinkText,
lt_user,
userLinkText,
participantPeerLinkText,
lt_percent,
volumeText);
addServiceMessageWithLink(text, userLink);
addServiceMessageWithLink(text, participantPeerLink);
};
auto createChangeHistoryTTL = [&](const MTPDchannelAdminLogEventActionChangeHistoryTTL &data) {

View file

@ -99,7 +99,7 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
for (const auto &participant : call->participants()) {
const auto alreadyInList = ranges::contains(
state->userpics,
participant.user,
participant.peer,
&UserpicInRow::peer);
if (alreadyInList) {
continue;
@ -120,7 +120,7 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
for (auto i = 0; i != kLimit - already; ++i) {
if (adding[i]) {
state->userpics.push_back(UserpicInRow{
.peer = adding[i]->user,
.peer = adding[i]->peer,
.speaking = adding[i]->speaking,
});
}
@ -163,11 +163,11 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
static const auto RemoveUserpic = [](
not_null<State*> state,
not_null<Data::GroupCall*> call,
not_null<UserData*> user,
not_null<PeerData*> participantPeer,
int userpicSize) {
const auto i = ranges::find(
state->userpics,
user,
participantPeer,
&UserpicInRow::peer);
if (i == state->userpics.end()) {
return false;
@ -180,7 +180,7 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
static const auto CheckPushToFront = [](
not_null<State*> state,
not_null<Data::GroupCall*> call,
not_null<UserData*> user,
not_null<PeerData*> participantPeer,
int userpicSize) {
Expects(state->userpics.size() <= kLimit);
@ -189,7 +189,7 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
// Find where to put a new speaking userpic.
for (; i != end(state->userpics); ++i) {
if (i->peer == user) {
if (i->peer == participantPeer) {
if (i->speaking) {
return false;
}
@ -199,8 +199,8 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
}
const auto j = ranges::find(
participants,
not_null{ static_cast<UserData*>(i->peer.get()) },
&Data::GroupCall::Participant::user);
i->peer,
&Data::GroupCall::Participant::peer);
if (j == end(participants) || !j->speaking) {
// Found a non-speaking one, put the new speaking one here.
break;
@ -213,13 +213,13 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
// Add the new speaking to the place we found.
const auto added = state->userpics.insert(i, UserpicInRow{
.peer = user,
.peer = participantPeer,
.speaking = true,
});
// Remove him from the tail, if he was there.
for (auto i = added + 1; i != state->userpics.end(); ++i) {
if (i->peer == user) {
if (i->peer == participantPeer) {
state->userpics.erase(i);
break;
}
@ -230,8 +230,8 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
for (auto i = state->userpics.end() - 1; i != added; --i) {
const auto j = ranges::find(
participants,
not_null{ static_cast<UserData*>(i->peer.get()) },
&Data::GroupCall::Participant::user);
i->peer,
&Data::GroupCall::Participant::peer);
if (j == end(participants) || !j->speaking) {
// Found a non-speaking one, remove.
state->userpics.erase(i);
@ -263,14 +263,26 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
using ParticipantUpdate = Data::GroupCall::ParticipantUpdate;
call->participantUpdated(
) | rpl::start_with_next([=](const ParticipantUpdate &update) {
const auto user = update.now ? update.now->user : update.was->user;
const auto participantPeer = update.now
? update.now->peer
: update.was->peer;
if (!update.now) {
if (RemoveUserpic(state, call, user, userpicSize)) {
const auto removed = RemoveUserpic(
state,
call,
participantPeer,
userpicSize);
if (removed) {
pushNext();
}
} else if (update.now->speaking
&& (!update.was || !update.was->speaking)) {
if (CheckPushToFront(state, call, user, userpicSize)) {
const auto pushed = CheckPushToFront(
state,
call,
participantPeer,
userpicSize);
if (pushed) {
pushNext();
}
} else {
@ -279,7 +291,7 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
if (updateSpeakingState) {
const auto i = ranges::find(
state->userpics,
user,
participantPeer,
&UserpicInRow::peer);
if (i != end(state->userpics)) {
const auto index = i - begin(state->userpics);

View file

@ -166,6 +166,13 @@ StorageFileLocation::StorageFileLocation(
});
_volumeId = data.vvolume_id().v;
_localId = data.vlocal_id().v;
}, [&](const MTPDinputGroupCallStream &data) {
_type = Type::GroupCallStream;
data.vcall().match([&](const MTPDinputGroupCall &data) {
_id = data.vid().v;
_accessHash = data.vaccess_hash().v;
});
_localId = data.vdate().v;
});
}
@ -250,6 +257,11 @@ MTPInputFileLocation StorageFileLocation::tl(int32 self) const {
MTP_long(_volumeId),
MTP_int(_localId));
case Type::GroupCallStream:
return MTP_inputGroupCallStream(
MTP_inputGroupCall(MTP_long(_id), MTP_long(_accessHash)),
MTP_int(_localId));
}
Unexpected("Type in StorageFileLocation::tl.");
}
@ -358,6 +370,9 @@ bool StorageFileLocation::valid() const {
case Type::PeerPhoto:
case Type::StickerSetThumb:
return (_dcId != 0) && (_id != 0);
case Type::GroupCallStream:
return (_dcId != 0) && (_id != 0) && (_localId != 0);
}
return false;
}
@ -401,6 +416,11 @@ Storage::Cache::Key StorageFileLocation::cacheKey() const {
case Type::Takeout:
return Key{ shifted, 0 };
case Type::GroupCallStream:
return Key{
shifted | sliced | (uint64(uint32(_localId)) << 16),
_id };
}
return Key();
}
@ -443,6 +463,7 @@ Storage::Cache::Key StorageFileLocation::bigFileBaseCacheKey() const {
case Type::Encrypted:
case Type::Secure:
case Type::Takeout:
case Type::GroupCallStream:
Unexpected("Not implemented file location type.");
};
@ -522,6 +543,11 @@ bool operator==(const StorageFileLocation &a, const StorageFileLocation &b) {
&& (a._volumeId == b._volumeId)
&& (a._localId == b._localId)
&& (a._id == b._id);
case Type::GroupCallStream:
return (a._dcId == b._dcId)
&& (a._id == b._id)
&& (a._localId == b._localId);
};
Unexpected("Type in StorageFileLocation::operator==.");
}
@ -573,6 +599,10 @@ bool operator<(const StorageFileLocation &a, const StorageFileLocation &b) {
case Type::StickerSetThumb:
return std::tie(a._id, a._localId, a._volumeId, a._dcId)
< std::tie(b._id, b._localId, b._volumeId, b._dcId);
case Type::GroupCallStream:
return std::tie(a._id, a._localId, a._dcId)
< std::tie(b._id, b._localId, b._dcId);
};
Unexpected("Type in StorageFileLocation::operator==.");
}

View file

@ -59,6 +59,7 @@ public:
Photo = 0x05,
PeerPhoto = 0x06,
StickerSetThumb = 0x07,
GroupCallStream = 0x08,
};
StorageFileLocation() = default;