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)
, _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<Data::GroupCall*> 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<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() {
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<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) {
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<LoadPartTask*> task) {
void GroupCall::requestParticipantsInformation(
const std::vector<uint32_t> &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<int, not_null<UserData*>> GroupCall::inviteUsers(
const std::vector<not_null<UserData*>> &users) {
const auto real = _peer->groupCall();
if (!real || real->id() != _id) {
const auto real = lookupReal();
if (!real) {
return 0;
}
const auto owner = &_peer->owner();

View File

@ -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<not_null<Data::GroupCall*>> 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<PeerData*> as);
void subscribeToReal(not_null<Data::GroupCall*> real);
void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data);
void setInstanceConnected(tgcalls::GroupNetworkState networkState);
@ -288,6 +295,7 @@ private:
rpl::event_stream<PeerData*> _peerStream;
not_null<History*> _history; // Can change in legacy group migration.
MTP::Sender _api;
rpl::event_stream<not_null<Data::GroupCall*>> _realChanges;
rpl::variable<State> _state = State::Creating;
rpl::variable<InstanceState> _instanceState
= InstanceState::Disconnected;

View File

@ -317,7 +317,7 @@ private:
not_null<PeerData*> participantPeer,
bool participantIsCallAdmin,
not_null<Row*> row);
void setupListChangeViewers(not_null<GroupCall*> call);
void setupListChangeViewers();
void subscribeToChanges(not_null<Data::GroupCall*> real);
void updateRow(
const std::optional<Data::GroupCall::Participant> &was,
@ -335,16 +335,11 @@ private:
uint64 raiseHandRating) const;
Row *findRow(not_null<PeerData*> participantPeer) const;
[[nodiscard]] Data::GroupCall *resolvedRealCall() const;
void appendInvitedUsers();
void scheduleRaisedHandStatusRemove();
const base::weak_ptr<GroupCall> _call;
const not_null<GroupCall*> _call;
not_null<PeerData*> _peer;
// Use only resolvedRealCall() method, not this value directly.
Data::GroupCall *_realCallRawValue = nullptr;
uint64 _realId = 0;
bool _prepared = false;
rpl::event_stream<MuteRequest> _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<GroupCall*> 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<Data::GroupCall*> 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<GroupCall*> 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<GroupCall*> call) {
}
void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
_realCallRawValue = real;
_realId = real->id();
_fullCount = real->fullCountValue();
real->participantsSliceAdded(
@ -1053,17 +1033,19 @@ void MembersController::subscribeToChanges(not_null<Data::GroupCall*> 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<const Row&>(other);
@ -1314,14 +1296,6 @@ Row *MembersController::findRow(not_null<PeerData*> 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<PeerData*> participantPeer) const {
const auto call = _call.get();
return call && (call->joinAs() == participantPeer);
return (_call->joinAs() == participantPeer);
}
void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
@ -1379,19 +1348,17 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> 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<Data::GroupCall*> 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<Ui::PopupMenu> 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<bool>() | 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<Group::ParticipantState>() | 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<MenuVolumeItem>(
menu->menu(),
@ -1814,11 +1777,7 @@ void MembersController::addMuteActionsToContextMenu(
}
std::unique_ptr<Row> MembersController::createRowForMe() {
const auto call = _call.get();
if (!call) {
return nullptr;
}
auto result = std::make_unique<Row>(this, call->joinAs());
auto result = std::make_unique<Row>(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<GroupCall*> 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,

View File

@ -75,7 +75,7 @@ private:
void updateControlsGeometry();
const base::weak_ptr<GroupCall> _call;
const not_null<GroupCall*> _call;
object_ptr<Ui::ScrollArea> _scroll;
std::unique_ptr<PeerListController> _listController;
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_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<GroupCall*> call)
_window->body(),
st::groupCallTitle))
#endif // !Q_OS_MAC
, _scheduleDate(call->scheduleDate())
, _settings(widget(), st::groupCallSettings)
, _mute(std::make_unique<Ui::CallMuteButton>(
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<GroupCall*> call)
_peer,
_window->lifetime(),
[=](not_null<ChannelData*> channel) { migrate(channel); });
setupRealCallViewers(call);
setupRealCallViewers();
initWindow();
initWidget();
@ -295,17 +299,8 @@ Panel::~Panel() {
}
}
void Panel::setupRealCallViewers(not_null<GroupCall*> 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<Data::GroupCall*> 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<Data::GroupCall*> real) {
setupRealMuteButtonState(real);
}, _callLifetime);
}
void Panel::setupRealMuteButtonState(not_null<Data::GroupCall*> 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<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) {
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<Data::GroupCall*> 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<Data::GroupCall*> 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();

View File

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

View File

@ -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());
}

View File

@ -72,6 +72,12 @@ public:
[[nodiscard]] rpl::producer<TimeId> scheduleDateChanges() const {
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);
@ -173,6 +179,7 @@ private:
rpl::variable<int> _fullCount = 0;
rpl::variable<TimeId> _recordStartDate = 0;
rpl::variable<TimeId> _scheduleDate = 0;
rpl::variable<bool> _scheduleStartSubscribed = false;
base::flat_map<uint32, LastSpokeTimes> _unknownSpokenSsrcs;
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.");
}
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) {
if (user->isServiceUser() || user->isBot()) {
return -1;

View File

@ -111,6 +111,7 @@ inline auto PeerFullFlagValue(
[[nodiscard]] rpl::producer<bool> CanWriteValue(ChannelData *channel);
[[nodiscard]] rpl::producer<bool> CanWriteValue(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]] crl::time OnlineChangeTimeout(TimeId online, TimeId now);

View File

@ -102,21 +102,7 @@ auto MuteBlobs() {
auto Colors() {
using Vector = std::vector<QColor>;
using Colors = anim::gradient_colors;
return 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 } })
},
auto result = base::flat_map<CallMuteButtonType, Colors>{
{
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;
}
};

View File

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