From 346f7aadd6edda3bf921278b88d1caeb5293fafe Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 31 Mar 2025 00:06:02 +0500 Subject: [PATCH] Render confcall messages as phone calls. --- Telegram/Resources/langs/lang.strings | 6 +-- .../calls/calls_box_controller.cpp | 6 +-- Telegram/SourceFiles/calls/calls_instance.cpp | 1 - .../SourceFiles/data/data_media_types.cpp | 44 +++++++++++++------ Telegram/SourceFiles/data/data_media_types.h | 15 ++++--- .../export/data/export_data_types.cpp | 33 +++++++++----- .../export/data/export_data_types.h | 17 +++---- .../export/output/export_output_abstract.cpp | 2 +- .../export/output/export_output_html.cpp | 30 +++++-------- .../export/output/export_output_json.cpp | 37 ++++++++-------- Telegram/SourceFiles/history/history_item.cpp | 34 ++++---------- .../history/view/media/history_view_call.cpp | 32 +++++++++----- .../history/view/media/history_view_call.h | 7 +-- 13 files changed, 137 insertions(+), 127 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 85bbaec270..a5d537fe32 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2220,10 +2220,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_action_message_price_paid#other" = "Messages now cost {count} Stars each in this group."; "lng_you_paid_stars#one" = "You paid {count} Star."; "lng_you_paid_stars#other" = "You paid {count} Stars."; -"lng_action_confcall_invitation" = "Call invitation..."; -"lng_action_confcall_missed" = "Missed conference call"; -"lng_action_confcall_ongoing" = "Ongoing conference call"; -"lng_action_confcall_finished" = "Conference call"; "lng_you_joined_group" = "You joined this group"; @@ -4684,6 +4680,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_call_video_declined" = "Declined video call"; "lng_call_duration_info" = "{time}, {duration}"; "lng_call_type_and_duration" = "{type} ({duration})"; +"lng_call_invitation" = "Call invitation"; +"lng_call_ongoing" = "Ongoing call"; "lng_call_rate_label" = "Please rate the quality of your call"; "lng_call_rate_comment" = "Comment (optional)"; diff --git a/Telegram/SourceFiles/calls/calls_box_controller.cpp b/Telegram/SourceFiles/calls/calls_box_controller.cpp index 8e093719fc..1f3337569d 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.cpp +++ b/Telegram/SourceFiles/calls/calls_box_controller.cpp @@ -430,9 +430,9 @@ BoxController::Row::Type BoxController::Row::ComputeType( return Type::Out; } else if (auto media = item->media()) { if (const auto call = media->call()) { - const auto reason = call->finishReason; - if (reason == Data::Call::FinishReason::Busy - || reason == Data::Call::FinishReason::Missed) { + using State = Data::CallState; + const auto state = call->state; + if (state == State::Busy || state == State::Missed) { return Type::Missed; } } diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp index effbcfb8bb..833b40e93f 100644 --- a/Telegram/SourceFiles/calls/calls_instance.cpp +++ b/Telegram/SourceFiles/calls/calls_instance.cpp @@ -236,7 +236,6 @@ void Instance::startOrJoinConferenceCall(StartConferenceCallArgs args) { args.migrating ? args.linkSlug : QString()); const auto session = &args.call->peer()->session(); - const auto showShareLink = args.migrating && args.invite.empty(); auto call = std::make_unique( _delegate.get(), Calls::Group::ConferenceInfo{ diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 067e8af492..ab2c7e4712 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -457,28 +457,42 @@ Invoice ComputeInvoiceData( Call ComputeCallData(const MTPDmessageActionPhoneCall &call) { auto result = Call(); - result.finishReason = [&] { + result.state = [&] { if (const auto reason = call.vreason()) { return reason->match([](const MTPDphoneCallDiscardReasonBusy &) { - return CallFinishReason::Busy; + return CallState::Busy; }, [](const MTPDphoneCallDiscardReasonDisconnect &) { - return CallFinishReason::Disconnected; + return CallState::Disconnected; }, [](const MTPDphoneCallDiscardReasonHangup &) { - return CallFinishReason::Hangup; + return CallState::Hangup; }, [](const MTPDphoneCallDiscardReasonMissed &) { - return CallFinishReason::Missed; + return CallState::Missed; }, [](const MTPDphoneCallDiscardReasonMigrateConferenceCall &) { - return CallFinishReason::MigrateConferenceCall; + return CallState::MigrateConferenceCall; }); Unexpected("Call reason type."); } - return CallFinishReason::Hangup; + return CallState::Hangup; }(); result.duration = call.vduration().value_or_empty(); result.video = call.is_video(); return result; } +Call ComputeCallData(const MTPDmessageActionConferenceCall &call) { + return { + .conferenceId = call.vcall_id().v, + .duration = call.vduration().value_or_empty(), + .state = (call.vduration().value_or_empty() + ? CallState::Hangup + : call.is_missed() + ? CallState::Missed + : call.is_active() + ? CallState::Active + : CallState::Invitation), + }; +} + GiveawayStart ComputeGiveawayStartData( not_null item, const MTPDmessageMediaGiveaway &data) { @@ -1686,7 +1700,7 @@ const Call *MediaCall::call() const { } TextWithEntities MediaCall::notificationText() const { - auto result = Text(parent(), _call.finishReason, _call.video); + auto result = Text(parent(), _call.state, _call.video); if (_call.duration > 0) { result = tr::lng_call_type_and_duration( tr::now, @@ -1727,21 +1741,25 @@ std::unique_ptr MediaCall::createView( QString MediaCall::Text( not_null item, - CallFinishReason reason, + CallState state, bool video) { - if (item->out()) { - return ((reason == CallFinishReason::Missed) + if (state == CallState::Invitation) { + return tr::lng_call_invitation(tr::now); + } else if (state == CallState::Active) { + return tr::lng_call_ongoing(tr::now); + } else if (item->out()) { + return ((state == CallState::Missed) ? (video ? tr::lng_call_video_cancelled : tr::lng_call_cancelled) : (video ? tr::lng_call_video_outgoing : tr::lng_call_outgoing))(tr::now); - } else if (reason == CallFinishReason::Missed) { + } else if (state == CallState::Missed) { return (video ? tr::lng_call_video_missed : tr::lng_call_missed)(tr::now); - } else if (reason == CallFinishReason::Busy) { + } else if (state == CallState::Busy) { return (video ? tr::lng_call_video_declined : tr::lng_call_declined)(tr::now); diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index 14452c7972..f47197816e 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -41,12 +41,14 @@ class WallPaper; class Session; struct UniqueGift; -enum class CallFinishReason : char { +enum class CallState : char { Missed, Busy, Disconnected, Hangup, MigrateConferenceCall, + Invitation, + Active, }; struct SharedContact final { @@ -78,10 +80,11 @@ struct SharedContact final { }; struct Call { - using FinishReason = CallFinishReason; + using State = CallState; + CallId conferenceId = 0; int duration = 0; - FinishReason finishReason = FinishReason::Missed; + State state = State::Missed; bool video = false; }; @@ -462,9 +465,9 @@ public: not_null realParent, HistoryView::Element *replacing = nullptr) override; - static QString Text( + [[nodiscard]] static QString Text( not_null item, - CallFinishReason reason, + CallState state, bool video); private: @@ -799,6 +802,8 @@ private: const MTPDmessageMediaPaidMedia &data); [[nodiscard]] Call ComputeCallData(const MTPDmessageActionPhoneCall &call); +[[nodiscard]] Call ComputeCallData( + const MTPDmessageActionConferenceCall &call); [[nodiscard]] GiveawayStart ComputeGiveawayStartData( not_null item, diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index 919b35f8ef..e3bf2f2100 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -1461,18 +1461,18 @@ ServiceAction ParseServiceAction( content.duration = duration->v; } if (const auto reason = data.vreason()) { - using Reason = ActionPhoneCall::DiscardReason; - content.discardReason = reason->match( + using State = ActionPhoneCall::State; + content.state = reason->match( [](const MTPDphoneCallDiscardReasonMissed &data) { - return Reason::Missed; + return State::Missed; }, [](const MTPDphoneCallDiscardReasonDisconnect &data) { - return Reason::Disconnect; + return State::Disconnect; }, [](const MTPDphoneCallDiscardReasonHangup &data) { - return Reason::Hangup; + return State::Hangup; }, [](const MTPDphoneCallDiscardReasonBusy &data) { - return Reason::Busy; + return State::Busy; }, [](const MTPDphoneCallDiscardReasonMigrateConferenceCall &) { - return Reason::MigrateConferenceCall; + return State::MigrateConferenceCall; }); } result.content = content; @@ -1714,11 +1714,20 @@ ServiceAction ParseServiceAction( .stars = int(data.vstars().v), }; }, [&](const MTPDmessageActionConferenceCall &data) { - result.content = ActionConferenceCall{ - .duration = data.vduration().value_or_empty(), - .active = data.is_active(), - .missed = data.is_missed(), - }; + auto content = ActionPhoneCall(); + using State = ActionPhoneCall::State; + content.conferenceId = data.vcall_id().v; + if (const auto duration = data.vduration()) { + content.duration = duration->v; + } + content.state = data.is_missed() + ? State::Missed + : data.is_active() + ? State::Active + : data.vduration().value_or_empty() + ? State::Hangup + : State::Invitation; + result.content = content; }, [](const MTPDmessageActionEmpty &data) {}); return result; } diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index 3b37de23d9..ca9a95916d 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -497,15 +497,19 @@ struct ActionPaymentSent { }; struct ActionPhoneCall { - enum class DiscardReason { + enum class State { Unknown, Missed, Disconnect, Hangup, Busy, MigrateConferenceCall, + Invitation, + Active, }; - DiscardReason discardReason = DiscardReason::Unknown; + + uint64 conferenceId = 0; + State state = State::Unknown; int duration = 0; }; @@ -671,12 +675,6 @@ struct ActionPaidMessagesPrice { int stars = 0; }; -struct ActionConferenceCall { - int duration = 0; - bool active = false; - bool missed = false; -}; - struct ServiceAction { std::variant< v::null_t, @@ -724,8 +722,7 @@ struct ServiceAction { ActionPrizeStars, ActionStarGift, ActionPaidMessagesRefunded, - ActionPaidMessagesPrice, - ActionConferenceCall> content; + ActionPaidMessagesPrice> content; }; ServiceAction ParseServiceAction( diff --git a/Telegram/SourceFiles/export/output/export_output_abstract.cpp b/Telegram/SourceFiles/export/output/export_output_abstract.cpp index dbdb1bf999..cadd6dbb3c 100644 --- a/Telegram/SourceFiles/export/output/export_output_abstract.cpp +++ b/Telegram/SourceFiles/export/output/export_output_abstract.cpp @@ -408,7 +408,7 @@ Stats AbstractWriter::produceTestExample( auto message = serviceMessage(); auto action = Data::ActionPhoneCall(); action.duration = counter(); - action.discardReason = Data::ActionPhoneCall::DiscardReason::Busy; + action.state = Data::ActionPhoneCall::State::Busy; message.action.content = action; return message; }()); diff --git a/Telegram/SourceFiles/export/output/export_output_html.cpp b/Telegram/SourceFiles/export/output/export_output_html.cpp index 29f7d734a7..3d960a38e2 100644 --- a/Telegram/SourceFiles/export/output/export_output_html.cpp +++ b/Telegram/SourceFiles/export/output/export_output_html.cpp @@ -1387,16 +1387,6 @@ auto HtmlWriter::Wrap::pushMessage( + QString::number(data.stars).toUtf8() + " Telegram Stars."; return result; - }, [&](const ActionConferenceCall &data) { - return data.missed - ? "Missed conference call" - : data.active - ? "Ongoing conference call" - : data.duration - ? "Conference call (" - + NumberToString(data.duration) - + " seconds)" - : "Declined conference call"; }, [](v::null_t) { return QByteArray(); }); if (!serviceText.isEmpty()) { @@ -2286,16 +2276,20 @@ MediaData HtmlWriter::Wrap::prepareMediaData( if (const auto call = std::get_if(&action.content)) { result.classes = "media_call"; result.title = peers.peer(message.out - ? message.peerId - : message.selfId).name(); + ? message.peerId + : message.selfId).name(); result.status = [&] { - using Reason = ActionPhoneCall::DiscardReason; - const auto reason = call->discardReason; - if (message.out) { - return reason == Reason::Missed ? "Cancelled" : "Outgoing"; - } else if (reason == Reason::Missed) { + using State = ActionPhoneCall::State; + const auto state = call->state; + if (state == State::Invitation) { + return "Invitation"; + } else if (state == State::Active) { + return "Ongoing"; + } else if (message.out) { + return (state == State::Missed) ? "Cancelled" : "Outgoing"; + } else if (state == State::Missed) { return "Missed"; - } else if (reason == Reason::Busy) { + } else if (state == State::Busy) { return "Declined"; } return "Incoming"; diff --git a/Telegram/SourceFiles/export/output/export_output_json.cpp b/Telegram/SourceFiles/export/output/export_output_json.cpp index 5e263e60d3..772bc6f3f2 100644 --- a/Telegram/SourceFiles/export/output/export_output_json.cpp +++ b/Telegram/SourceFiles/export/output/export_output_json.cpp @@ -461,22 +461,27 @@ QByteArray SerializeMessage( } }, [&](const ActionPhoneCall &data) { pushActor(); - pushAction("phone_call"); + pushAction(data.conferenceId ? "conference_call" : "phone_call"); if (data.duration) { push("duration_seconds", data.duration); } - using Reason = ActionPhoneCall::DiscardReason; - push("discard_reason", [&] { - switch (data.discardReason) { - case Reason::Busy: return "busy"; - case Reason::Disconnect: return "disconnect"; - case Reason::Hangup: return "hangup"; - case Reason::Missed: return "missed"; - case Reason::MigrateConferenceCall: - return "migrate_conference_all"; - } - return ""; - }()); + using State = ActionPhoneCall::State; + if (data.conferenceId) { + push("is_active", data.state == State::Active); + push("is_missed", data.state == State::Missed); + } else { + push("discard_reason", [&] { + switch (data.state) { + case State::Busy: return "busy"; + case State::Disconnect: return "disconnect"; + case State::Hangup: return "hangup"; + case State::Missed: return "missed"; + case State::MigrateConferenceCall: + return "migrate_conference_all"; + } + return ""; + }()); + } }, [&](const ActionScreenshotTaken &data) { pushActor(); pushAction("take_screenshot"); @@ -674,12 +679,6 @@ QByteArray SerializeMessage( pushActor(); pushAction("paid_messages_price_change"); push("price_stars", data.stars); - }, [&](const ActionConferenceCall &data) { - pushActor(); - pushAction("conference_call"); - push("duration_seconds", data.duration); - push("is_missed", data.missed); - push("is_active", data.active); }, [](v::null_t) {}); if (v::is_null(message.action.content)) { diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 00bae0b60a..43a732f3e9 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -483,6 +483,12 @@ HistoryItem::HistoryItem( this, Data::ComputeCallData(data)); setTextValue({}); + }, [&](const MTPDmessageActionConferenceCall &data) { + createComponents(CreateConfig()); + _media = std::make_unique( + this, + Data::ComputeCallData(data)); + setTextValue({}); }, [&](const auto &) { createServiceFromMtp(data); }); @@ -5624,32 +5630,8 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) { return result; }; - auto prepareConferenceCall = [&](const MTPDmessageActionConferenceCall &action) { - auto result = PreparedServiceText(); - const auto duration = action.vduration().value_or_empty(); - result.text.text = action.is_missed() - ? tr::lng_action_confcall_missed(tr::now) - : action.is_active() - ? tr::lng_action_confcall_ongoing(tr::now) - : duration - ? tr::lng_action_confcall_finished(tr::now) - : tr::lng_action_confcall_invitation(tr::now); - - if (duration) { - result.text.text += " (" + QString::number(duration) + " seconds)"; - } - - const auto id = this->id; - setCustomServiceLink(std::make_shared([=]( - ClickContext context) { - const auto my = context.other.value(); - const auto weak = my.sessionWindow; - if (const auto strong = weak.get()) { - strong->resolveConferenceCall(id); - } - })); - - return result; + auto prepareConferenceCall = [&](const MTPDmessageActionConferenceCall &) -> PreparedServiceText { + Unexpected("PhoneCall type in setServiceMessageFromMtp."); }; setServiceText(action.match( diff --git a/Telegram/SourceFiles/history/view/media/history_view_call.cpp b/Telegram/SourceFiles/history/view/media/history_view_call.cpp index 954ce579ea..730975957f 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_call.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_call.cpp @@ -17,20 +17,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_element.h" #include "history/view/history_view_cursor_state.h" #include "core/application.h" +#include "core/click_handler_types.h" #include "calls/calls_instance.h" #include "data/data_media_types.h" #include "data/data_user.h" #include "main/main_session.h" +#include "window/window_session_controller.h" #include "styles/style_chat.h" namespace HistoryView { namespace { -using FinishReason = Data::CallFinishReason; +using State = Data::CallState; -[[nodiscard]] int ComputeDuration(FinishReason reason, int duration) { - return (reason != FinishReason::Missed - && reason != FinishReason::Busy) +[[nodiscard]] int ComputeDuration(State state, int duration) { + return (state != State::Missed && state != State::Busy) ? duration : 0; } @@ -41,11 +42,12 @@ Call::Call( not_null parent, not_null call) : Media(parent) -, _duration(ComputeDuration(call->finishReason, call->duration)) -, _reason(call->finishReason) +, _duration(ComputeDuration(call->state, call->duration)) +, _state(call->state) +, _conference(call->conferenceId != 0) , _video(call->video) { const auto item = parent->data(); - _text = Data::MediaCall::Text(item, _reason, _video); + _text = Data::MediaCall::Text(item, _state, _video); _status = QLocale().toString( parent->dateTime().time(), QLocale::ShortFormat); @@ -61,13 +63,20 @@ Call::Call( QSize Call::countOptimalSize() { const auto user = _parent->history()->peer->asUser(); + const auto conference = _conference; const auto video = _video; - _link = std::make_shared([=] { - if (user) { + const auto id = _parent->data()->id; + _link = std::make_shared([=](ClickContext context) { + if (conference) { + const auto my = context.other.value(); + const auto weak = my.sessionWindow; + if (const auto strong = weak.get()) { + strong->resolveConferenceCall(id); + } + } else if (user) { Core::App().calls().startOutgoingCall(user, video); } }); - auto maxWidth = st::historyCallWidth; auto minHeight = st::historyCallHeight; if (!isBubbleTop()) { @@ -96,8 +105,7 @@ void Call::draw(Painter &p, const PaintContext &context) const { p.drawTextLeft(nameleft, nametop, paintw, _text); auto statusleft = nameleft; - auto missed = (_reason == FinishReason::Missed) - || (_reason == FinishReason::Busy); + auto missed = (_state == State::Missed) || (_state == State::Busy); const auto &arrow = missed ? stm->historyCallArrowMissed : stm->historyCallArrow; diff --git a/Telegram/SourceFiles/history/view/media/history_view_call.h b/Telegram/SourceFiles/history/view/media/history_view_call.h index 8689b4ce96..891d7620b9 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_call.h +++ b/Telegram/SourceFiles/history/view/media/history_view_call.h @@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_media.h" namespace Data { -enum class CallFinishReason : char; +enum class CallState : char; struct Call; } // namespace Data @@ -40,12 +40,13 @@ public: } private: - using FinishReason = Data::CallFinishReason; + using State = Data::CallState; QSize countOptimalSize() override; const int _duration = 0; - const FinishReason _reason; + const State _state = {}; + const bool _conference = false; const bool _video = false; QString _text;