diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0aa42c3e9..38d08d5d3 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1929,6 +1929,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_you_are_live" = "You are Live"; "lng_group_call_force_muted" = "Muted by admin"; "lng_group_call_force_muted_sub" = "You are in Listen Only mode"; +"lng_group_call_raise_hand_tip" = "Click if you want to speak"; +"lng_group_call_raised_hand" = "You asked to speak"; +"lng_group_call_raised_hand_sub" = "We let the speakers know"; "lng_group_call_connecting" = "Connecting..."; "lng_group_call_leave" = "Leave"; "lng_group_call_leave_title" = "Leave voice chat"; diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index c542fa9fb..b9f88b4b1 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -173,7 +173,8 @@ GroupCall::GroupCall( if (_instance) { updateInstanceMuteState(); } - if (_mySsrc) { + if (_mySsrc && !_initialMuteStateSent) { + _initialMuteStateSent = true; maybeSendMutedUpdate(previous); } }, _lifetime); @@ -371,6 +372,7 @@ void GroupCall::rejoin() { } _mySsrc = 0; + _initialMuteStateSent = false; setState(State::Joining); ensureControllerCreated(); setInstanceMode(InstanceMode::None); @@ -464,7 +466,8 @@ void GroupCall::applyMeInCallLocally() { const auto volume = (i != end(participants)) ? i->volume : Group::kDefaultVolume; - const auto canSelfUnmute = (muted() != MuteState::ForceMuted); + const auto canSelfUnmute = (muted() != MuteState::ForceMuted) + && (muted() != MuteState::RaisedHand); const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0)) | (lastActive ? Flag::f_active_date : Flag(0)) | (_mySsrc ? Flag(0) : Flag::f_left) @@ -612,10 +615,12 @@ void GroupCall::setMuted(MuteState mute) { const auto set = [=] { const auto wasMuted = (muted() == MuteState::Muted) || (muted() == MuteState::PushToTalk); + const auto wasRaiseHand = (muted() == MuteState::RaisedHand); _muted = mute; const auto nowMuted = (muted() == MuteState::Muted) || (muted() == MuteState::PushToTalk); - if (wasMuted != nowMuted) { + const auto nowRaiseHand = (muted() == MuteState::RaisedHand); + if (wasMuted != nowMuted || wasRaiseHand != nowRaiseHand) { applyMeInCallLocally(); } }; @@ -626,6 +631,15 @@ void GroupCall::setMuted(MuteState mute) { } } +void GroupCall::setMutedAndUpdate(MuteState mute) { + const auto was = muted(); + const auto send = _initialMuteStateSent; + setMuted(mute); + if (send) { + maybeSendMutedUpdate(was); + } +} + void GroupCall::handleUpdate(const MTPGroupCall &call) { return call.match([&](const MTPDgroupCall &data) { if (_acceptFields) { @@ -814,12 +828,15 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) { hangup(); } if (data.is_muted() && !data.is_can_self_unmute()) { - setMuted(MuteState::ForceMuted); + setMuted(data.vraise_hand_rating().value_or_empty() + ? MuteState::RaisedHand + : MuteState::ForceMuted); } else if (_instanceMode == InstanceMode::Stream) { LOG(("Call Info: Rejoin after unforcemute in stream mode.")); setState(State::Joining); rejoin(); - } else if (muted() == MuteState::ForceMuted) { + } else if (muted() == MuteState::ForceMuted + || muted() == MuteState::RaisedHand) { setMuted(MuteState::Muted); } else if (data.is_muted() && muted() != MuteState::Muted) { setMuted(MuteState::Muted); @@ -1176,23 +1193,28 @@ void GroupCall::maybeSendMutedUpdate(MuteState previous) { const auto now = muted(); const auto wasActive = (previous == MuteState::Active); const auto nowActive = (now == MuteState::Active); - if (now == MuteState::ForceMuted - || previous == MuteState::ForceMuted - || (nowActive == wasActive)) { - return; + if ((wasActive && now == MuteState::Muted) + || (nowActive + && (previous == MuteState::Muted + || previous == MuteState::PushToTalk)) + || (now == MuteState::ForceMuted + && previous == MuteState::RaisedHand) + || (now == MuteState::RaisedHand + && previous == MuteState::ForceMuted)) { + sendMutedUpdate(); } - sendMutedUpdate(); } void GroupCall::sendMutedUpdate() { _api.request(_updateMuteRequestId).cancel(); using Flag = MTPphone_EditGroupCallParticipant::Flag; _updateMuteRequestId = _api.request(MTPphone_EditGroupCallParticipant( - MTP_flags((muted() != MuteState::Active) ? Flag::f_muted : Flag(0)), + MTP_flags(((muted() != MuteState::Active) ? Flag::f_muted : Flag(0)) + | Flag::f_raise_hand), inputCall(), _joinAs->input, MTP_int(100000), // volume - MTPBool() // #TODO calls raise_hand + MTP_bool(muted() == MuteState::RaisedHand) )).done([=](const MTPUpdates &result) { _updateMuteRequestId = 0; _peer->session().api().applyUpdates(result); @@ -1364,6 +1386,7 @@ void GroupCall::applyGlobalShortcutChanges() { void GroupCall::pushToTalk(bool pressed, crl::time delay) { if (muted() == MuteState::ForceMuted + || muted() == MuteState::RaisedHand || muted() == MuteState::Active) { return; } else if (pressed) { diff --git a/Telegram/SourceFiles/calls/calls_group_call.h b/Telegram/SourceFiles/calls/calls_group_call.h index d6f91aa81..f11a618f5 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.h +++ b/Telegram/SourceFiles/calls/calls_group_call.h @@ -50,6 +50,7 @@ enum class MuteState { PushToTalk, Muted, ForceMuted, + RaisedHand, }; [[nodiscard]] inline auto MapPushToTalkToActive() { @@ -120,6 +121,7 @@ public: void changeTitle(const QString &title); void setMuted(MuteState mute); + void setMutedAndUpdate(MuteState mute); [[nodiscard]] MuteState muted() const { return _muted.current(); } @@ -263,6 +265,7 @@ private: std::vector> _possibleJoinAs; rpl::variable _muted = MuteState::Muted; + bool _initialMuteStateSent = false; bool _acceptFields = false; rpl::event_stream _otherParticipantStateValue; diff --git a/Telegram/SourceFiles/calls/calls_group_panel.cpp b/Telegram/SourceFiles/calls/calls_group_panel.cpp index 4c9077ba2..9ec74ab8a 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_group_panel.cpp @@ -471,13 +471,15 @@ void GroupPanel::initControls() { ) | rpl::filter([=](Qt::MouseButton button) { return (button == Qt::LeftButton) && (_call != nullptr); }) | rpl::start_with_next([=] { - if (_call->muted() == MuteState::ForceMuted) { - _mute->shake(); - } else { - _call->setMuted((_call->muted() == MuteState::Muted) - ? MuteState::Active - : MuteState::Muted); - } + const auto oldState = _call->muted(); + const auto newState = (oldState == MuteState::ForceMuted) + ? MuteState::RaisedHand + : (oldState == MuteState::RaisedHand) + ? MuteState::ForceMuted + : (oldState == MuteState::Muted) + ? MuteState::Active + : MuteState::Muted; + _call->setMutedAndUpdate(newState); }, _mute->lifetime()); _hangup->setClickedCallback([=] { endCall(); }); @@ -562,13 +564,17 @@ void GroupPanel::initWithCall(GroupCall *call) { ? tr::lng_group_call_connecting(tr::now) : mute == MuteState::ForceMuted ? tr::lng_group_call_force_muted(tr::now) + : mute == MuteState::RaisedHand + ? tr::lng_group_call_raised_hand(tr::now) : mute == MuteState::Muted ? tr::lng_group_call_unmute(tr::now) : tr::lng_group_call_you_are_live(tr::now)), .subtext = (connecting ? QString() : mute == MuteState::ForceMuted - ? tr::lng_group_call_force_muted_sub(tr::now) + ? tr::lng_group_call_raise_hand_tip(tr::now) + : mute == MuteState::RaisedHand + ? tr::lng_group_call_raised_hand_sub(tr::now) : mute == MuteState::Muted ? tr::lng_group_call_unmute_sub(tr::now) : QString()), @@ -576,6 +582,8 @@ void GroupPanel::initWithCall(GroupCall *call) { ? Ui::CallMuteButtonType::Connecting : mute == MuteState::ForceMuted ? Ui::CallMuteButtonType::ForceMuted + : mute == MuteState::RaisedHand + ? Ui::CallMuteButtonType::RaisedHand : mute == MuteState::Muted ? Ui::CallMuteButtonType::Muted : Ui::CallMuteButtonType::Active), diff --git a/Telegram/SourceFiles/calls/calls_top_bar.cpp b/Telegram/SourceFiles/calls/calls_top_bar.cpp index e54ec8e5e..0f33e2b51 100644 --- a/Telegram/SourceFiles/calls/calls_top_bar.cpp +++ b/Telegram/SourceFiles/calls/calls_top_bar.cpp @@ -60,7 +60,7 @@ constexpr auto kBlobUpdateInterval = crl::time(100); auto BarStateFromMuteState(MuteState state, bool connecting) { return (connecting ? BarState::Connecting - : state == MuteState::ForceMuted + : (state == MuteState::ForceMuted || state == MuteState::RaisedHand) ? BarState::ForceMuted : state == MuteState::Muted ? BarState::Muted @@ -274,7 +274,8 @@ void TopBar::initControls() { if (const auto call = _call.get()) { call->setMuted(!call->muted()); } else if (const auto group = _groupCall.get()) { - if (group->muted() == MuteState::ForceMuted) { + if (group->muted() == MuteState::ForceMuted + || group->muted() == MuteState::RaisedHand) { Ui::Toast::Show(tr::lng_group_call_force_muted_sub(tr::now)); } else { group->setMuted((group->muted() == MuteState::Muted) diff --git a/Telegram/lib_ui_patch.patch b/Telegram/lib_ui_patch.patch new file mode 100644 index 000000000..e69de29bb