Correctly track mute button scheduled state.

This commit is contained in:
John Preston 2021-04-05 16:28:25 +04:00
parent 15d17c8b0e
commit 088fda4ed8
12 changed files with 292 additions and 204 deletions

View File

@ -184,6 +184,7 @@ GroupCall::GroupCall(
, _joinAs(info.joinAs) , _joinAs(info.joinAs)
, _possibleJoinAs(std::move(info.possibleJoinAs)) , _possibleJoinAs(std::move(info.possibleJoinAs))
, _joinHash(info.joinHash) , _joinHash(info.joinHash)
, _id(inputCall.c_inputGroupCall().vid().v)
, _scheduleDate(info.scheduleDate) , _scheduleDate(info.scheduleDate)
, _lastSpokeCheckTimer([=] { checkLastSpoke(); }) , _lastSpokeCheckTimer([=] { checkLastSpoke(); })
, _checkJoinedTimer([=] { checkJoined(); }) , _checkJoinedTimer([=] { checkJoined(); })
@ -216,14 +217,29 @@ GroupCall::GroupCall(
checkGlobalShortcutAvailability(); checkGlobalShortcutAvailability();
const auto id = inputCall.c_inputGroupCall().vid().v; if (const auto real = lookupReal()) {
if (id) { subscribeToReal(real);
if (const auto call = _peer->groupCall(); call && call->id() == id) { if (!_peer->canManageGroupCall() && real->joinMuted()) {
_scheduleDate = call->scheduleDate(); _muted = MuteState::ForceMuted;
if (!_peer->canManageGroupCall() && call->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<Data::GroupCall*> call) {
subscribeToReal(real);
_realChanges.fire_copy(call);
}, _lifetime);
}
if (_id) {
join(inputCall); join(inputCall);
} else { } else {
start(info.scheduleDate); start(info.scheduleDate);
@ -250,6 +266,17 @@ GroupCall::~GroupCall() {
destroyController(); destroyController();
} }
void GroupCall::subscribeToReal(not_null<Data::GroupCall*> real) {
real->scheduleDateValue(
) | rpl::start_with_next([=](TimeId date) {
const auto was = _scheduleDate;
_scheduleDate = date;
if (was && !date) {
join(inputCall());
}
}, _lifetime);
}
void GroupCall::checkGlobalShortcutAvailability() { void GroupCall::checkGlobalShortcutAvailability() {
auto &settings = Core::App().settings(); auto &settings = Core::App().settings();
if (!settings.groupCallPushToTalk()) { if (!settings.groupCallPushToTalk()) {
@ -327,6 +354,18 @@ bool GroupCall::showChooseJoinAs() const {
&& !_possibleJoinAs.front()->isSelf()); && !_possibleJoinAs.front()->isSelf());
} }
Data::GroupCall *GroupCall::lookupReal() const {
const auto real = _peer->groupCall();
return (real && real->id() == _id) ? real : nullptr;
}
rpl::producer<not_null<Data::GroupCall*>> GroupCall::real() const {
if (const auto real = lookupReal()) {
return rpl::single(not_null{ real });
}
return _realChanges.events();
}
void GroupCall::start(TimeId scheduleDate) { void GroupCall::start(TimeId scheduleDate) {
using Flag = MTPphone_CreateGroupCall::Flag; using Flag = MTPphone_CreateGroupCall::Flag;
_createRequestId = _api.request(MTPphone_CreateGroupCall( _createRequestId = _api.request(MTPphone_CreateGroupCall(
@ -699,6 +738,29 @@ void GroupCall::finish(FinishType type) {
})).send(); })).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) { void GroupCall::setMuted(MuteState mute) {
const auto set = [=] { const auto set = [=] {
const auto wasMuted = (muted() == MuteState::Muted) const auto wasMuted = (muted() == MuteState::Muted)
@ -744,6 +806,8 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
const MTPDgroupCall &data) { const MTPDgroupCall &data) {
if (const auto date = data.vschedule_date()) { if (const auto date = data.vschedule_date()) {
_scheduleDate = date->v; _scheduleDate = date->v;
} else {
_scheduleDate = 0;
} }
if (_acceptFields) { if (_acceptFields) {
if (!_instance && !_id) { if (!_instance && !_id) {
@ -841,10 +905,8 @@ void GroupCall::handlePossibleDiscarded(const MTPDgroupCallDiscarded &data) {
} }
void GroupCall::addParticipantsToInstance() { void GroupCall::addParticipantsToInstance() {
const auto real = _peer->groupCall(); const auto real = lookupReal();
if (!real if (!real || (_instanceMode == InstanceMode::None)) {
|| (real->id() != _id)
|| (_instanceMode == InstanceMode::None)) {
return; return;
} }
for (const auto &participant : real->participants()) { for (const auto &participant : real->participants()) {
@ -866,7 +928,7 @@ void GroupCall::addPreparedParticipants() {
if (!_preparedParticipants.empty()) { if (!_preparedParticipants.empty()) {
_instance->addParticipants(base::take(_preparedParticipants)); _instance->addParticipants(base::take(_preparedParticipants));
} }
if (const auto real = _peer->groupCall(); real && real->id() == _id) { if (const auto real = lookupReal()) {
if (!_unresolvedSsrcs.empty()) { if (!_unresolvedSsrcs.empty()) {
real->resolveParticipants(base::take(_unresolvedSsrcs)); real->resolveParticipants(base::take(_unresolvedSsrcs));
} }
@ -989,8 +1051,8 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
} }
void GroupCall::changeTitle(const QString &title) { void GroupCall::changeTitle(const QString &title) {
const auto real = _peer->groupCall(); const auto real = lookupReal();
if (!real || real->id() != _id || real->title() == title) { if (!real || real->title() == title) {
return; return;
} }
@ -1005,8 +1067,8 @@ void GroupCall::changeTitle(const QString &title) {
} }
void GroupCall::toggleRecording(bool enabled, const QString &title) { void GroupCall::toggleRecording(bool enabled, const QString &title) {
const auto real = _peer->groupCall(); const auto real = lookupReal();
if (!real || real->id() != _id) { if (!real) {
return; return;
} }
@ -1185,10 +1247,8 @@ void GroupCall::broadcastPartCancel(not_null<LoadPartTask*> task) {
void GroupCall::requestParticipantsInformation( void GroupCall::requestParticipantsInformation(
const std::vector<uint32_t> &ssrcs) { const std::vector<uint32_t> &ssrcs) {
const auto real = _peer->groupCall(); const auto real = lookupReal();
if (!real if (!real || (_instanceMode == InstanceMode::None)) {
|| (real->id() != _id)
|| (_instanceMode == InstanceMode::None)) {
for (const auto ssrc : ssrcs) { for (const auto ssrc : ssrcs) {
_unresolvedSsrcs.emplace(ssrc); _unresolvedSsrcs.emplace(ssrc);
} }
@ -1222,8 +1282,8 @@ void GroupCall::updateInstanceMuteState() {
} }
void GroupCall::updateInstanceVolumes() { void GroupCall::updateInstanceVolumes() {
const auto real = _peer->groupCall(); const auto real = lookupReal();
if (!real || real->id() != _id) { if (!real) {
return; return;
} }
@ -1299,8 +1359,8 @@ void GroupCall::audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data) {
} }
void GroupCall::checkLastSpoke() { void GroupCall::checkLastSpoke() {
const auto real = _peer->groupCall(); const auto real = lookupReal();
if (!real || real->id() != _id) { if (!real) {
return; return;
} }
@ -1511,8 +1571,8 @@ void GroupCall::editParticipant(
std::variant<int, not_null<UserData*>> GroupCall::inviteUsers( std::variant<int, not_null<UserData*>> GroupCall::inviteUsers(
const std::vector<not_null<UserData*>> &users) { const std::vector<not_null<UserData*>> &users) {
const auto real = _peer->groupCall(); const auto real = lookupReal();
if (!real || real->id() != _id) { if (!real) {
return 0; return 0;
} }
const auto owner = &_peer->owner(); const auto owner = &_peer->owner();

View File

@ -34,6 +34,7 @@ class MediaDevices;
namespace Data { namespace Data {
struct LastSpokeTimes; struct LastSpokeTimes;
struct GroupCallParticipant; struct GroupCallParticipant;
class GroupCall;
} // namespace Data } // namespace Data
namespace Calls { namespace Calls {
@ -113,6 +114,9 @@ public:
return _scheduleDate; return _scheduleDate;
} }
[[nodiscard]] Data::GroupCall *lookupReal() const;
[[nodiscard]] rpl::producer<not_null<Data::GroupCall*>> real() const;
void start(TimeId scheduleDate); void start(TimeId scheduleDate);
void hangup(); void hangup();
void discard(); void discard();
@ -126,6 +130,8 @@ public:
[[nodiscard]] bool recordingStoppedByMe() const { [[nodiscard]] bool recordingStoppedByMe() const {
return _recordingStoppedByMe; return _recordingStoppedByMe;
} }
void startScheduledNow();
void toggleScheduleStartSubscribed(bool subscribed);
void setMuted(MuteState mute); void setMuted(MuteState mute);
void setMutedAndUpdate(MuteState mute); void setMutedAndUpdate(MuteState mute);
@ -249,6 +255,7 @@ private:
void applyMeInCallLocally(); void applyMeInCallLocally();
void rejoin(); void rejoin();
void rejoin(not_null<PeerData*> as); void rejoin(not_null<PeerData*> as);
void subscribeToReal(not_null<Data::GroupCall*> real);
void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data); void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data);
void setInstanceConnected(tgcalls::GroupNetworkState networkState); void setInstanceConnected(tgcalls::GroupNetworkState networkState);
@ -288,6 +295,7 @@ private:
rpl::event_stream<PeerData*> _peerStream; rpl::event_stream<PeerData*> _peerStream;
not_null<History*> _history; // Can change in legacy group migration. not_null<History*> _history; // Can change in legacy group migration.
MTP::Sender _api; MTP::Sender _api;
rpl::event_stream<not_null<Data::GroupCall*>> _realChanges;
rpl::variable<State> _state = State::Creating; rpl::variable<State> _state = State::Creating;
rpl::variable<InstanceState> _instanceState rpl::variable<InstanceState> _instanceState
= InstanceState::Disconnected; = InstanceState::Disconnected;

View File

@ -317,7 +317,7 @@ private:
not_null<PeerData*> participantPeer, not_null<PeerData*> participantPeer,
bool participantIsCallAdmin, bool participantIsCallAdmin,
not_null<Row*> row); not_null<Row*> row);
void setupListChangeViewers(not_null<GroupCall*> call); void setupListChangeViewers();
void subscribeToChanges(not_null<Data::GroupCall*> real); void subscribeToChanges(not_null<Data::GroupCall*> real);
void updateRow( void updateRow(
const std::optional<Data::GroupCall::Participant> &was, const std::optional<Data::GroupCall::Participant> &was,
@ -335,16 +335,11 @@ private:
uint64 raiseHandRating) const; uint64 raiseHandRating) const;
Row *findRow(not_null<PeerData*> participantPeer) const; Row *findRow(not_null<PeerData*> participantPeer) const;
[[nodiscard]] Data::GroupCall *resolvedRealCall() const;
void appendInvitedUsers(); void appendInvitedUsers();
void scheduleRaisedHandStatusRemove(); void scheduleRaisedHandStatusRemove();
const base::weak_ptr<GroupCall> _call; const not_null<GroupCall*> _call;
not_null<PeerData*> _peer; not_null<PeerData*> _peer;
// Use only resolvedRealCall() method, not this value directly.
Data::GroupCall *_realCallRawValue = nullptr;
uint64 _realId = 0;
bool _prepared = false; bool _prepared = false;
rpl::event_stream<MuteRequest> _toggleMuteRequests; rpl::event_stream<MuteRequest> _toggleMuteRequests;
@ -909,7 +904,7 @@ MembersController::MembersController(
, _raisedHandStatusRemoveTimer([=] { scheduleRaisedHandStatusRemove(); }) , _raisedHandStatusRemoveTimer([=] { scheduleRaisedHandStatusRemove(); })
, _inactiveCrossLine(st::groupCallMemberInactiveCrossLine) , _inactiveCrossLine(st::groupCallMemberInactiveCrossLine)
, _coloredCrossLine(st::groupCallMemberColoredCrossLine) { , _coloredCrossLine(st::groupCallMemberColoredCrossLine) {
setupListChangeViewers(call); setupListChangeViewers();
style::PaletteChanged( style::PaletteChanged(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
@ -964,32 +959,20 @@ MembersController::~MembersController() {
base::take(_menu); base::take(_menu);
} }
void MembersController::setupListChangeViewers(not_null<GroupCall*> call) { void MembersController::setupListChangeViewers() {
const auto peer = call->peer(); _call->real(
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::start_with_next([=](not_null<Data::GroupCall*> real) { ) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) {
subscribeToChanges(real); subscribeToChanges(real);
}, _lifetime); }, _lifetime);
call->stateValue( _call->stateValue(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
const auto call = _call.get(); if (const auto real = _call->lookupReal()) {
const auto real = peer->groupCall();
if (call && real && (real->id() == call->id())) {
//updateRow(channel->session().user()); //updateRow(channel->session().user());
} }
}, _lifetime); }, _lifetime);
call->levelUpdates( _call->levelUpdates(
) | rpl::start_with_next([=](const LevelUpdate &update) { ) | rpl::start_with_next([=](const LevelUpdate &update) {
const auto i = _soundingRowBySsrc.find(update.ssrc); const auto i = _soundingRowBySsrc.find(update.ssrc);
if (i != end(_soundingRowBySsrc)) { if (i != end(_soundingRowBySsrc)) {
@ -997,7 +980,7 @@ void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
} }
}, _lifetime); }, _lifetime);
call->rejoinEvents( _call->rejoinEvents(
) | rpl::start_with_next([=](const Group::RejoinEvent &event) { ) | rpl::start_with_next([=](const Group::RejoinEvent &event) {
const auto guard = gsl::finally([&] { const auto guard = gsl::finally([&] {
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
@ -1014,9 +997,6 @@ void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
} }
void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) { void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
_realCallRawValue = real;
_realId = real->id();
_fullCount = real->fullCountValue(); _fullCount = real->fullCountValue();
real->participantsSliceAdded( real->participantsSliceAdded(
@ -1053,17 +1033,19 @@ void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
} }
void MembersController::appendInvitedUsers() { void MembersController::appendInvitedUsers() {
for (const auto user : _peer->owner().invitedToCallUsers(_realId)) { if (const auto id = _call->id()) {
if (auto row = createInvitedRow(user)) { for (const auto user : _peer->owner().invitedToCallUsers(id)) {
delegate()->peerListAppendRow(std::move(row)); if (auto row = createInvitedRow(user)) {
delegate()->peerListAppendRow(std::move(row));
}
} }
delegate()->peerListRefreshRows();
} }
delegate()->peerListRefreshRows();
using Invite = Data::Session::InviteToCall; using Invite = Data::Session::InviteToCall;
_peer->owner().invitesToCalls( _peer->owner().invitesToCalls(
) | rpl::filter([=](const Invite &invite) { ) | rpl::filter([=](const Invite &invite) {
return (invite.id == _realId); return (invite.id == _call->id());
}) | rpl::start_with_next([=](const Invite &invite) { }) | rpl::start_with_next([=](const Invite &invite) {
if (auto row = createInvitedRow(invite.user)) { if (auto row = createInvitedRow(invite.user)) {
delegate()->peerListAppendRow(std::move(row)); delegate()->peerListAppendRow(std::move(row));
@ -1120,7 +1102,7 @@ void MembersController::updateRow(
if (checkPosition) { if (checkPosition) {
checkRowPosition(checkPosition); checkRowPosition(checkPosition);
} else if (addedToBottom) { } else if (addedToBottom) {
const auto real = resolvedRealCall(); const auto real = _call->lookupReal();
if (real && real->joinedToTop()) { if (real && real->joinedToTop()) {
const auto proj = [&](const PeerListRow &other) { const auto proj = [&](const PeerListRow &other) {
const auto &real = static_cast<const Row&>(other); const auto &real = static_cast<const Row&>(other);
@ -1314,14 +1296,6 @@ Row *MembersController::findRow(not_null<PeerData*> participantPeer) const {
delegate()->peerListFindRow(participantPeer->id)); delegate()->peerListFindRow(participantPeer->id));
} }
Data::GroupCall *MembersController::resolvedRealCall() const {
return (_realCallRawValue
&& (_peer->groupCall() == _realCallRawValue)
&& (_realCallRawValue->id() == _realId))
? _realCallRawValue
: nullptr;
}
Main::Session &MembersController::session() const { Main::Session &MembersController::session() const {
return _call->peer()->session(); return _call->peer()->session();
} }
@ -1332,9 +1306,7 @@ void MembersController::prepare() {
setDescriptionText(tr::lng_contacts_loading(tr::now)); setDescriptionText(tr::lng_contacts_loading(tr::now));
setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now)); setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now));
const auto call = _call.get(); if (const auto real = _call->lookupReal()) {
if (const auto real = _peer->groupCall()
; real && call && real->id() == call->id()) {
prepareRows(real); prepareRows(real);
} else if (auto row = createRowForMe()) { } else if (auto row = createRowForMe()) {
delegate()->peerListAppendRow(std::move(row)); delegate()->peerListAppendRow(std::move(row));
@ -1342,15 +1314,12 @@ void MembersController::prepare() {
} }
loadMoreRows(); loadMoreRows();
if (_realId) { appendInvitedUsers();
appendInvitedUsers();
}
_prepared = true; _prepared = true;
} }
bool MembersController::isMe(not_null<PeerData*> participantPeer) const { bool MembersController::isMe(not_null<PeerData*> participantPeer) const {
const auto call = _call.get(); return (_call->joinAs() == participantPeer);
return call && (call->joinAs() == participantPeer);
} }
void MembersController::prepareRows(not_null<Data::GroupCall*> real) { void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
@ -1379,19 +1348,17 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
} }
} }
if (!foundMe) { if (!foundMe) {
if (const auto call = _call.get()) { const auto me = _call->joinAs();
const auto me = call->joinAs(); const auto i = ranges::find(
const auto i = ranges::find( participants,
participants, me,
me, &Data::GroupCall::Participant::peer);
&Data::GroupCall::Participant::peer); auto row = (i != end(participants))
auto row = (i != end(participants)) ? createRow(*i)
? createRow(*i) : createRowForMe();
: createRowForMe(); if (row) {
if (row) { changed = true;
changed = true; delegate()->peerListAppendRow(std::move(row));
delegate()->peerListAppendRow(std::move(row));
}
} }
} }
for (const auto &participant : participants) { for (const auto &participant : participants) {
@ -1406,7 +1373,7 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
} }
void MembersController::loadMoreRows() { void MembersController::loadMoreRows() {
if (const auto real = _peer->groupCall()) { if (const auto real = _call->lookupReal()) {
real->requestParticipants(); real->requestParticipants();
} }
} }
@ -1634,12 +1601,10 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
} }
if (isMe(participantPeer)) { if (isMe(participantPeer)) {
if (const auto strong = _call.get() if (_call->muted() == MuteState::RaisedHand) {
; strong && strong->muted() == MuteState::RaisedHand) {
const auto removeHand = [=] { const auto removeHand = [=] {
if (const auto strong = _call.get() if (_call->muted() == MuteState::RaisedHand) {
; strong && strong->muted() == MuteState::RaisedHand) { _call->setMutedAndUpdate(MuteState::ForceMuted);
strong->setMutedAndUpdate(MuteState::ForceMuted);
} }
}; };
result->addAction( result->addAction(
@ -1728,14 +1693,12 @@ void MembersController::addMuteActionsToContextMenu(
auto mutesFromVolume = rpl::never<bool>() | rpl::type_erased(); auto mutesFromVolume = rpl::never<bool>() | rpl::type_erased();
const auto call = _call.get(); if (!isMuted || _call->joinAs() == participantPeer) {
if (!isMuted || (call && call->joinAs() == participantPeer)) { auto otherParticipantStateValue
auto otherParticipantStateValue = call = _call->otherParticipantStateValue(
? call->otherParticipantStateValue( ) | rpl::filter([=](const Group::ParticipantState &data) {
) | rpl::filter([=](const Group::ParticipantState &data) { return data.peer == participantPeer;
return data.peer == participantPeer; });
})
: rpl::never<Group::ParticipantState>() | rpl::type_erased();
auto volumeItem = base::make_unique_q<MenuVolumeItem>( auto volumeItem = base::make_unique_q<MenuVolumeItem>(
menu->menu(), menu->menu(),
@ -1814,11 +1777,7 @@ void MembersController::addMuteActionsToContextMenu(
} }
std::unique_ptr<Row> MembersController::createRowForMe() { std::unique_ptr<Row> MembersController::createRowForMe() {
const auto call = _call.get(); auto result = std::make_unique<Row>(this, _call->joinAs());
if (!call) {
return nullptr;
}
auto result = std::make_unique<Row>(this, call->joinAs());
updateRow(result.get(), nullptr); updateRow(result.get(), nullptr);
return result; return result;
} }
@ -1877,12 +1836,8 @@ auto Members::kickParticipantRequests() const
int Members::desiredHeight() const { int Members::desiredHeight() const {
const auto top = _addMember ? _addMember->height() : 0; const auto top = _addMember ? _addMember->height() : 0;
auto count = [&] { auto count = [&] {
if (const auto call = _call.get()) { if (const auto real = _call->lookupReal()) {
if (const auto real = call->peer()->groupCall()) { return real->fullCount();
if (call->id() == real->id()) {
return real->fullCount();
}
}
} }
return 0; return 0;
}(); }();
@ -1911,16 +1866,7 @@ void Members::setupAddMember(not_null<GroupCall*> call) {
if (const auto channel = peer->asBroadcast()) { if (const auto channel = peer->asBroadcast()) {
_canAddMembers = rpl::single( _canAddMembers = rpl::single(
false false
) | rpl::then(peer->session().changes().peerFlagsValue( ) | rpl::then(_call->real(
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::map([=] { ) | rpl::map([=] {
return Data::PeerFlagValue( return Data::PeerFlagValue(
channel, channel,

View File

@ -75,7 +75,7 @@ private:
void updateControlsGeometry(); void updateControlsGeometry();
const base::weak_ptr<GroupCall> _call; const not_null<GroupCall*> _call;
object_ptr<Ui::ScrollArea> _scroll; object_ptr<Ui::ScrollArea> _scroll;
std::unique_ptr<PeerListController> _listController; std::unique_ptr<PeerListController> _listController;
object_ptr<Ui::SettingsButton> _addMember = { nullptr }; object_ptr<Ui::SettingsButton> _addMember = { nullptr };

View File

@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_group_call.h" #include "data/data_group_call.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_peer_values.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "base/event_filter.h" #include "base/event_filter.h"
#include "boxes/peers/edit_participants_box.h" #include "boxes/peers/edit_participants_box.h"
@ -259,14 +260,17 @@ Panel::Panel(not_null<GroupCall*> call)
_window->body(), _window->body(),
st::groupCallTitle)) st::groupCallTitle))
#endif // !Q_OS_MAC #endif // !Q_OS_MAC
, _scheduleDate(call->scheduleDate())
, _settings(widget(), st::groupCallSettings) , _settings(widget(), st::groupCallSettings)
, _mute(std::make_unique<Ui::CallMuteButton>( , _mute(std::make_unique<Ui::CallMuteButton>(
widget(), widget(),
Core::App().appDeactivatedValue(), Core::App().appDeactivatedValue(),
Ui::CallMuteButtonState{ Ui::CallMuteButtonState{
.text = tr::lng_group_call_connecting(tr::now), .text = (_call->scheduleDate()
.type = Ui::CallMuteButtonType::Connecting, ? "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) { , _hangup(widget(), st::groupCallHangup) {
_layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox); _layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox);
@ -277,7 +281,7 @@ Panel::Panel(not_null<GroupCall*> call)
_peer, _peer,
_window->lifetime(), _window->lifetime(),
[=](not_null<ChannelData*> channel) { migrate(channel); }); [=](not_null<ChannelData*> channel) { migrate(channel); });
setupRealCallViewers(call); setupRealCallViewers();
initWindow(); initWindow();
initWidget(); initWidget();
@ -295,17 +299,8 @@ Panel::~Panel() {
} }
} }
void Panel::setupRealCallViewers(not_null<GroupCall*> call) { void Panel::setupRealCallViewers() {
const auto peer = call->peer(); _call->real(
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
) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) { ) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) {
subscribeToChanges(real); subscribeToChanges(real);
}, _window->lifetime()); }, _window->lifetime());
@ -430,6 +425,15 @@ void Panel::initControls() {
) | rpl::filter([=](Qt::MouseButton button) { ) | rpl::filter([=](Qt::MouseButton button) {
return (button == Qt::LeftButton); return (button == Qt::LeftButton);
}) | rpl::start_with_next([=] { }) | 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 oldState = _call->muted();
const auto newState = (oldState == MuteState::ForceMuted) const auto newState = (oldState == MuteState::ForceMuted)
? MuteState::RaisedHand ? MuteState::RaisedHand
@ -449,10 +453,6 @@ void Panel::initControls() {
_settings->setText(tr::lng_group_call_settings()); _settings->setText(tr::lng_group_call_settings());
_hangup->setText(tr::lng_group_call_leave()); _hangup->setText(tr::lng_group_call_leave());
if (!_call->scheduleDate()) {
setupMembers();
}
_call->stateValue( _call->stateValue(
) | rpl::filter([](State state) { ) | rpl::filter([](State state) {
return (state == State::HangingUp) return (state == State::HangingUp)
@ -470,18 +470,38 @@ void Panel::initControls() {
_mute->setLevel(update.value); _mute->setLevel(update.value);
}, _callLifetime); }, _callLifetime);
_call->real(
) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) {
setupRealMuteButtonState(real);
}, _callLifetime);
}
void Panel::setupRealMuteButtonState(not_null<Data::GroupCall*> real) {
using namespace rpl::mappers; using namespace rpl::mappers;
rpl::combine( rpl::combine(
_call->mutedValue() | MapPushToTalkToActive(), _call->mutedValue() | MapPushToTalkToActive(),
_call->instanceStateValue() _call->instanceStateValue(),
real->scheduleDateValue(),
real->scheduleStartSubscribedValue(),
Data::CanManageGroupCallValue(_peer)
) | rpl::distinct_until_changed( ) | rpl::distinct_until_changed(
) | rpl::filter( ) | rpl::filter(
_2 != GroupCall::InstanceState::TransitionToRtc _2 != GroupCall::InstanceState::TransitionToRtc
) | rpl::start_with_next([=]( ) | rpl::start_with_next([=](
MuteState mute, MuteState mute,
GroupCall::InstanceState state) { GroupCall::InstanceState state,
TimeId scheduleDate,
bool scheduleStartSubscribed,
bool canManage) {
using Type = Ui::CallMuteButtonType;
_mute->setState(Ui::CallMuteButtonState{ _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) ? tr::lng_group_call_connecting(tr::now)
: mute == MuteState::ForceMuted : mute == MuteState::ForceMuted
? tr::lng_group_call_force_muted(tr::now) ? tr::lng_group_call_force_muted(tr::now)
@ -490,7 +510,9 @@ void Panel::initControls() {
: mute == MuteState::Muted : mute == MuteState::Muted
? tr::lng_group_call_unmute(tr::now) ? tr::lng_group_call_unmute(tr::now)
: tr::lng_group_call_you_are_live(tr::now)), : tr::lng_group_call_you_are_live(tr::now)),
.subtext = (state == GroupCall::InstanceState::Disconnected .subtext = (scheduleDate
? QString()
: state == GroupCall::InstanceState::Disconnected
? QString() ? QString()
: mute == MuteState::ForceMuted : mute == MuteState::ForceMuted
? tr::lng_group_call_raise_hand_tip(tr::now) ? tr::lng_group_call_raise_hand_tip(tr::now)
@ -499,15 +521,21 @@ void Panel::initControls() {
: mute == MuteState::Muted : mute == MuteState::Muted
? tr::lng_group_call_unmute_sub(tr::now) ? tr::lng_group_call_unmute_sub(tr::now)
: QString()), : QString()),
.type = (state == GroupCall::InstanceState::Disconnected .type = (scheduleDate
? Ui::CallMuteButtonType::Connecting ? (canManage
? Type::ScheduledCanStart
: scheduleStartSubscribed
? Type::ScheduledNotify
: Type::ScheduledSilent)
: state == GroupCall::InstanceState::Disconnected
? Type::Connecting
: mute == MuteState::ForceMuted : mute == MuteState::ForceMuted
? Ui::CallMuteButtonType::ForceMuted ? Type::ForceMuted
: mute == MuteState::RaisedHand : mute == MuteState::RaisedHand
? Ui::CallMuteButtonType::RaisedHand ? Type::RaisedHand
: mute == MuteState::Muted : mute == MuteState::Muted
? Ui::CallMuteButtonType::Muted ? Type::Muted
: Ui::CallMuteButtonType::Active), : Type::Active),
}); });
}, _callLifetime); }, _callLifetime);
} }
@ -590,8 +618,7 @@ void Panel::setupJoinAsChangedToasts() {
void Panel::setupTitleChangedToasts() { void Panel::setupTitleChangedToasts() {
_call->titleChanged( _call->titleChanged(
) | rpl::filter([=] { ) | rpl::filter([=] {
const auto real = _peer->groupCall(); return (_call->lookupReal() != nullptr);
return real && (real->id() == _call->id());
}) | rpl::map([=] { }) | rpl::map([=] {
return _peer->groupCall()->title().isEmpty() return _peer->groupCall()->title().isEmpty()
? _peer->name ? _peer->name
@ -617,10 +644,8 @@ void Panel::setupAllowedToSpeakToasts() {
.text = { tr::lng_group_call_can_speak_here(tr::now) }, .text = { tr::lng_group_call_can_speak_here(tr::now) },
}); });
} else { } else {
const auto real = _peer->groupCall(); const auto real = _call->lookupReal();
const auto name = (real const auto name = (real && !real->title().isEmpty())
&& (real->id() == _call->id())
&& !real->title().isEmpty())
? real->title() ? real->title()
: _peer->name; : _peer->name;
Ui::ShowMultilineToast({ Ui::ShowMultilineToast({
@ -635,18 +660,6 @@ void Panel::setupAllowedToSpeakToasts() {
} }
void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) { void Panel::subscribeToChanges(not_null<Data::GroupCall*> 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) { const auto validateRecordingMark = [=](bool recording) {
if (!recording && _recordingMark) { if (!recording && _recordingMark) {
_recordingMark.destroy(); _recordingMark.destroy();
@ -823,8 +836,8 @@ void Panel::showMainMenu() {
} }
void Panel::addMembers() { void Panel::addMembers() {
const auto real = _peer->groupCall(); const auto real = _call->lookupReal();
if (!real || real->id() != _call->id()) { if (!real) {
return; return;
} }
auto alreadyIn = _peer->owner().invitedToCallUsers(real->id()); auto alreadyIn = _peer->owner().invitedToCallUsers(real->id());
@ -1135,7 +1148,12 @@ void Panel::refreshTitle() {
if (!_title) { if (!_title) {
auto text = rpl::combine( auto text = rpl::combine(
Info::Profile::NameValue(_peer), Info::Profile::NameValue(_peer),
_titleText.value() rpl::single(
QString()
) | rpl::then(_call->real(
) | rpl::map([=](not_null<Data::GroupCall*> real) {
return real->titleValue();
}) | rpl::flatten_latest())
) | rpl::map([=]( ) | rpl::map([=](
const TextWithEntities &name, const TextWithEntities &name,
const QString &title) { const QString &title) {
@ -1154,15 +1172,24 @@ void Panel::refreshTitle() {
if (!_subtitle) { if (!_subtitle) {
_subtitle.create( _subtitle.create(
widget(), widget(),
_scheduleDate.value( rpl::single(
_call->scheduleDate()
) | rpl::then(
_call->real(
) | rpl::map([=](not_null<Data::GroupCall*> real) {
return real->scheduleDateValue();
}) | rpl::flatten_latest()
) | rpl::map([=](TimeId scheduleDate) { ) | rpl::map([=](TimeId scheduleDate) {
return scheduleDate if (scheduleDate) {
? tr::lng_group_call_scheduled_status() return tr::lng_group_call_scheduled_status();
: tr::lng_group_call_members( } else if (!_members) {
lt_count_decimal, setupMembers();
_members->fullCountValue() | rpl::map([](int value) { }
return (value > 0) ? float64(value) : 1.; return tr::lng_group_call_members(
})); lt_count_decimal,
_members->fullCountValue() | rpl::map([](int value) {
return (value > 0) ? float64(value) : 1.;
}));
}) | rpl::flatten_latest(), }) | rpl::flatten_latest(),
st::groupCallSubtitleLabel); st::groupCallSubtitleLabel);
_subtitle->show(); _subtitle->show();

View File

@ -79,6 +79,7 @@ private:
void setupJoinAsChangedToasts(); void setupJoinAsChangedToasts();
void setupTitleChangedToasts(); void setupTitleChangedToasts();
void setupAllowedToSpeakToasts(); void setupAllowedToSpeakToasts();
void setupRealMuteButtonState(not_null<Data::GroupCall*> real);
bool handleClose(); bool handleClose();
@ -96,7 +97,7 @@ private:
[[nodiscard]] QRect computeTitleRect() const; [[nodiscard]] QRect computeTitleRect() const;
void refreshTitle(); void refreshTitle();
void refreshTitleGeometry(); void refreshTitleGeometry();
void setupRealCallViewers(not_null<GroupCall*> call); void setupRealCallViewers();
void subscribeToChanges(not_null<Data::GroupCall*> real); void subscribeToChanges(not_null<Data::GroupCall*> real);
void migrate(not_null<ChannelData*> channel); void migrate(not_null<ChannelData*> channel);
@ -121,8 +122,6 @@ private:
object_ptr<Ui::DropdownMenu> _menu = { nullptr }; object_ptr<Ui::DropdownMenu> _menu = { nullptr };
object_ptr<Ui::AbstractButton> _joinAsToggle = { nullptr }; object_ptr<Ui::AbstractButton> _joinAsToggle = { nullptr };
object_ptr<Members> _members = { nullptr }; object_ptr<Members> _members = { nullptr };
rpl::variable<QString> _titleText;
rpl::variable<TimeId> _scheduleDate;
ChooseJoinAsProcess _joinAsProcess; ChooseJoinAsProcess _joinAsProcess;
object_ptr<Ui::CallButton> _settings; object_ptr<Ui::CallButton> _settings;

View File

@ -330,6 +330,7 @@ void GroupCall::applyCallFields(const MTPDgroupCall &data) {
_title = qs(data.vtitle().value_or_empty()); _title = qs(data.vtitle().value_or_empty());
_recordStartDate = data.vrecord_start_date().value_or_empty(); _recordStartDate = data.vrecord_start_date().value_or_empty();
_scheduleDate = data.vschedule_date().value_or_empty(); _scheduleDate = data.vschedule_date().value_or_empty();
_scheduleStartSubscribed = data.is_schedule_start_subscribed();
_allParticipantsLoaded _allParticipantsLoaded
= (_serverParticipantsCount == _participants.size()); = (_serverParticipantsCount == _participants.size());
} }

View File

@ -72,6 +72,12 @@ public:
[[nodiscard]] rpl::producer<TimeId> scheduleDateChanges() const { [[nodiscard]] rpl::producer<TimeId> scheduleDateChanges() const {
return _scheduleDate.changes(); return _scheduleDate.changes();
} }
[[nodiscard]] bool scheduleStartSubscribed() const {
return _scheduleStartSubscribed.current();
}
[[nodiscard]] rpl::producer<bool> scheduleStartSubscribedValue() const {
return _scheduleStartSubscribed.value();
}
void setPeer(not_null<PeerData*> peer); void setPeer(not_null<PeerData*> peer);
@ -173,6 +179,7 @@ private:
rpl::variable<int> _fullCount = 0; rpl::variable<int> _fullCount = 0;
rpl::variable<TimeId> _recordStartDate = 0; rpl::variable<TimeId> _recordStartDate = 0;
rpl::variable<TimeId> _scheduleDate = 0; rpl::variable<TimeId> _scheduleDate = 0;
rpl::variable<bool> _scheduleStartSubscribed = false;
base::flat_map<uint32, LastSpokeTimes> _unknownSpokenSsrcs; base::flat_map<uint32, LastSpokeTimes> _unknownSpokenSsrcs;
base::flat_map<PeerId, LastSpokeTimes> _unknownSpokenPeerIds; base::flat_map<PeerId, LastSpokeTimes> _unknownSpokenPeerIds;

View File

@ -320,6 +320,20 @@ rpl::producer<bool> CanPinMessagesValue(not_null<PeerData*> peer) {
Unexpected("Peer type in CanPinMessagesValue."); Unexpected("Peer type in CanPinMessagesValue.");
} }
rpl::producer<bool> CanManageGroupCallValue(not_null<PeerData*> 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<UserData*> user, TimeId now) { TimeId SortByOnlineValue(not_null<UserData*> user, TimeId now) {
if (user->isServiceUser() || user->isBot()) { if (user->isServiceUser() || user->isBot()) {
return -1; return -1;

View File

@ -111,6 +111,7 @@ inline auto PeerFullFlagValue(
[[nodiscard]] rpl::producer<bool> CanWriteValue(ChannelData *channel); [[nodiscard]] rpl::producer<bool> CanWriteValue(ChannelData *channel);
[[nodiscard]] rpl::producer<bool> CanWriteValue(not_null<PeerData*> peer); [[nodiscard]] rpl::producer<bool> CanWriteValue(not_null<PeerData*> peer);
[[nodiscard]] rpl::producer<bool> CanPinMessagesValue(not_null<PeerData*> peer); [[nodiscard]] rpl::producer<bool> CanPinMessagesValue(not_null<PeerData*> peer);
[[nodiscard]] rpl::producer<bool> CanManageGroupCallValue(not_null<PeerData*> peer);
[[nodiscard]] TimeId SortByOnlineValue(not_null<UserData*> user, TimeId now); [[nodiscard]] TimeId SortByOnlineValue(not_null<UserData*> user, TimeId now);
[[nodiscard]] crl::time OnlineChangeTimeout(TimeId online, TimeId now); [[nodiscard]] crl::time OnlineChangeTimeout(TimeId online, TimeId now);

View File

@ -102,21 +102,7 @@ auto MuteBlobs() {
auto Colors() { auto Colors() {
using Vector = std::vector<QColor>; using Vector = std::vector<QColor>;
using Colors = anim::gradient_colors; using Colors = anim::gradient_colors;
return base::flat_map<CallMuteButtonType, Colors>{ auto result = base::flat_map<CallMuteButtonType, Colors>{
{
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 } })
},
{ {
CallMuteButtonType::Active, CallMuteButtonType::Active,
Colors(Vector{ st::groupCallLive1->c, st::groupCallLive2->c }) Colors(Vector{ st::groupCallLive1->c, st::groupCallLive2->c })
@ -130,6 +116,21 @@ auto Colors() {
Colors(Vector{ st::groupCallMuted1->c, st::groupCallMuted2->c }) 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) { bool IsMuted(CallMuteButtonType type) {
@ -143,7 +144,10 @@ bool IsConnecting(CallMuteButtonType type) {
bool IsInactive(CallMuteButtonType type) { bool IsInactive(CallMuteButtonType type) {
return IsConnecting(type) return IsConnecting(type)
|| (type == CallMuteButtonType::ForceMuted) || (type == CallMuteButtonType::ForceMuted)
|| (type == CallMuteButtonType::RaisedHand); || (type == CallMuteButtonType::RaisedHand)
|| (type == CallMuteButtonType::ScheduledCanStart)
|| (type == CallMuteButtonType::ScheduledNotify)
|| (type == CallMuteButtonType::ScheduledSilent);
} }
auto Clamp(float64 value) { auto Clamp(float64 value) {
@ -585,6 +589,9 @@ CallMuteButton::IconState CallMuteButton::iconStateFrom(
.frameTo = 62, .frameTo = 62,
}; };
} break; } break;
case CallMuteButtonType::ScheduledCanStart: // #TODO voice chats
case CallMuteButtonType::ScheduledNotify:
case CallMuteButtonType::ScheduledSilent:
case CallMuteButtonType::ForceMuted: case CallMuteButtonType::ForceMuted:
case CallMuteButtonType::RaisedHand: { case CallMuteButtonType::RaisedHand: {
return { // Active -> Hand return { // Active -> Hand
@ -615,6 +622,9 @@ CallMuteButton::IconState CallMuteButton::iconStateFrom(
.frameTo = 22, .frameTo = 22,
}; };
} break; } break;
case CallMuteButtonType::ScheduledCanStart: // #TODO voice chats
case CallMuteButtonType::ScheduledNotify:
case CallMuteButtonType::ScheduledSilent:
case CallMuteButtonType::ForceMuted: case CallMuteButtonType::ForceMuted:
case CallMuteButtonType::RaisedHand: { case CallMuteButtonType::RaisedHand: {
return { // Muted -> Hand return { // Muted -> Hand
@ -626,6 +636,9 @@ CallMuteButton::IconState CallMuteButton::iconStateFrom(
} break; } break;
} }
} break; } break;
case CallMuteButtonType::ScheduledCanStart: // #TODO voice chats
case CallMuteButtonType::ScheduledNotify:
case CallMuteButtonType::ScheduledSilent:
case CallMuteButtonType::ForceMuted: case CallMuteButtonType::ForceMuted:
case CallMuteButtonType::RaisedHand: { case CallMuteButtonType::RaisedHand: {
switch (current) { switch (current) {
@ -645,6 +658,9 @@ CallMuteButton::IconState CallMuteButton::iconStateFrom(
.frameTo = 20, .frameTo = 20,
}; };
} break; } break;
case CallMuteButtonType::ScheduledCanStart: // #TODO voice chats
case CallMuteButtonType::ScheduledNotify:
case CallMuteButtonType::ScheduledSilent:
case CallMuteButtonType::ForceMuted: case CallMuteButtonType::ForceMuted:
case CallMuteButtonType::RaisedHand: { case CallMuteButtonType::RaisedHand: {
return { // Hand return { // Hand
@ -1015,6 +1031,9 @@ CallMuteButton::HandleMouseState CallMuteButton::HandleMouseStateFromType(
return HandleMouseState::Enabled; return HandleMouseState::Enabled;
case CallMuteButtonType::Connecting: case CallMuteButtonType::Connecting:
return HandleMouseState::Disabled; return HandleMouseState::Disabled;
case CallMuteButtonType::ScheduledCanStart:
case CallMuteButtonType::ScheduledNotify:
case CallMuteButtonType::ScheduledSilent:
case CallMuteButtonType::ForceMuted: case CallMuteButtonType::ForceMuted:
case CallMuteButtonType::RaisedHand: case CallMuteButtonType::RaisedHand:
return HandleMouseState::Enabled; return HandleMouseState::Enabled;
@ -1098,7 +1117,10 @@ void CallMuteButton::overridesColors(
float64 progress) { float64 progress) {
const auto forceMutedToConnecting = [](CallMuteButtonType &type) { const auto forceMutedToConnecting = [](CallMuteButtonType &type) {
if (type == CallMuteButtonType::ForceMuted if (type == CallMuteButtonType::ForceMuted
|| type == CallMuteButtonType::RaisedHand) { || type == CallMuteButtonType::RaisedHand
|| type == CallMuteButtonType::ScheduledCanStart
|| type == CallMuteButtonType::ScheduledNotify
|| type == CallMuteButtonType::ScheduledSilent) {
type = CallMuteButtonType::Connecting; type = CallMuteButtonType::Connecting;
} }
}; };

View File

@ -30,6 +30,9 @@ enum class CallMuteButtonType {
Muted, Muted,
ForceMuted, ForceMuted,
RaisedHand, RaisedHand,
ScheduledCanStart,
ScheduledSilent,
ScheduledNotify,
}; };
struct CallMuteButtonState { struct CallMuteButtonState {