diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 76c6cc1161..2119b89a61 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -363,6 +363,8 @@ PRIVATE calls/group/calls_group_members_row.h calls/group/calls_group_menu.cpp calls/group/calls_group_menu.h + calls/group/calls_group_messages.cpp + calls/group/calls_group_messages.h calls/group/calls_group_message_field.cpp calls/group/calls_group_message_field.h calls/group/calls_group_panel.cpp diff --git a/Telegram/SourceFiles/api/api_polls.cpp b/Telegram/SourceFiles/api/api_polls.cpp index 810d515875..f091949b1a 100644 --- a/Telegram/SourceFiles/api/api_polls.cpp +++ b/Telegram/SourceFiles/api/api_polls.cpp @@ -22,13 +22,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" namespace Api { -namespace { - -[[nodiscard]] TimeId UnixtimeFromMsgId(mtpMsgId msgId) { - return TimeId(msgId >> 32); -} - -} // namespace Polls::Polls(not_null api) : _session(&api->session()) diff --git a/Telegram/SourceFiles/api/api_todo_lists.cpp b/Telegram/SourceFiles/api/api_todo_lists.cpp index c65001f671..7471f55846 100644 --- a/Telegram/SourceFiles/api/api_todo_lists.cpp +++ b/Telegram/SourceFiles/api/api_todo_lists.cpp @@ -25,10 +25,6 @@ namespace { constexpr auto kSendTogglesDelay = 3 * crl::time(1000); -[[nodiscard]] TimeId UnixtimeFromMsgId(mtpMsgId msgId) { - return TimeId(msgId >> 32); -} - } // namespace TodoLists::TodoLists(not_null api) diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index 46673f55e3..ddfabb6a56 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -2128,7 +2128,9 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updateGroupCallParticipants: case mtpc_updateGroupCallChainBlocks: case mtpc_updateGroupCallConnection: - case mtpc_updateGroupCall: { + case mtpc_updateGroupCall: + case mtpc_updateGroupCallMessage: + case mtpc_updateGroupCallEncryptedMessage: { Core::App().calls().handleUpdate(&session(), update); } break; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 21d03f338f..dcf1420983 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -107,10 +107,6 @@ using PhotoFileLocationId = Data::PhotoFileLocationId; using DocumentFileLocationId = Data::DocumentFileLocationId; using UpdatedFileReferences = Data::UpdatedFileReferences; -[[nodiscard]] TimeId UnixtimeFromMsgId(mtpMsgId msgId) { - return TimeId(msgId >> 32); -} - [[nodiscard]] std::shared_ptr ShowForPeer( not_null peer) { if (const auto window = Core::App().windowFor(peer)) { @@ -155,6 +151,14 @@ void ShowChannelsLimitBox(not_null peer) { } // namespace +namespace Api { + +TimeId UnixtimeFromMsgId(mtpMsgId msgId) { + return TimeId(msgId >> 32); +} + +} // namespace Api + ApiWrap::ApiWrap(not_null session) : MTP::Sender(&session->account().mtp()) , _session(session) @@ -2182,7 +2186,7 @@ void ApiWrap::saveDraftsToCloud() { history->finishSavingCloudDraft( topicRootId, monoforumPeerId, - UnixtimeFromMsgId(response.outerMsgId)); + Api::UnixtimeFromMsgId(response.outerMsgId)); const auto cloudDraft = history->cloudDraft( topicRootId, monoforumPeerId); @@ -2203,7 +2207,7 @@ void ApiWrap::saveDraftsToCloud() { history->finishSavingCloudDraft( topicRootId, monoforumPeerId, - UnixtimeFromMsgId(response.outerMsgId)); + Api::UnixtimeFromMsgId(response.outerMsgId)); const auto cloudDraft = history->cloudDraft( topicRootId, monoforumPeerId); @@ -4059,7 +4063,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) { history->finishSavingCloudDraft( draftTopicRootId, draftMonoforumPeerId, - UnixtimeFromMsgId(response.outerMsgId)); + Api::UnixtimeFromMsgId(response.outerMsgId)); } }; const auto fail = [=]( @@ -4074,7 +4078,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) { history->finishSavingCloudDraft( draftTopicRootId, draftMonoforumPeerId, - UnixtimeFromMsgId(response.outerMsgId)); + Api::UnixtimeFromMsgId(response.outerMsgId)); } }; const auto mtpShortcut = Data::ShortcutIdToMTP( @@ -4270,7 +4274,7 @@ void ApiWrap::sendInlineResult( history->finishSavingCloudDraft( topicRootId, monoforumPeerId, - UnixtimeFromMsgId(response.outerMsgId)); + Api::UnixtimeFromMsgId(response.outerMsgId)); if (done) { done(true); } @@ -4279,7 +4283,7 @@ void ApiWrap::sendInlineResult( history->finishSavingCloudDraft( topicRootId, monoforumPeerId, - UnixtimeFromMsgId(response.outerMsgId)); + Api::UnixtimeFromMsgId(response.outerMsgId)); if (done) { done(false); } diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 0a2a728eee..f1244c29b0 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -129,6 +129,8 @@ QString RequestKey(Types &&...values) { return result; } +[[nodiscard]] TimeId UnixtimeFromMsgId(mtpMsgId msgId); + } // namespace Api class ApiWrap final : public MTP::Sender { diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp index a04db8bdb7..bdd36e2710 100644 --- a/Telegram/SourceFiles/calls/calls_instance.cpp +++ b/Telegram/SourceFiles/calls/calls_instance.cpp @@ -596,6 +596,10 @@ void Instance::handleUpdate( handleGroupCallUpdate(session, update); }, [&](const MTPDupdateGroupCallChainBlocks &data) { handleGroupCallUpdate(session, update); + }, [&](const MTPDupdateGroupCallMessage &data) { + handleGroupCallUpdate(session, update); + }, [&](const MTPDupdateGroupCallEncryptedMessage &data) { + handleGroupCallUpdate(session, update); }, [](const auto &) { Unexpected("Update type in Calls::Instance::handleUpdate."); }); @@ -711,11 +715,17 @@ void Instance::handleGroupCallUpdate( groupCall->handlePossibleCreateOrJoinResponse(data); }, [&](const MTPDupdateGroupCallConnection &data) { groupCall->handlePossibleCreateOrJoinResponse(data); + }, [&](const MTPDupdateGroupCallMessage &data) { + groupCall->handleIncomingMessage(data); + }, [&](const MTPDupdateGroupCallEncryptedMessage &data) { + groupCall->handleIncomingMessage(data); }, [](const auto &) { }); } - if (update.type() == mtpc_updateGroupCallConnection) { + if (update.type() == mtpc_updateGroupCallConnection + || update.type() == mtpc_updateGroupCallMessage + || update.type() == mtpc_updateGroupCallEncryptedMessage) { return; } const auto callId = update.match([](const MTPDupdateGroupCall &data) { diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index 96b2f7a043..beb33ee1f2 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/group/calls_group_call.h" #include "calls/group/calls_group_common.h" +#include "calls/group/calls_group_messages.h" #include "calls/calls_instance.h" #include "main/session/session_show.h" #include "main/main_app_config.h" @@ -597,6 +598,7 @@ GroupCall::GroupCall( , _peer(join.peer) , _history(_peer->owner().history(_peer)) , _api(&_peer->session().mtp()) +, _messages(std::make_unique(this, &_api)) , _joinAs(join.joinAs) , _possibleJoinAs(std::move(join.possibleJoinAs)) , _joinHash(join.joinHash) @@ -1710,6 +1712,8 @@ void GroupCall::startConference() { return; } applyInputCall(_conferenceCall->input()); + _realChanges.fire_copy(_conferenceCall.get()); + initialJoinRequested(); joinDone( TimestampInMsFromMsgId(response.outerMsgId), @@ -2370,6 +2374,32 @@ void GroupCall::handlePossibleCreateOrJoinResponse( } } +void GroupCall::handleIncomingMessage( + const MTPDupdateGroupCallMessage &data) { + const auto id = data.vcall().match([&](const MTPDinputGroupCall &data) { + return data.vid().v; + }, [](const auto &) -> CallId { + Unexpected("slug/msg in GroupCall::handleIncomingMessage"); + }); + if (id != _id || conference()) { + return; + } + _messages->received(data); +} + +void GroupCall::handleIncomingMessage( + const MTPDupdateGroupCallEncryptedMessage &data) { + const auto id = data.vcall().match([&](const MTPDinputGroupCall &data) { + return data.vid().v; + }, [](const auto &) -> CallId { + Unexpected("slug/msg in GroupCall::handleIncomingMessage"); + }); + if (id != _id || !conference()) { + return; + } + _messages->received(data); +} + void GroupCall::handlePossibleDiscarded(const MTPDgroupCallDiscarded &data) { if (data.vid().v == _id) { LOG(("Call Info: Hangup after groupCallDiscarded.")); @@ -2995,9 +3025,7 @@ bool GroupCall::tryCreateController() { }); return result; }, - .e2eEncryptDecrypt = (_e2eEncryptDecrypt - ? _e2eEncryptDecrypt->callback() - : nullptr), + .e2eEncryptDecrypt = e2eEncryptDecrypt(), }; if (Logs::DebugEnabled()) { auto callLogFolder = cWorkingDir() + u"DebugLogs"_q; @@ -3050,9 +3078,7 @@ bool GroupCall::tryCreateScreencast() { .videoCapture = _screenCapture, .videoContentType = tgcalls::VideoContentType::Screencast, .videoCodecPreferences = lookupVideoCodecPreferences(), - .e2eEncryptDecrypt = (_e2eEncryptDecrypt - ? _e2eEncryptDecrypt->callback() - : nullptr), + .e2eEncryptDecrypt = e2eEncryptDecrypt(), }; LOG(("Call Info: Creating group screen instance")); @@ -4095,6 +4121,17 @@ void GroupCall::setNotRequireARGB32() { _requireARGB32 = false; } +std::function( + std::vector const &, + int64_t, bool, + int32_t)> GroupCall::e2eEncryptDecrypt() const { + return _e2eEncryptDecrypt ? _e2eEncryptDecrypt->callback() : nullptr; +} + +void GroupCall::sendMessage(TextWithTags message) { + _messages->send(std::move(message)); +} + auto GroupCall::otherParticipantStateValue() const -> rpl::producer { return _otherParticipantStateValue.events(); diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.h b/Telegram/SourceFiles/calls/group/calls_group_call.h index a78d3ab258..28d45432db 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.h +++ b/Telegram/SourceFiles/calls/group/calls_group_call.h @@ -60,6 +60,7 @@ struct RejoinEvent; struct RtmpInfo; enum class VideoQuality; enum class Error; +class Messages; } // namespace Group struct InviteRequest; @@ -278,6 +279,9 @@ public: void handlePossibleCreateOrJoinResponse(const MTPDupdateGroupCall &data); void handlePossibleCreateOrJoinResponse( const MTPDupdateGroupCallConnection &data); + void handleIncomingMessage(const MTPDupdateGroupCallMessage &data); + void handleIncomingMessage( + const MTPDupdateGroupCallEncryptedMessage &data); void changeTitle(const QString &title); void toggleRecording( bool enabled, @@ -446,6 +450,13 @@ public: void pushToTalk(bool pressed, crl::time delay); void setNotRequireARGB32(); + [[nodiscard]] std::function( + std::vector const &, + int64_t, bool, + int32_t)> e2eEncryptDecrypt() const; + void sendMessage(TextWithTags message); + [[nodiscard]] MTPInputGroupCall inputCall() const; + [[nodiscard]] rpl::lifetime &lifetime() { return _lifetime; } @@ -647,7 +658,6 @@ private: [[nodiscard]] int activeVideoSendersCount() const; - [[nodiscard]] MTPInputGroupCall inputCall() const; [[nodiscard]] MTPInputGroupCall inputCallSafe() const; const not_null _delegate; @@ -667,6 +677,7 @@ private: base::flat_set _unresolvedSsrcs; rpl::event_stream _errors; std::vector> _rejoinedCallbacks; + std::unique_ptr _messages; bool _recordingStoppedByMe = false; bool _requestedVideoChannelsUpdateScheduled = false; diff --git a/Telegram/SourceFiles/calls/group/calls_group_message_field.cpp b/Telegram/SourceFiles/calls/group/calls_group_message_field.cpp index 3dfcb4da86..fd354b552c 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_message_field.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_message_field.cpp @@ -7,8 +7,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "calls/group/calls_group_message_field.h" +#include "boxes/premium_preview_box.h" #include "chat_helpers/compose/compose_show.h" +#include "chat_helpers/emoji_suggestions_widget.h" +#include "chat_helpers/message_field.h" +#include "chat_helpers/tabbed_panel.h" +#include "chat_helpers/tabbed_selector.h" +#include "data/stickers/data_custom_emoji.h" +#include "data/stickers/data_stickers.h" +#include "data/data_document.h" #include "lang/lang_keys.h" +#include "main/main_session.h" #include "ui/controls/emoji_button.h" #include "ui/controls/send_button.h" #include "ui/widgets/fields/input_field.h" @@ -22,14 +31,15 @@ namespace Calls::Group { MessageField::MessageField( not_null parent, - std::shared_ptr show) + std::shared_ptr show, + PeerData *peer) : _parent(parent) , _show(std::move(show)) , _wrap(std::make_unique(_parent)) { - createControls(); + createControls(peer); } -void MessageField::createControls() { +void MessageField::createControls(PeerData *peer) { setupBackground(); const auto &st = st::storiesComposeControls; @@ -44,10 +54,83 @@ void MessageField::createControls() { _field->setDocumentMargin(4.); _field->setAdditionalMargin(style::ConvertScale(4) - 4); + const auto show = _show; + const auto allow = [=](not_null emoji) { + return peer + ? Data::AllowEmojiWithoutPremium(peer, emoji) + : show->session().premium(); + }; + InitMessageFieldHandlers({ + .session = &show->session(), + .show = show, + .field = _field, + .customEmojiPaused = [=] { + return show->paused(ChatHelpers::PauseReason::Layer); + }, + .allowPremiumEmoji = allow, + .fieldStyle = &st.files.caption, + }); + Ui::Emoji::SuggestionsController::Init( + _parent, + _field, + &_show->session(), + { + .suggestCustomEmoji = true, + .allowCustomWithoutPremium = allow, + .st = &st.suggestions, + }); + _send = Ui::CreateChild(_wrap.get(), st.send); + using Selector = ChatHelpers::TabbedSelector; + using Descriptor = ChatHelpers::TabbedPanelDescriptor; + _emojiPanel = Ui::CreateChild( + _parent, + ChatHelpers::TabbedPanelDescriptor{ + .ownedSelector = object_ptr( + nullptr, + ChatHelpers::TabbedSelectorDescriptor{ + .show = _show, + .st = st.tabbed, + .level = ChatHelpers::PauseReason::Layer, + .mode = ChatHelpers::TabbedSelector::Mode::EmojiOnly, + .features = { + .stickersSettings = false, + .openStickerSets = false, + }, + }), + }); + _emojiPanel->setDesiredHeightValues( + 1., + st::emojiPanMinHeight / 2, + st::emojiPanMinHeight); + _emojiPanel->hide(); + _emojiPanel->selector()->setCurrentPeer(peer); + _emojiPanel->selector()->emojiChosen( + ) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) { + Ui::InsertEmojiAtCursor(_field->textCursor(), data.emoji); + }, lifetime()); + _emojiPanel->selector()->customEmojiChosen( + ) | rpl::start_with_next([=](ChatHelpers::FileChosen data) { + const auto info = data.document->sticker(); + if (info + && info->setType == Data::StickersType::Emoji + && !_show->session().premium()) { + ShowPremiumPreviewBox( + _show, + PremiumFeature::AnimatedEmoji); + } else { + Data::InsertCustomEmoji(_field, data.document); + } + }, lifetime()); + _emojiToggle = Ui::CreateChild(_wrap.get(), st.emoji); + _emojiToggle->installEventFilter(_emojiPanel); + _emojiToggle->addClickHandler([=] { + _emojiPanel->toggleAnimated(); + }); + _width.value( ) | rpl::filter( rpl::mappers::_1 > 0 @@ -79,6 +162,15 @@ void MessageField::createControls() { }, _lifetime); } +void MessageField::updateEmojiPanelGeometry() { + const auto parent = _emojiPanel->parentWidget(); + const auto global = _emojiToggle->mapToGlobal({ 0, 0 }); + const auto local = parent->mapFromGlobal(global); + _emojiPanel->moveBottomRight( + local.y(), + local.x() + _emojiToggle->width() * 3); +} + void MessageField::setupBackground() { _wrap->paintRequest() | rpl::start_with_next([=] { const auto radius = st::historySendSize.height() / 2.; @@ -96,6 +188,7 @@ void MessageField::resizeToWidth(int newWidth) { if (_wrap->isHidden()) { Ui::SendPendingMoveResizeEvents(_wrap.get()); } + updateEmojiPanelGeometry(); } void MessageField::move(int x, int y) { diff --git a/Telegram/SourceFiles/calls/group/calls_group_message_field.h b/Telegram/SourceFiles/calls/group/calls_group_message_field.h index b573ed561b..179f67eb1a 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_message_field.h +++ b/Telegram/SourceFiles/calls/group/calls_group_message_field.h @@ -13,6 +13,7 @@ struct TextWithTags; namespace ChatHelpers { class Show; +class TabbedPanel; } // namespace ChatHelpers namespace Ui { @@ -28,7 +29,8 @@ class MessageField final { public: MessageField( not_null parent, - std::shared_ptr show); + std::shared_ptr show, + PeerData *peer); void resizeToWidth(int newWidth); void move(int x, int y); @@ -44,9 +46,10 @@ public: [[nodiscard]] rpl::lifetime &lifetime(); private: - void createControls(); + void createControls(PeerData *peer); void setupBackground(); void shownAnimationCallback(); + void updateEmojiPanelGeometry(); const not_null _parent; const std::shared_ptr _show; @@ -55,6 +58,7 @@ private: Ui::InputField *_field = nullptr; Ui::SendButton *_send = nullptr; Ui::EmojiButton *_emojiToggle = nullptr; + ChatHelpers::TabbedPanel *_emojiPanel = nullptr; rpl::variable _width; diff --git a/Telegram/SourceFiles/calls/group/calls_group_messages.cpp b/Telegram/SourceFiles/calls/group/calls_group_messages.cpp new file mode 100644 index 0000000000..4d793a548b --- /dev/null +++ b/Telegram/SourceFiles/calls/group/calls_group_messages.cpp @@ -0,0 +1,172 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "calls/group/calls_group_messages.h" + +#include "apiwrap.h" +#include "api/api_text_entities.h" +#include "base/unixtime.h" +#include "calls/group/calls_group_call.h" +#include "data/data_group_call.h" +#include "data/data_peer.h" +#include "data/data_session.h" +#include "data/data_user.h" +#include "main/main_session.h" +#include "mtproto/sender.h" +#include "ui/ui_utility.h" + +namespace Calls::Group { + +Messages::Messages(not_null call, not_null api) +: _call(call) +, _api(api) { + Ui::PostponeCall(_call, [=] { + _call->real( + ) | rpl::start_with_next([=](not_null call) { + _real = call; + if (ready()) { + sendPending(); + } else { + Unexpected("Not ready call."); + } + }, _lifetime); + }); +} + +bool Messages::ready() const { + return _real && (!_call->conference() || _call->e2eEncryptDecrypt()); +} + +void Messages::send(TextWithTags text) { + if (!ready()) { + _pending.push_back(std::move(text)); + return; + } + + auto prepared = TextWithEntities{ + text.text, + TextUtilities::ConvertTextTagsToEntities(text.tags) + }; + auto serialized = MTPTextWithEntities(MTP_textWithEntities( + MTP_string(prepared.text), + Api::EntitiesToMTP( + &_real->session(), + prepared.entities, + Api::ConvertOption::SkipLocal))); + + const auto id = ++_autoincrementId; + const auto from = _call->peer()->session().user(); + _messages.push_back({ + .id = id, + .peer = from, + .text = std::move(prepared), + }); + + if (!_call->conference()) { + _api->request(MTPphone_SendGroupCallMessage( + _call->inputCall(), + serialized + )).done([=](const MTPBool &, const MTP::Response &response) { + sent(id, response); + }).fail([=] { + failed(id); + }).send(); + } else { + auto counter = ::tl::details::LengthCounter(); + serialized.write(counter); + auto buffer = mtpBuffer(); + buffer.reserve(counter.length); + serialized.write(buffer); + const auto view = bytes::make_span(buffer); + auto v = std::vector(view.size()); + bytes::copy(bytes::make_span(v), view); + + const auto userId = peerToUser(from->id).bare; + const auto encrypt = _call->e2eEncryptDecrypt(); + const auto encrypted = encrypt(v, int64_t(userId), true, 0); + + _api->request(MTPphone_SendGroupCallEncryptedMessage( + _call->inputCall(), + MTP_bytes(bytes::make_span(encrypted)) + )).done([=](const MTPBool &, const MTP::Response &response) { + sent(id, response); + }).fail([=] { + failed(id); + }).send(); + } +} + +void Messages::received(const MTPDupdateGroupCallMessage &data) { + if (!ready()) { + return; + } + received(data.vfrom_id(), data.vmessage()); +} + +void Messages::received(const MTPDupdateGroupCallEncryptedMessage &data) { + if (!ready()) { + return; + } + const auto fromId = data.vfrom_id(); + const auto &bytes = data.vencrypted_message().v; + auto v = std::vector(bytes.size()); + bytes::copy(bytes::make_span(v), bytes::make_span(bytes)); + + const auto userId = peerToUser(peerFromMTP(fromId)).bare; + const auto decrypt = _call->e2eEncryptDecrypt(); + const auto decrypted = decrypt(v, int64_t(userId), false, 0); + if (decrypted.empty() || decrypted.size() % 4 != 0) { + LOG(("API Error: Wrong decrypted message size: %1" + ).arg(decrypted.size())); + return; + } + auto info = reinterpret_cast(decrypted.data()); + + auto text = MTPTextWithEntities(); + if (!text.read(info, info + (decrypted.size() / 4))) { + LOG(("API Error: Can't parse decrypted message")); + return; + } + received(fromId, text); +} + +void Messages::received( + const MTPPeer &from, + const MTPTextWithEntities &message) { + const auto id = ++_autoincrementId; + const auto peer = _call->peer(); + _messages.push_back({ + .id = id, + .date = base::unixtime::now(), + .peer = peer->owner().peer(peerFromMTP(from)), + .text = Api::ParseTextWithEntities(&peer->session(), message), + }); +} + +void Messages::sendPending() { + Expects(_real != nullptr); + + for (auto &pending : base::take(_pending)) { + send(std::move(pending)); + } +} + +void Messages::sent(int id, const MTP::Response &response) { + const auto i = ranges::find(_messages, id, &Message::id); + if (i != end(_messages)) { + i->date = Api::UnixtimeFromMsgId(response.outerMsgId); + } +} + +void Messages::failed(int id) { + const auto i = ranges::find(_messages, id, &Message::id); + if (i != end(_messages)) { + i->failed = true; + } +} + +} // namespace Calls::Group diff --git a/Telegram/SourceFiles/calls/group/calls_group_messages.h b/Telegram/SourceFiles/calls/group/calls_group_messages.h new file mode 100644 index 0000000000..6563150b3b --- /dev/null +++ b/Telegram/SourceFiles/calls/group/calls_group_messages.h @@ -0,0 +1,65 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Calls { +class GroupCall; +} // namespace Calls + +namespace Data { +class GroupCall; +} // namespace Data + +namespace MTP { +class Sender; +struct Response; +} // namespace MTP + +namespace Calls::Group { + +class Messages final { +public: + Messages(not_null call, not_null api); + + void send(TextWithTags text); + + void received(const MTPDupdateGroupCallMessage &data); + void received(const MTPDupdateGroupCallEncryptedMessage &data); + +private: + struct Message { + int id = 0; + TimeId date = 0; + not_null peer; + TextWithEntities text; + bool failed = false; + }; + + [[nodiscard]] bool ready() const; + void sendPending(); + + void received(const MTPPeer &from, const MTPTextWithEntities &message); + void sent(int id, const MTP::Response &response); + void failed(int id); + + const not_null _call; + const not_null _api; + + Data::GroupCall *_real = nullptr; + + std::vector _pending; + + std::vector _messages; + + int _autoincrementId = 0; + + rpl::lifetime _lifetime; + +}; + +} // namespace Calls::Group diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp index 484033c37f..6423f67f14 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp @@ -486,14 +486,18 @@ void Panel::toggleMessageTyping() { if (_messageField) { _messageField->toggle(typing); } else if (typing) { - _messageField = std::make_unique(widget(), uiShow()); + _messageField = std::make_unique( + widget(), + uiShow(), + _call->conference() ? nullptr : _call->peer().get()); updateButtonsGeometry(); _messageField->toggle(true); _messageField->submitted( ) | rpl::start_with_next([=](TextWithTags text) { - //_call->sendMessage(text); + _call->sendMessage(std::move(text)); + _messageField->toggle(false); _messageTyping = false; updateWideControlsVisibility();