diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 6c75ca852..5aa204d79 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1066,7 +1066,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_action_invite_users_many" = "{from} invited {users} to the voice chat"; "lng_action_invite_users_and_one" = "{accumulated}, {user}"; "lng_action_invite_users_and_last" = "{accumulated} and {user}"; -"lng_action_group_call" = "Group call"; +"lng_action_group_call_started" = "{from} started a voice chat"; +"lng_action_group_call_finished" = "Voice chat finished ({duration})"; "lng_action_add_user" = "{from} added {user}"; "lng_action_add_users_many" = "{from} added {users}"; "lng_action_add_users_and_one" = "{accumulated}, {user}"; @@ -1847,6 +1848,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_no_anonymous" = "Anonymous admins can't join voice chats :("; "lng_group_call_context_mute" = "Mute"; "lng_group_call_context_unmute" = "Unmute"; +"lng_group_call_duration_days#one" = "{count} day"; +"lng_group_call_duration_days#other" = "{count} days"; +"lng_group_call_duration_hours#one" = "{count} hour"; +"lng_group_call_duration_hours#other" = "{count} hours"; +"lng_group_call_duration_minutes#one" = "{count} minute"; +"lng_group_call_duration_minutes#other" = "{count} minutes"; +"lng_group_call_duration_seconds#one" = "{count} second"; +"lng_group_call_duration_seconds#other" = "{count} seconds"; "lng_no_mic_permission" = "Telegram needs access to your microphone so that you can make calls and record voice messages."; diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index 9babc7347..ca1c6f3cc 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -1799,6 +1799,15 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updatePhoneCallSignalingData: case mtpc_updateGroupCallParticipants: case mtpc_updateGroupCall: { + if (update.type() == mtpc_updateGroupCall) { + const auto &data = update.c_updateGroupCall(); + if (data.vcall().type() == mtpc_groupCallDiscarded) { + const auto &call = data.vcall().c_groupCallDiscarded(); + session().data().groupCallDiscarded( + call.vid().v, + call.vduration().v); + } + } Core::App().calls().handleUpdate(&session(), update); } break; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 833aae59d..38f887599 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -800,6 +800,14 @@ void Session::unregisterGroupCall(not_null call) { _groupCalls.remove(call->id()); } +rpl::producer Session::groupCallDiscards() const { + return _groupCallDiscarded.events(); +} + +void Session::groupCallDiscarded(uint64 id, int duration) { + _groupCallDiscarded.fire({ id, duration }); +} + GroupCall *Session::groupCall(uint64 callId) const { const auto i = _groupCalls.find(callId); return (i != end(_groupCalls)) ? i->second.get() : nullptr; diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index b61cce387..f3d05032d 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -158,6 +158,13 @@ public: void unregisterGroupCall(not_null call); GroupCall *groupCall(uint64 callId) const; + struct GroupCallDiscard { + uint64 id = 0; + int duration = 0; + }; + rpl::producer groupCallDiscards() const; + void groupCallDiscarded(uint64 id, int duration); + [[nodiscard]] auto invitedToCallUsers(uint64 callId) const -> const base::flat_set> &; void registerInvitedToCallUser( @@ -921,6 +928,7 @@ private: base::flat_map> _groupCalls; base::flat_map>> _invitedToCallUsers; + rpl::event_stream _groupCallDiscarded; History *_topPromoted = nullptr; diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index 74ee7373a..7467f196f 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -274,6 +274,16 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { return result; }; + auto prepareGroupCall = [this](const MTPDmessageActionGroupCall &action) { + if (const auto duration = action.vduration()) { + return prepareDiscardedCallText(duration->v); + } + auto result = PreparedText{}; + result.links.push_back(fromLink()); + result.text = tr::lng_action_group_call_started(tr::now, lt_from, fromLinkText()); + return result; + }; + auto prepareInviteToGroupCall = [this](const MTPDmessageActionInviteToGroupCall &action) { const auto channel = history()->peer->asChannel(); const auto callId = action.vcall().match([&](const MTPDinputGroupCall &data) { @@ -367,7 +377,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { LOG(("API Error: messageActionSecureValuesSentMe received.")); return PreparedText{ tr::lng_message_empty(tr::now) }; }, [&](const MTPDmessageActionGroupCall &data) { - return PreparedText{ tr::lng_action_group_call(tr::now) }; + return prepareGroupCall(data); }, [&](const MTPDmessageActionInviteToGroupCall &data) { return prepareInviteToGroupCall(data); }, [](const MTPDmessageActionEmpty &) { @@ -466,6 +476,22 @@ bool HistoryService::updateDependent(bool force) { return (dependent->msg || !dependent->msgId); } +HistoryService::PreparedText HistoryService::prepareDiscardedCallText( + int duration) { + const auto seconds = duration; + const auto days = seconds / 86400; + const auto hours = seconds / 3600; + const auto minutes = seconds / 60; + auto text = (days > 1) + ? tr::lng_group_call_duration_days(tr::now, lt_count, days) + : (hours > 1) + ? tr::lng_group_call_duration_hours(tr::now, lt_count, hours) + : (minutes > 1) + ? tr::lng_group_call_duration_minutes(tr::now, lt_count, minutes) + : tr::lng_group_call_duration_seconds(tr::now, lt_count, seconds); + return PreparedText{ tr::lng_action_group_call_finished(tr::now, lt_duration, text) }; +} + HistoryService::PreparedText HistoryService::preparePinnedText() { auto result = PreparedText {}; auto pinned = Get(); @@ -789,13 +815,33 @@ void HistoryService::createFromMtp(const MTPDmessage &message) { void HistoryService::createFromMtp(const MTPDmessageService &message) { if (message.vaction().type() == mtpc_messageActionGameScore) { + const auto &data = message.vaction().c_messageActionGameScore(); UpdateComponents(HistoryServiceGameScore::Bit()); - Get()->score = message.vaction().c_messageActionGameScore().vscore().v; + Get()->score = data.vscore().v; } else if (message.vaction().type() == mtpc_messageActionPaymentSent) { + const auto &data = message.vaction().c_messageActionPaymentSent(); UpdateComponents(HistoryServicePayment::Bit()); - auto amount = message.vaction().c_messageActionPaymentSent().vtotal_amount().v; - auto currency = qs(message.vaction().c_messageActionPaymentSent().vcurrency()); + const auto amount = data.vtotal_amount().v; + const auto currency = qs(data.vcurrency()); Get()->amount = Ui::FillAmountAndCurrency(amount, currency); + } else if (message.vaction().type() == mtpc_messageActionGroupCall) { + const auto &data = message.vaction().c_messageActionGroupCall(); + if (data.vduration()) { + RemoveComponents(HistoryServiceOngoingCall::Bit()); + } else { + UpdateComponents(HistoryServiceOngoingCall::Bit()); + const auto call = Get(); + const auto id = data.vcall().c_inputGroupCall().vid().v; + call->lifetime.destroy(); + history()->owner().groupCallDiscards( + ) | rpl::filter([=](Data::Session::GroupCallDiscard discard) { + return (discard.id == id); + }) | rpl::start_with_next([=]( + Data::Session::GroupCallDiscard discard) { + RemoveComponents(HistoryServiceOngoingCall::Bit()); + updateText(prepareDiscardedCallText(discard.duration)); + }, call->lifetime); + } } if (const auto replyTo = message.vreply_to()) { replyTo->match([&](const MTPDmessageReplyHeader &data) { @@ -852,7 +898,7 @@ Storage::SharedMediaTypesMask HistoryService::sharedMediaTypes() const { } void HistoryService::updateDependentText() { - auto text = PreparedText {}; + auto text = PreparedText{}; if (Has()) { text = preparePinnedText(); } else if (Has()) { @@ -862,7 +908,10 @@ void HistoryService::updateDependentText() { } else { return; } + updateText(std::move(text)); +} +void HistoryService::updateText(PreparedText &&text) { setServiceText(text); history()->owner().requestItemResize(this); const auto inDialogsHistory = history()->migrateToOrMe(); diff --git a/Telegram/SourceFiles/history/history_service.h b/Telegram/SourceFiles/history/history_service.h index 99e93be8d..f9f93240a 100644 --- a/Telegram/SourceFiles/history/history_service.h +++ b/Telegram/SourceFiles/history/history_service.h @@ -20,24 +20,24 @@ struct HistoryServiceDependentData { }; struct HistoryServicePinned - : public RuntimeComponent - , public HistoryServiceDependentData { +: public RuntimeComponent +, public HistoryServiceDependentData { }; struct HistoryServiceGameScore - : public RuntimeComponent - , public HistoryServiceDependentData { +: public RuntimeComponent +, public HistoryServiceDependentData { int score = 0; }; struct HistoryServicePayment - : public RuntimeComponent - , public HistoryServiceDependentData { +: public RuntimeComponent +, public HistoryServiceDependentData { QString amount; }; struct HistoryServiceSelfDestruct - : public RuntimeComponent { +: public RuntimeComponent { enum class Type { Photo, Video, @@ -47,6 +47,12 @@ struct HistoryServiceSelfDestruct crl::time destructAt = 0; }; +struct HistoryServiceOngoingCall +: public RuntimeComponent { + uint64 id = 0; + rpl::lifetime lifetime; +}; + namespace HistoryView { class ServiceMessagePainter; } // namespace HistoryView @@ -136,6 +142,7 @@ private: } bool updateDependent(bool force = false); void updateDependentText(); + void updateText(PreparedText &&text); void clearDependency(); void createFromMtp(const MTPDmessage &message); @@ -149,6 +156,7 @@ private: PreparedText preparePinnedText(); PreparedText prepareGameScoreText(); PreparedText preparePaymentSentText(); + PreparedText prepareDiscardedCallText(int duration); friend class HistoryView::Service;