mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Send / receive messages in group calls.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<ApiWrap*> api)
|
||||
: _session(&api->session())
|
||||
|
||||
@@ -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<ApiWrap*> api)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<ChatHelpers::Show> ShowForPeer(
|
||||
not_null<PeerData*> peer) {
|
||||
if (const auto window = Core::App().windowFor(peer)) {
|
||||
@@ -155,6 +151,14 @@ void ShowChannelsLimitBox(not_null<PeerData*> peer) {
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Api {
|
||||
|
||||
TimeId UnixtimeFromMsgId(mtpMsgId msgId) {
|
||||
return TimeId(msgId >> 32);
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
ApiWrap::ApiWrap(not_null<Main::Session*> 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);
|
||||
}
|
||||
|
||||
@@ -129,6 +129,8 @@ QString RequestKey(Types &&...values) {
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] TimeId UnixtimeFromMsgId(mtpMsgId msgId);
|
||||
|
||||
} // namespace Api
|
||||
|
||||
class ApiWrap final : public MTP::Sender {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<Group::Messages>(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<uint8_t>(
|
||||
std::vector<uint8_t> 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<Group::ParticipantState> {
|
||||
return _otherParticipantStateValue.events();
|
||||
|
||||
@@ -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<uint8_t>(
|
||||
std::vector<uint8_t> 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*> _delegate;
|
||||
@@ -667,6 +677,7 @@ private:
|
||||
base::flat_set<uint32> _unresolvedSsrcs;
|
||||
rpl::event_stream<Error> _errors;
|
||||
std::vector<Fn<void()>> _rejoinedCallbacks;
|
||||
std::unique_ptr<Group::Messages> _messages;
|
||||
bool _recordingStoppedByMe = false;
|
||||
bool _requestedVideoChannelsUpdateScheduled = false;
|
||||
|
||||
|
||||
@@ -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<QWidget*> parent,
|
||||
std::shared_ptr<ChatHelpers::Show> show)
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
PeerData *peer)
|
||||
: _parent(parent)
|
||||
, _show(std::move(show))
|
||||
, _wrap(std::make_unique<Ui::RpWidget>(_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<DocumentData*> 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<Ui::SendButton>(_wrap.get(), st.send);
|
||||
|
||||
using Selector = ChatHelpers::TabbedSelector;
|
||||
using Descriptor = ChatHelpers::TabbedPanelDescriptor;
|
||||
_emojiPanel = Ui::CreateChild<ChatHelpers::TabbedPanel>(
|
||||
_parent,
|
||||
ChatHelpers::TabbedPanelDescriptor{
|
||||
.ownedSelector = object_ptr<Selector>(
|
||||
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<Ui::EmojiButton>(_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) {
|
||||
|
||||
@@ -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<QWidget*> parent,
|
||||
std::shared_ptr<ChatHelpers::Show> show);
|
||||
std::shared_ptr<ChatHelpers::Show> 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<QWidget*> _parent;
|
||||
const std::shared_ptr<ChatHelpers::Show> _show;
|
||||
@@ -55,6 +58,7 @@ private:
|
||||
Ui::InputField *_field = nullptr;
|
||||
Ui::SendButton *_send = nullptr;
|
||||
Ui::EmojiButton *_emojiToggle = nullptr;
|
||||
ChatHelpers::TabbedPanel *_emojiPanel = nullptr;
|
||||
|
||||
rpl::variable<int> _width;
|
||||
|
||||
|
||||
@@ -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<GroupCall*> call, not_null<MTP::Sender*> api)
|
||||
: _call(call)
|
||||
, _api(api) {
|
||||
Ui::PostponeCall(_call, [=] {
|
||||
_call->real(
|
||||
) | rpl::start_with_next([=](not_null<Data::GroupCall*> 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<std::uint8_t>(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<std::uint8_t>(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<const mtpPrime*>(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
|
||||
@@ -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<GroupCall*> call, not_null<MTP::Sender*> 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<PeerData*> 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<GroupCall*> _call;
|
||||
const not_null<MTP::Sender*> _api;
|
||||
|
||||
Data::GroupCall *_real = nullptr;
|
||||
|
||||
std::vector<TextWithTags> _pending;
|
||||
|
||||
std::vector<Message> _messages;
|
||||
|
||||
int _autoincrementId = 0;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Calls::Group
|
||||
@@ -486,14 +486,18 @@ void Panel::toggleMessageTyping() {
|
||||
if (_messageField) {
|
||||
_messageField->toggle(typing);
|
||||
} else if (typing) {
|
||||
_messageField = std::make_unique<MessageField>(widget(), uiShow());
|
||||
_messageField = std::make_unique<MessageField>(
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user