From 088fda4ed89d987c0f73a5d7b3f001e71e2afaf3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 5 Apr 2021 16:28:25 +0400 Subject: [PATCH] Correctly track mute button scheduled state. --- .../SourceFiles/calls/calls_group_call.cpp | 112 ++++++++++---- Telegram/SourceFiles/calls/calls_group_call.h | 8 + .../SourceFiles/calls/calls_group_members.cpp | 144 ++++++------------ .../SourceFiles/calls/calls_group_members.h | 2 +- .../SourceFiles/calls/calls_group_panel.cpp | 143 ++++++++++------- .../SourceFiles/calls/calls_group_panel.h | 5 +- Telegram/SourceFiles/data/data_group_call.cpp | 1 + Telegram/SourceFiles/data/data_group_call.h | 7 + .../SourceFiles/data/data_peer_values.cpp | 14 ++ Telegram/SourceFiles/data/data_peer_values.h | 1 + .../ui/controls/call_mute_button.cpp | 56 ++++--- .../ui/controls/call_mute_button.h | 3 + 12 files changed, 292 insertions(+), 204 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 36192d0bc..d7dbf8eff 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -184,6 +184,7 @@ GroupCall::GroupCall( , _joinAs(info.joinAs) , _possibleJoinAs(std::move(info.possibleJoinAs)) , _joinHash(info.joinHash) +, _id(inputCall.c_inputGroupCall().vid().v) , _scheduleDate(info.scheduleDate) , _lastSpokeCheckTimer([=] { checkLastSpoke(); }) , _checkJoinedTimer([=] { checkJoined(); }) @@ -216,14 +217,29 @@ GroupCall::GroupCall( checkGlobalShortcutAvailability(); - const auto id = inputCall.c_inputGroupCall().vid().v; - if (id) { - if (const auto call = _peer->groupCall(); call && call->id() == id) { - _scheduleDate = call->scheduleDate(); - if (!_peer->canManageGroupCall() && call->joinMuted()) { - _muted = MuteState::ForceMuted; - } + if (const auto real = lookupReal()) { + subscribeToReal(real); + if (!_peer->canManageGroupCall() && real->joinMuted()) { + _muted = MuteState::ForceMuted; } + } else { + _peer->session().changes().peerFlagsValue( + _peer, + Data::PeerUpdate::Flag::GroupCall + ) | rpl::map([=] { + return lookupReal(); + }) | rpl::filter([](Data::GroupCall *real) { + return real != nullptr; + }) | rpl::map([](Data::GroupCall *real) { + return not_null{ real }; + }) | rpl::take( + 1 + ) | rpl::start_with_next([=](not_null call) { + subscribeToReal(real); + _realChanges.fire_copy(call); + }, _lifetime); + } + if (_id) { join(inputCall); } else { start(info.scheduleDate); @@ -250,6 +266,17 @@ GroupCall::~GroupCall() { destroyController(); } +void GroupCall::subscribeToReal(not_null real) { + real->scheduleDateValue( + ) | rpl::start_with_next([=](TimeId date) { + const auto was = _scheduleDate; + _scheduleDate = date; + if (was && !date) { + join(inputCall()); + } + }, _lifetime); +} + void GroupCall::checkGlobalShortcutAvailability() { auto &settings = Core::App().settings(); if (!settings.groupCallPushToTalk()) { @@ -327,6 +354,18 @@ bool GroupCall::showChooseJoinAs() const { && !_possibleJoinAs.front()->isSelf()); } +Data::GroupCall *GroupCall::lookupReal() const { + const auto real = _peer->groupCall(); + return (real && real->id() == _id) ? real : nullptr; +} + +rpl::producer> GroupCall::real() const { + if (const auto real = lookupReal()) { + return rpl::single(not_null{ real }); + } + return _realChanges.events(); +} + void GroupCall::start(TimeId scheduleDate) { using Flag = MTPphone_CreateGroupCall::Flag; _createRequestId = _api.request(MTPphone_CreateGroupCall( @@ -699,6 +738,29 @@ void GroupCall::finish(FinishType type) { })).send(); } +void GroupCall::startScheduledNow() { + if (!lookupReal()) { + return; + } + _api.request(MTPphone_StartScheduledGroupCall( + inputCall() + )).done([=](const MTPUpdates &result) { + _peer->session().api().applyUpdates(result); + }).send(); +} + +void GroupCall::toggleScheduleStartSubscribed(bool subscribed) { + if (!lookupReal()) { + return; + } + _api.request(MTPphone_ToggleGroupCallStartSubscription( + inputCall(), + MTP_bool(subscribed) + )).done([=](const MTPUpdates &result) { + _peer->session().api().applyUpdates(result); + }).send(); +} + void GroupCall::setMuted(MuteState mute) { const auto set = [=] { const auto wasMuted = (muted() == MuteState::Muted) @@ -744,6 +806,8 @@ void GroupCall::handlePossibleCreateOrJoinResponse( const MTPDgroupCall &data) { if (const auto date = data.vschedule_date()) { _scheduleDate = date->v; + } else { + _scheduleDate = 0; } if (_acceptFields) { if (!_instance && !_id) { @@ -841,10 +905,8 @@ void GroupCall::handlePossibleDiscarded(const MTPDgroupCallDiscarded &data) { } void GroupCall::addParticipantsToInstance() { - const auto real = _peer->groupCall(); - if (!real - || (real->id() != _id) - || (_instanceMode == InstanceMode::None)) { + const auto real = lookupReal(); + if (!real || (_instanceMode == InstanceMode::None)) { return; } for (const auto &participant : real->participants()) { @@ -866,7 +928,7 @@ void GroupCall::addPreparedParticipants() { if (!_preparedParticipants.empty()) { _instance->addParticipants(base::take(_preparedParticipants)); } - if (const auto real = _peer->groupCall(); real && real->id() == _id) { + if (const auto real = lookupReal()) { if (!_unresolvedSsrcs.empty()) { real->resolveParticipants(base::take(_unresolvedSsrcs)); } @@ -989,8 +1051,8 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) { } void GroupCall::changeTitle(const QString &title) { - const auto real = _peer->groupCall(); - if (!real || real->id() != _id || real->title() == title) { + const auto real = lookupReal(); + if (!real || real->title() == title) { return; } @@ -1005,8 +1067,8 @@ void GroupCall::changeTitle(const QString &title) { } void GroupCall::toggleRecording(bool enabled, const QString &title) { - const auto real = _peer->groupCall(); - if (!real || real->id() != _id) { + const auto real = lookupReal(); + if (!real) { return; } @@ -1185,10 +1247,8 @@ void GroupCall::broadcastPartCancel(not_null task) { void GroupCall::requestParticipantsInformation( const std::vector &ssrcs) { - const auto real = _peer->groupCall(); - if (!real - || (real->id() != _id) - || (_instanceMode == InstanceMode::None)) { + const auto real = lookupReal(); + if (!real || (_instanceMode == InstanceMode::None)) { for (const auto ssrc : ssrcs) { _unresolvedSsrcs.emplace(ssrc); } @@ -1222,8 +1282,8 @@ void GroupCall::updateInstanceMuteState() { } void GroupCall::updateInstanceVolumes() { - const auto real = _peer->groupCall(); - if (!real || real->id() != _id) { + const auto real = lookupReal(); + if (!real) { return; } @@ -1299,8 +1359,8 @@ void GroupCall::audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data) { } void GroupCall::checkLastSpoke() { - const auto real = _peer->groupCall(); - if (!real || real->id() != _id) { + const auto real = lookupReal(); + if (!real) { return; } @@ -1511,8 +1571,8 @@ void GroupCall::editParticipant( std::variant> GroupCall::inviteUsers( const std::vector> &users) { - const auto real = _peer->groupCall(); - if (!real || real->id() != _id) { + const auto real = lookupReal(); + if (!real) { return 0; } const auto owner = &_peer->owner(); diff --git a/Telegram/SourceFiles/calls/calls_group_call.h b/Telegram/SourceFiles/calls/calls_group_call.h index 337542fd8..9be926cdd 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.h +++ b/Telegram/SourceFiles/calls/calls_group_call.h @@ -34,6 +34,7 @@ class MediaDevices; namespace Data { struct LastSpokeTimes; struct GroupCallParticipant; +class GroupCall; } // namespace Data namespace Calls { @@ -113,6 +114,9 @@ public: return _scheduleDate; } + [[nodiscard]] Data::GroupCall *lookupReal() const; + [[nodiscard]] rpl::producer> real() const; + void start(TimeId scheduleDate); void hangup(); void discard(); @@ -126,6 +130,8 @@ public: [[nodiscard]] bool recordingStoppedByMe() const { return _recordingStoppedByMe; } + void startScheduledNow(); + void toggleScheduleStartSubscribed(bool subscribed); void setMuted(MuteState mute); void setMutedAndUpdate(MuteState mute); @@ -249,6 +255,7 @@ private: void applyMeInCallLocally(); void rejoin(); void rejoin(not_null as); + void subscribeToReal(not_null real); void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data); void setInstanceConnected(tgcalls::GroupNetworkState networkState); @@ -288,6 +295,7 @@ private: rpl::event_stream _peerStream; not_null _history; // Can change in legacy group migration. MTP::Sender _api; + rpl::event_stream> _realChanges; rpl::variable _state = State::Creating; rpl::variable _instanceState = InstanceState::Disconnected; diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 2d1d29ba6..622f3801d 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -317,7 +317,7 @@ private: not_null participantPeer, bool participantIsCallAdmin, not_null row); - void setupListChangeViewers(not_null call); + void setupListChangeViewers(); void subscribeToChanges(not_null real); void updateRow( const std::optional &was, @@ -335,16 +335,11 @@ private: uint64 raiseHandRating) const; Row *findRow(not_null participantPeer) const; - [[nodiscard]] Data::GroupCall *resolvedRealCall() const; void appendInvitedUsers(); void scheduleRaisedHandStatusRemove(); - const base::weak_ptr _call; + const not_null _call; not_null _peer; - - // Use only resolvedRealCall() method, not this value directly. - Data::GroupCall *_realCallRawValue = nullptr; - uint64 _realId = 0; bool _prepared = false; rpl::event_stream _toggleMuteRequests; @@ -909,7 +904,7 @@ MembersController::MembersController( , _raisedHandStatusRemoveTimer([=] { scheduleRaisedHandStatusRemove(); }) , _inactiveCrossLine(st::groupCallMemberInactiveCrossLine) , _coloredCrossLine(st::groupCallMemberColoredCrossLine) { - setupListChangeViewers(call); + setupListChangeViewers(); style::PaletteChanged( ) | rpl::start_with_next([=] { @@ -964,32 +959,20 @@ MembersController::~MembersController() { base::take(_menu); } -void MembersController::setupListChangeViewers(not_null call) { - const auto peer = call->peer(); - peer->session().changes().peerFlagsValue( - peer, - Data::PeerUpdate::Flag::GroupCall - ) | rpl::map([=] { - return peer->groupCall(); - }) | rpl::filter([=](Data::GroupCall *real) { - const auto call = _call.get(); - return call && real && (real->id() == call->id()); - }) | rpl::take( - 1 +void MembersController::setupListChangeViewers() { + _call->real( ) | rpl::start_with_next([=](not_null real) { subscribeToChanges(real); }, _lifetime); - call->stateValue( + _call->stateValue( ) | rpl::start_with_next([=] { - const auto call = _call.get(); - const auto real = peer->groupCall(); - if (call && real && (real->id() == call->id())) { + if (const auto real = _call->lookupReal()) { //updateRow(channel->session().user()); } }, _lifetime); - call->levelUpdates( + _call->levelUpdates( ) | rpl::start_with_next([=](const LevelUpdate &update) { const auto i = _soundingRowBySsrc.find(update.ssrc); if (i != end(_soundingRowBySsrc)) { @@ -997,7 +980,7 @@ void MembersController::setupListChangeViewers(not_null call) { } }, _lifetime); - call->rejoinEvents( + _call->rejoinEvents( ) | rpl::start_with_next([=](const Group::RejoinEvent &event) { const auto guard = gsl::finally([&] { delegate()->peerListRefreshRows(); @@ -1014,9 +997,6 @@ void MembersController::setupListChangeViewers(not_null call) { } void MembersController::subscribeToChanges(not_null real) { - _realCallRawValue = real; - _realId = real->id(); - _fullCount = real->fullCountValue(); real->participantsSliceAdded( @@ -1053,17 +1033,19 @@ void MembersController::subscribeToChanges(not_null real) { } void MembersController::appendInvitedUsers() { - for (const auto user : _peer->owner().invitedToCallUsers(_realId)) { - if (auto row = createInvitedRow(user)) { - delegate()->peerListAppendRow(std::move(row)); + if (const auto id = _call->id()) { + for (const auto user : _peer->owner().invitedToCallUsers(id)) { + if (auto row = createInvitedRow(user)) { + delegate()->peerListAppendRow(std::move(row)); + } } + delegate()->peerListRefreshRows(); } - delegate()->peerListRefreshRows(); using Invite = Data::Session::InviteToCall; _peer->owner().invitesToCalls( ) | rpl::filter([=](const Invite &invite) { - return (invite.id == _realId); + return (invite.id == _call->id()); }) | rpl::start_with_next([=](const Invite &invite) { if (auto row = createInvitedRow(invite.user)) { delegate()->peerListAppendRow(std::move(row)); @@ -1120,7 +1102,7 @@ void MembersController::updateRow( if (checkPosition) { checkRowPosition(checkPosition); } else if (addedToBottom) { - const auto real = resolvedRealCall(); + const auto real = _call->lookupReal(); if (real && real->joinedToTop()) { const auto proj = [&](const PeerListRow &other) { const auto &real = static_cast(other); @@ -1314,14 +1296,6 @@ Row *MembersController::findRow(not_null participantPeer) const { delegate()->peerListFindRow(participantPeer->id)); } -Data::GroupCall *MembersController::resolvedRealCall() const { - return (_realCallRawValue - && (_peer->groupCall() == _realCallRawValue) - && (_realCallRawValue->id() == _realId)) - ? _realCallRawValue - : nullptr; -} - Main::Session &MembersController::session() const { return _call->peer()->session(); } @@ -1332,9 +1306,7 @@ void MembersController::prepare() { setDescriptionText(tr::lng_contacts_loading(tr::now)); setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now)); - const auto call = _call.get(); - if (const auto real = _peer->groupCall() - ; real && call && real->id() == call->id()) { + if (const auto real = _call->lookupReal()) { prepareRows(real); } else if (auto row = createRowForMe()) { delegate()->peerListAppendRow(std::move(row)); @@ -1342,15 +1314,12 @@ void MembersController::prepare() { } loadMoreRows(); - if (_realId) { - appendInvitedUsers(); - } + appendInvitedUsers(); _prepared = true; } bool MembersController::isMe(not_null participantPeer) const { - const auto call = _call.get(); - return call && (call->joinAs() == participantPeer); + return (_call->joinAs() == participantPeer); } void MembersController::prepareRows(not_null real) { @@ -1379,19 +1348,17 @@ void MembersController::prepareRows(not_null real) { } } if (!foundMe) { - if (const auto call = _call.get()) { - const auto me = call->joinAs(); - const auto i = ranges::find( - participants, - me, - &Data::GroupCall::Participant::peer); - auto row = (i != end(participants)) - ? createRow(*i) - : createRowForMe(); - if (row) { - changed = true; - delegate()->peerListAppendRow(std::move(row)); - } + const auto me = _call->joinAs(); + const auto i = ranges::find( + participants, + me, + &Data::GroupCall::Participant::peer); + auto row = (i != end(participants)) + ? createRow(*i) + : createRowForMe(); + if (row) { + changed = true; + delegate()->peerListAppendRow(std::move(row)); } } for (const auto &participant : participants) { @@ -1406,7 +1373,7 @@ void MembersController::prepareRows(not_null real) { } void MembersController::loadMoreRows() { - if (const auto real = _peer->groupCall()) { + if (const auto real = _call->lookupReal()) { real->requestParticipants(); } } @@ -1634,12 +1601,10 @@ base::unique_qptr MembersController::createRowContextMenu( } if (isMe(participantPeer)) { - if (const auto strong = _call.get() - ; strong && strong->muted() == MuteState::RaisedHand) { + if (_call->muted() == MuteState::RaisedHand) { const auto removeHand = [=] { - if (const auto strong = _call.get() - ; strong && strong->muted() == MuteState::RaisedHand) { - strong->setMutedAndUpdate(MuteState::ForceMuted); + if (_call->muted() == MuteState::RaisedHand) { + _call->setMutedAndUpdate(MuteState::ForceMuted); } }; result->addAction( @@ -1728,14 +1693,12 @@ void MembersController::addMuteActionsToContextMenu( auto mutesFromVolume = rpl::never() | rpl::type_erased(); - const auto call = _call.get(); - if (!isMuted || (call && call->joinAs() == participantPeer)) { - auto otherParticipantStateValue = call - ? call->otherParticipantStateValue( - ) | rpl::filter([=](const Group::ParticipantState &data) { - return data.peer == participantPeer; - }) - : rpl::never() | rpl::type_erased(); + if (!isMuted || _call->joinAs() == participantPeer) { + auto otherParticipantStateValue + = _call->otherParticipantStateValue( + ) | rpl::filter([=](const Group::ParticipantState &data) { + return data.peer == participantPeer; + }); auto volumeItem = base::make_unique_q( menu->menu(), @@ -1814,11 +1777,7 @@ void MembersController::addMuteActionsToContextMenu( } std::unique_ptr MembersController::createRowForMe() { - const auto call = _call.get(); - if (!call) { - return nullptr; - } - auto result = std::make_unique(this, call->joinAs()); + auto result = std::make_unique(this, _call->joinAs()); updateRow(result.get(), nullptr); return result; } @@ -1877,12 +1836,8 @@ auto Members::kickParticipantRequests() const int Members::desiredHeight() const { const auto top = _addMember ? _addMember->height() : 0; auto count = [&] { - if (const auto call = _call.get()) { - if (const auto real = call->peer()->groupCall()) { - if (call->id() == real->id()) { - return real->fullCount(); - } - } + if (const auto real = _call->lookupReal()) { + return real->fullCount(); } return 0; }(); @@ -1911,16 +1866,7 @@ void Members::setupAddMember(not_null call) { if (const auto channel = peer->asBroadcast()) { _canAddMembers = rpl::single( false - ) | rpl::then(peer->session().changes().peerFlagsValue( - peer, - Data::PeerUpdate::Flag::GroupCall - ) | rpl::map([=] { - return peer->groupCall(); - }) | rpl::filter([=](Data::GroupCall *real) { - const auto call = _call.get(); - return call && real && (real->id() == call->id()); - }) | rpl::take( - 1 + ) | rpl::then(_call->real( ) | rpl::map([=] { return Data::PeerFlagValue( channel, diff --git a/Telegram/SourceFiles/calls/calls_group_members.h b/Telegram/SourceFiles/calls/calls_group_members.h index 23dc33717..20064879c 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.h +++ b/Telegram/SourceFiles/calls/calls_group_members.h @@ -75,7 +75,7 @@ private: void updateControlsGeometry(); - const base::weak_ptr _call; + const not_null _call; object_ptr _scroll; std::unique_ptr _listController; object_ptr _addMember = { nullptr }; diff --git a/Telegram/SourceFiles/calls/calls_group_panel.cpp b/Telegram/SourceFiles/calls/calls_group_panel.cpp index c3b6b57d9..c4f61d352 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_group_panel.cpp @@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_group_call.h" #include "data/data_session.h" #include "data/data_changes.h" +#include "data/data_peer_values.h" #include "main/main_session.h" #include "base/event_filter.h" #include "boxes/peers/edit_participants_box.h" @@ -259,14 +260,17 @@ Panel::Panel(not_null call) _window->body(), st::groupCallTitle)) #endif // !Q_OS_MAC -, _scheduleDate(call->scheduleDate()) , _settings(widget(), st::groupCallSettings) , _mute(std::make_unique( widget(), Core::App().appDeactivatedValue(), Ui::CallMuteButtonState{ - .text = tr::lng_group_call_connecting(tr::now), - .type = Ui::CallMuteButtonType::Connecting, + .text = (_call->scheduleDate() + ? "Start Now" // #TODO voice chats + : tr::lng_group_call_connecting(tr::now)), + .type = (_call->scheduleDate() + ? Ui::CallMuteButtonType::ScheduledCanStart + : Ui::CallMuteButtonType::Connecting), })) , _hangup(widget(), st::groupCallHangup) { _layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox); @@ -277,7 +281,7 @@ Panel::Panel(not_null call) _peer, _window->lifetime(), [=](not_null channel) { migrate(channel); }); - setupRealCallViewers(call); + setupRealCallViewers(); initWindow(); initWidget(); @@ -295,17 +299,8 @@ Panel::~Panel() { } } -void Panel::setupRealCallViewers(not_null call) { - const auto peer = call->peer(); - peer->session().changes().peerFlagsValue( - peer, - Data::PeerUpdate::Flag::GroupCall - ) | rpl::map([=] { - return peer->groupCall(); - }) | rpl::filter([=](Data::GroupCall *real) { - return real && (real->id() == _call->id()); - }) | rpl::take( - 1 +void Panel::setupRealCallViewers() { + _call->real( ) | rpl::start_with_next([=](not_null real) { subscribeToChanges(real); }, _window->lifetime()); @@ -430,6 +425,15 @@ void Panel::initControls() { ) | rpl::filter([=](Qt::MouseButton button) { return (button == Qt::LeftButton); }) | rpl::start_with_next([=] { + if (_call->scheduleDate()) { + if (_peer->canManageGroupCall()) { + _call->startScheduledNow(); + } else if (const auto real = _call->lookupReal()) { + _call->toggleScheduleStartSubscribed( + !real->scheduleStartSubscribed()); + } + return; + } const auto oldState = _call->muted(); const auto newState = (oldState == MuteState::ForceMuted) ? MuteState::RaisedHand @@ -449,10 +453,6 @@ void Panel::initControls() { _settings->setText(tr::lng_group_call_settings()); _hangup->setText(tr::lng_group_call_leave()); - if (!_call->scheduleDate()) { - setupMembers(); - } - _call->stateValue( ) | rpl::filter([](State state) { return (state == State::HangingUp) @@ -470,18 +470,38 @@ void Panel::initControls() { _mute->setLevel(update.value); }, _callLifetime); + _call->real( + ) | rpl::start_with_next([=](not_null real) { + setupRealMuteButtonState(real); + }, _callLifetime); +} + +void Panel::setupRealMuteButtonState(not_null real) { using namespace rpl::mappers; rpl::combine( _call->mutedValue() | MapPushToTalkToActive(), - _call->instanceStateValue() + _call->instanceStateValue(), + real->scheduleDateValue(), + real->scheduleStartSubscribedValue(), + Data::CanManageGroupCallValue(_peer) ) | rpl::distinct_until_changed( ) | rpl::filter( _2 != GroupCall::InstanceState::TransitionToRtc ) | rpl::start_with_next([=]( MuteState mute, - GroupCall::InstanceState state) { + GroupCall::InstanceState state, + TimeId scheduleDate, + bool scheduleStartSubscribed, + bool canManage) { + using Type = Ui::CallMuteButtonType; _mute->setState(Ui::CallMuteButtonState{ - .text = (state == GroupCall::InstanceState::Disconnected + .text = (scheduleDate + ? (canManage + ? "Start Now" // #TODO voice chats + : scheduleStartSubscribed + ? "Cancel Reminder" + : "Set Reminder") + : state == GroupCall::InstanceState::Disconnected ? tr::lng_group_call_connecting(tr::now) : mute == MuteState::ForceMuted ? tr::lng_group_call_force_muted(tr::now) @@ -490,7 +510,9 @@ void Panel::initControls() { : mute == MuteState::Muted ? tr::lng_group_call_unmute(tr::now) : tr::lng_group_call_you_are_live(tr::now)), - .subtext = (state == GroupCall::InstanceState::Disconnected + .subtext = (scheduleDate + ? QString() + : state == GroupCall::InstanceState::Disconnected ? QString() : mute == MuteState::ForceMuted ? tr::lng_group_call_raise_hand_tip(tr::now) @@ -499,15 +521,21 @@ void Panel::initControls() { : mute == MuteState::Muted ? tr::lng_group_call_unmute_sub(tr::now) : QString()), - .type = (state == GroupCall::InstanceState::Disconnected - ? Ui::CallMuteButtonType::Connecting + .type = (scheduleDate + ? (canManage + ? Type::ScheduledCanStart + : scheduleStartSubscribed + ? Type::ScheduledNotify + : Type::ScheduledSilent) + : state == GroupCall::InstanceState::Disconnected + ? Type::Connecting : mute == MuteState::ForceMuted - ? Ui::CallMuteButtonType::ForceMuted + ? Type::ForceMuted : mute == MuteState::RaisedHand - ? Ui::CallMuteButtonType::RaisedHand + ? Type::RaisedHand : mute == MuteState::Muted - ? Ui::CallMuteButtonType::Muted - : Ui::CallMuteButtonType::Active), + ? Type::Muted + : Type::Active), }); }, _callLifetime); } @@ -590,8 +618,7 @@ void Panel::setupJoinAsChangedToasts() { void Panel::setupTitleChangedToasts() { _call->titleChanged( ) | rpl::filter([=] { - const auto real = _peer->groupCall(); - return real && (real->id() == _call->id()); + return (_call->lookupReal() != nullptr); }) | rpl::map([=] { return _peer->groupCall()->title().isEmpty() ? _peer->name @@ -617,10 +644,8 @@ void Panel::setupAllowedToSpeakToasts() { .text = { tr::lng_group_call_can_speak_here(tr::now) }, }); } else { - const auto real = _peer->groupCall(); - const auto name = (real - && (real->id() == _call->id()) - && !real->title().isEmpty()) + const auto real = _call->lookupReal(); + const auto name = (real && !real->title().isEmpty()) ? real->title() : _peer->name; Ui::ShowMultilineToast({ @@ -635,18 +660,6 @@ void Panel::setupAllowedToSpeakToasts() { } void Panel::subscribeToChanges(not_null real) { - if (!_members) { - real->scheduleDateValue( - ) | rpl::filter([=](TimeId scheduleDate) { - return !scheduleDate; - }) | rpl::take(1) | rpl::start_with_next([=] { - setupMembers(); - }, _callLifetime); - } - - _titleText = real->titleValue(); - _scheduleDate = real->scheduleDateValue(); - const auto validateRecordingMark = [=](bool recording) { if (!recording && _recordingMark) { _recordingMark.destroy(); @@ -823,8 +836,8 @@ void Panel::showMainMenu() { } void Panel::addMembers() { - const auto real = _peer->groupCall(); - if (!real || real->id() != _call->id()) { + const auto real = _call->lookupReal(); + if (!real) { return; } auto alreadyIn = _peer->owner().invitedToCallUsers(real->id()); @@ -1135,7 +1148,12 @@ void Panel::refreshTitle() { if (!_title) { auto text = rpl::combine( Info::Profile::NameValue(_peer), - _titleText.value() + rpl::single( + QString() + ) | rpl::then(_call->real( + ) | rpl::map([=](not_null real) { + return real->titleValue(); + }) | rpl::flatten_latest()) ) | rpl::map([=]( const TextWithEntities &name, const QString &title) { @@ -1154,15 +1172,24 @@ void Panel::refreshTitle() { if (!_subtitle) { _subtitle.create( widget(), - _scheduleDate.value( + rpl::single( + _call->scheduleDate() + ) | rpl::then( + _call->real( + ) | rpl::map([=](not_null real) { + return real->scheduleDateValue(); + }) | rpl::flatten_latest() ) | rpl::map([=](TimeId scheduleDate) { - return scheduleDate - ? tr::lng_group_call_scheduled_status() - : tr::lng_group_call_members( - lt_count_decimal, - _members->fullCountValue() | rpl::map([](int value) { - return (value > 0) ? float64(value) : 1.; - })); + if (scheduleDate) { + return tr::lng_group_call_scheduled_status(); + } else if (!_members) { + setupMembers(); + } + return tr::lng_group_call_members( + lt_count_decimal, + _members->fullCountValue() | rpl::map([](int value) { + return (value > 0) ? float64(value) : 1.; + })); }) | rpl::flatten_latest(), st::groupCallSubtitleLabel); _subtitle->show(); diff --git a/Telegram/SourceFiles/calls/calls_group_panel.h b/Telegram/SourceFiles/calls/calls_group_panel.h index 6d0c04f6c..493245f7b 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.h +++ b/Telegram/SourceFiles/calls/calls_group_panel.h @@ -79,6 +79,7 @@ private: void setupJoinAsChangedToasts(); void setupTitleChangedToasts(); void setupAllowedToSpeakToasts(); + void setupRealMuteButtonState(not_null real); bool handleClose(); @@ -96,7 +97,7 @@ private: [[nodiscard]] QRect computeTitleRect() const; void refreshTitle(); void refreshTitleGeometry(); - void setupRealCallViewers(not_null call); + void setupRealCallViewers(); void subscribeToChanges(not_null real); void migrate(not_null channel); @@ -121,8 +122,6 @@ private: object_ptr _menu = { nullptr }; object_ptr _joinAsToggle = { nullptr }; object_ptr _members = { nullptr }; - rpl::variable _titleText; - rpl::variable _scheduleDate; ChooseJoinAsProcess _joinAsProcess; object_ptr _settings; diff --git a/Telegram/SourceFiles/data/data_group_call.cpp b/Telegram/SourceFiles/data/data_group_call.cpp index 33ba9085e..c646993f0 100644 --- a/Telegram/SourceFiles/data/data_group_call.cpp +++ b/Telegram/SourceFiles/data/data_group_call.cpp @@ -330,6 +330,7 @@ void GroupCall::applyCallFields(const MTPDgroupCall &data) { _title = qs(data.vtitle().value_or_empty()); _recordStartDate = data.vrecord_start_date().value_or_empty(); _scheduleDate = data.vschedule_date().value_or_empty(); + _scheduleStartSubscribed = data.is_schedule_start_subscribed(); _allParticipantsLoaded = (_serverParticipantsCount == _participants.size()); } diff --git a/Telegram/SourceFiles/data/data_group_call.h b/Telegram/SourceFiles/data/data_group_call.h index 71b77fda4..3742ddedc 100644 --- a/Telegram/SourceFiles/data/data_group_call.h +++ b/Telegram/SourceFiles/data/data_group_call.h @@ -72,6 +72,12 @@ public: [[nodiscard]] rpl::producer scheduleDateChanges() const { return _scheduleDate.changes(); } + [[nodiscard]] bool scheduleStartSubscribed() const { + return _scheduleStartSubscribed.current(); + } + [[nodiscard]] rpl::producer scheduleStartSubscribedValue() const { + return _scheduleStartSubscribed.value(); + } void setPeer(not_null peer); @@ -173,6 +179,7 @@ private: rpl::variable _fullCount = 0; rpl::variable _recordStartDate = 0; rpl::variable _scheduleDate = 0; + rpl::variable _scheduleStartSubscribed = false; base::flat_map _unknownSpokenSsrcs; base::flat_map _unknownSpokenPeerIds; diff --git a/Telegram/SourceFiles/data/data_peer_values.cpp b/Telegram/SourceFiles/data/data_peer_values.cpp index 606a5d757..74086aeed 100644 --- a/Telegram/SourceFiles/data/data_peer_values.cpp +++ b/Telegram/SourceFiles/data/data_peer_values.cpp @@ -320,6 +320,20 @@ rpl::producer CanPinMessagesValue(not_null peer) { Unexpected("Peer type in CanPinMessagesValue."); } +rpl::producer CanManageGroupCallValue(not_null peer) { + const auto flag = MTPDchatAdminRights::Flag::f_manage_call; + if (const auto chat = peer->asChat()) { + return chat->amCreator() + ? (rpl::single(true) | rpl::type_erased()) + : AdminRightValue(chat, flag); + } else if (const auto channel = peer->asChannel()) { + return channel->amCreator() + ? (rpl::single(true) | rpl::type_erased()) + : AdminRightValue(channel, flag); + } + return rpl::single(false); +} + TimeId SortByOnlineValue(not_null user, TimeId now) { if (user->isServiceUser() || user->isBot()) { return -1; diff --git a/Telegram/SourceFiles/data/data_peer_values.h b/Telegram/SourceFiles/data/data_peer_values.h index 04d12b391..24605bf07 100644 --- a/Telegram/SourceFiles/data/data_peer_values.h +++ b/Telegram/SourceFiles/data/data_peer_values.h @@ -111,6 +111,7 @@ inline auto PeerFullFlagValue( [[nodiscard]] rpl::producer CanWriteValue(ChannelData *channel); [[nodiscard]] rpl::producer CanWriteValue(not_null peer); [[nodiscard]] rpl::producer CanPinMessagesValue(not_null peer); +[[nodiscard]] rpl::producer CanManageGroupCallValue(not_null peer); [[nodiscard]] TimeId SortByOnlineValue(not_null user, TimeId now); [[nodiscard]] crl::time OnlineChangeTimeout(TimeId online, TimeId now); diff --git a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp index 7fcc1d878..78ee4f9c1 100644 --- a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp +++ b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp @@ -102,21 +102,7 @@ auto MuteBlobs() { auto Colors() { using Vector = std::vector; using Colors = anim::gradient_colors; - return base::flat_map{ - { - CallMuteButtonType::ForceMuted, - Colors(QGradientStops{ - { .0, st::groupCallForceMuted3->c }, - { .5, st::groupCallForceMuted2->c }, - { 1., st::groupCallForceMuted1->c } }) - }, - { - CallMuteButtonType::RaisedHand, - Colors(QGradientStops{ - { .0, st::groupCallForceMuted3->c }, - { .5, st::groupCallForceMuted2->c }, - { 1., st::groupCallForceMuted1->c } }) - }, + auto result = base::flat_map{ { CallMuteButtonType::Active, Colors(Vector{ st::groupCallLive1->c, st::groupCallLive2->c }) @@ -130,6 +116,21 @@ auto Colors() { Colors(Vector{ st::groupCallMuted1->c, st::groupCallMuted2->c }) }, }; + const auto forceMutedColors = Colors(QGradientStops{ + { .0, st::groupCallForceMuted3->c }, + { .5, st::groupCallForceMuted2->c }, + { 1., st::groupCallForceMuted1->c } }); + const auto forceMutedTypes = { + CallMuteButtonType::ForceMuted, + CallMuteButtonType::RaisedHand, + CallMuteButtonType::ScheduledCanStart, + CallMuteButtonType::ScheduledNotify, + CallMuteButtonType::ScheduledSilent, + }; + for (const auto type : forceMutedTypes) { + result.emplace(type, forceMutedColors); + } + return result; } bool IsMuted(CallMuteButtonType type) { @@ -143,7 +144,10 @@ bool IsConnecting(CallMuteButtonType type) { bool IsInactive(CallMuteButtonType type) { return IsConnecting(type) || (type == CallMuteButtonType::ForceMuted) - || (type == CallMuteButtonType::RaisedHand); + || (type == CallMuteButtonType::RaisedHand) + || (type == CallMuteButtonType::ScheduledCanStart) + || (type == CallMuteButtonType::ScheduledNotify) + || (type == CallMuteButtonType::ScheduledSilent); } auto Clamp(float64 value) { @@ -585,6 +589,9 @@ CallMuteButton::IconState CallMuteButton::iconStateFrom( .frameTo = 62, }; } break; + case CallMuteButtonType::ScheduledCanStart: // #TODO voice chats + case CallMuteButtonType::ScheduledNotify: + case CallMuteButtonType::ScheduledSilent: case CallMuteButtonType::ForceMuted: case CallMuteButtonType::RaisedHand: { return { // Active -> Hand @@ -615,6 +622,9 @@ CallMuteButton::IconState CallMuteButton::iconStateFrom( .frameTo = 22, }; } break; + case CallMuteButtonType::ScheduledCanStart: // #TODO voice chats + case CallMuteButtonType::ScheduledNotify: + case CallMuteButtonType::ScheduledSilent: case CallMuteButtonType::ForceMuted: case CallMuteButtonType::RaisedHand: { return { // Muted -> Hand @@ -626,6 +636,9 @@ CallMuteButton::IconState CallMuteButton::iconStateFrom( } break; } } break; + case CallMuteButtonType::ScheduledCanStart: // #TODO voice chats + case CallMuteButtonType::ScheduledNotify: + case CallMuteButtonType::ScheduledSilent: case CallMuteButtonType::ForceMuted: case CallMuteButtonType::RaisedHand: { switch (current) { @@ -645,6 +658,9 @@ CallMuteButton::IconState CallMuteButton::iconStateFrom( .frameTo = 20, }; } break; + case CallMuteButtonType::ScheduledCanStart: // #TODO voice chats + case CallMuteButtonType::ScheduledNotify: + case CallMuteButtonType::ScheduledSilent: case CallMuteButtonType::ForceMuted: case CallMuteButtonType::RaisedHand: { return { // Hand @@ -1015,6 +1031,9 @@ CallMuteButton::HandleMouseState CallMuteButton::HandleMouseStateFromType( return HandleMouseState::Enabled; case CallMuteButtonType::Connecting: return HandleMouseState::Disabled; + case CallMuteButtonType::ScheduledCanStart: + case CallMuteButtonType::ScheduledNotify: + case CallMuteButtonType::ScheduledSilent: case CallMuteButtonType::ForceMuted: case CallMuteButtonType::RaisedHand: return HandleMouseState::Enabled; @@ -1098,7 +1117,10 @@ void CallMuteButton::overridesColors( float64 progress) { const auto forceMutedToConnecting = [](CallMuteButtonType &type) { if (type == CallMuteButtonType::ForceMuted - || type == CallMuteButtonType::RaisedHand) { + || type == CallMuteButtonType::RaisedHand + || type == CallMuteButtonType::ScheduledCanStart + || type == CallMuteButtonType::ScheduledNotify + || type == CallMuteButtonType::ScheduledSilent) { type = CallMuteButtonType::Connecting; } }; diff --git a/Telegram/SourceFiles/ui/controls/call_mute_button.h b/Telegram/SourceFiles/ui/controls/call_mute_button.h index d5737787d..7a8a0f4c9 100644 --- a/Telegram/SourceFiles/ui/controls/call_mute_button.h +++ b/Telegram/SourceFiles/ui/controls/call_mute_button.h @@ -30,6 +30,9 @@ enum class CallMuteButtonType { Muted, ForceMuted, RaisedHand, + ScheduledCanStart, + ScheduledSilent, + ScheduledNotify, }; struct CallMuteButtonState {