Mute by me / change participant volume.

This commit is contained in:
John Preston 2020-12-29 18:54:17 +04:00
parent b396244606
commit f63f0a7668
7 changed files with 188 additions and 26 deletions

View file

@ -238,11 +238,25 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) {
using Update = Data::GroupCall::ParticipantUpdate; using Update = Data::GroupCall::ParticipantUpdate;
_peer->groupCall()->participantUpdated( _peer->groupCall()->participantUpdated(
) | rpl::filter([=](const Update &update) { ) | rpl::filter([=](const Update &update) {
return (_instance != nullptr) && !update.now; return (_instance != nullptr);
}) | rpl::start_with_next([=](const Update &update) { }) | rpl::start_with_next([=](const Update &update) {
Expects(update.was.has_value()); if (!update.now) {
_instance->removeSsrcs({ update.was->ssrc });
_instance->removeSsrcs({ update.was->ssrc }); } else {
const auto &now = *update.now;
const auto &was = update.was;
const auto volumeChanged = was
? (was->volume != now.volume || was->mutedByMe != now.mutedByMe)
: (now.volume != Data::GroupCall::kDefaultVolume || now.mutedByMe);
if (volumeChanged) {
_instance->setVolume(
now.ssrc,
(now.mutedByMe
? 0.
: (now.volume
/ float64(Data::GroupCall::kDefaultVolume))));
}
}
}, _lifetime); }, _lifetime);
SubscribeToMigration(_peer, _lifetime, [=](not_null<ChannelData*> group) { SubscribeToMigration(_peer, _lifetime, [=](not_null<ChannelData*> group) {
@ -610,6 +624,7 @@ void GroupCall::createAndStartController() {
std::move(descriptor)); std::move(descriptor));
updateInstanceMuteState(); updateInstanceMuteState();
updateInstanceVolumes();
//raw->setAudioOutputDuckingEnabled(settings.callAudioDuckingEnabled()); //raw->setAudioOutputDuckingEnabled(settings.callAudioDuckingEnabled());
} }
@ -622,6 +637,27 @@ void GroupCall::updateInstanceMuteState() {
&& state != MuteState::PushToTalk); && state != MuteState::PushToTalk);
} }
void GroupCall::updateInstanceVolumes() {
const auto real = _peer->groupCall();
if (!real || real->id() != _id) {
return;
}
const auto &participants = real->participants();
for (const auto &participant : participants) {
const auto setVolume = participant.mutedByMe
|| (participant.volume != Data::GroupCall::kDefaultVolume);
if (setVolume && participant.ssrc) {
_instance->setVolume(
participant.ssrc,
(participant.mutedByMe
? 0.
: (participant.volume
/ float64(Data::GroupCall::kDefaultVolume))));
}
}
}
void GroupCall::audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data) { void GroupCall::audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data) {
Expects(!data.updates.empty()); Expects(!data.updates.empty());
@ -792,17 +828,46 @@ void GroupCall::setCurrentAudioDevice(bool input, const QString &deviceId) {
} }
} }
[[nodiscard]] const Data::GroupCall::Participant *LookupParticipant(
not_null<PeerData*> chat,
uint64 id,
not_null<UserData*> user) {
const auto call = chat->groupCall();
if (!id || !call || call->id() != id) {
return nullptr;
}
const auto &participants = call->participants();
const auto i = ranges::find(
participants,
user,
&Data::GroupCall::Participant::user);
return (i != end(participants)) ? &*i : nullptr;
}
void GroupCall::toggleMute(not_null<UserData*> user, bool mute) { void GroupCall::toggleMute(not_null<UserData*> user, bool mute) {
if (!_id) { editParticipant(user, mute, std::nullopt);
}
void GroupCall::changeVolume(not_null<UserData*> user, int volume) {
editParticipant(user, false, volume);
}
void GroupCall::editParticipant(
not_null<UserData*> user,
bool mute,
std::optional<int> volume) {
const auto participant = LookupParticipant(_peer, _id, user);
if (!participant) {
return; return;
} }
using Flag = MTPphone_EditGroupCallMember::Flag;
const auto flags = (mute ? Flag::f_muted : Flag(0))
| (volume.has_value() ? Flag::f_volume : Flag(0));
_api.request(MTPphone_EditGroupCallMember( _api.request(MTPphone_EditGroupCallMember(
MTP_flags(mute MTP_flags(flags),
? MTPphone_EditGroupCallMember::Flag::f_muted
: MTPphone_EditGroupCallMember::Flag(0)),
inputCall(), inputCall(),
user->inputUser, user->inputUser,
MTP_int(100000) // volume MTP_int(std::clamp(volume.value_or(0), 1, 20000))
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
_peer->session().api().applyUpdates(result); _peer->session().api().applyUpdates(result);
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {

View file

@ -132,6 +132,7 @@ public:
void setAudioDuckingEnabled(bool enabled); void setAudioDuckingEnabled(bool enabled);
void toggleMute(not_null<UserData*> user, bool mute); void toggleMute(not_null<UserData*> user, bool mute);
void changeVolume(not_null<UserData*> user, int volume);
std::variant<int, not_null<UserData*>> inviteUsers( std::variant<int, not_null<UserData*>> inviteUsers(
const std::vector<not_null<UserData*>> &users); const std::vector<not_null<UserData*>> &users);
@ -163,6 +164,7 @@ private:
void maybeSendMutedUpdate(MuteState previous); void maybeSendMutedUpdate(MuteState previous);
void sendMutedUpdate(); void sendMutedUpdate();
void updateInstanceMuteState(); void updateInstanceMuteState();
void updateInstanceVolumes();
void applySelfInCallLocally(); void applySelfInCallLocally();
void rejoin(); void rejoin();
@ -178,6 +180,11 @@ private:
void stopConnectingSound(); void stopConnectingSound();
void playConnectingSoundOnce(); void playConnectingSoundOnce();
void editParticipant(
not_null<UserData*> user,
bool mute,
std::optional<int> volume);
[[nodiscard]] MTPInputGroupCall inputCall() const; [[nodiscard]] MTPInputGroupCall inputCall() const;
const not_null<Delegate*> _delegate; const not_null<Delegate*> _delegate;

View file

@ -76,7 +76,8 @@ public:
QRect rect, QRect rect,
float64 speaking, float64 speaking,
float64 active, float64 active,
float64 muted) = 0; float64 muted,
bool mutedByMe) = 0;
}; };
class Row final : public PeerListRow { class Row final : public PeerListRow {
@ -87,6 +88,7 @@ public:
Active, Active,
Inactive, Inactive,
Muted, Muted,
MutedByMe,
Invited, Invited,
}; };
@ -106,6 +108,9 @@ public:
[[nodiscard]] bool speaking() const { [[nodiscard]] bool speaking() const {
return _speaking; return _speaking;
} }
[[nodiscard]] int volume() const {
return _volume;
}
void addActionRipple(QPoint point, Fn<void()> updateCallback) override; void addActionRipple(QPoint point, Fn<void()> updateCallback) override;
void stopLastActionRipple() override; void stopLastActionRipple() override;
@ -177,6 +182,7 @@ private:
void setSpeaking(bool speaking); void setSpeaking(bool speaking);
void setState(State state); void setState(State state);
void setSsrc(uint32 ssrc); void setSsrc(uint32 ssrc);
void setVolume(int volume);
void ensureUserpicCache( void ensureUserpicCache(
std::shared_ptr<Data::CloudImageView> &view, std::shared_ptr<Data::CloudImageView> &view,
@ -190,6 +196,7 @@ private:
Ui::Animations::Simple _mutedAnimation; // For gray/red icon. Ui::Animations::Simple _mutedAnimation; // For gray/red icon.
Ui::Animations::Simple _activeAnimation; // For icon cross animation. Ui::Animations::Simple _activeAnimation; // For icon cross animation.
uint32 _ssrc = 0; uint32 _ssrc = 0;
int _volume = Data::GroupCall::kDefaultVolume;
bool _sounding = false; bool _sounding = false;
bool _speaking = false; bool _speaking = false;
bool _skipLevelUpdate = false; bool _skipLevelUpdate = false;
@ -207,6 +214,7 @@ public:
~MembersController(); ~MembersController();
using MuteRequest = GroupMembers::MuteRequest; using MuteRequest = GroupMembers::MuteRequest;
using VolumeRequest = GroupMembers::VolumeRequest;
Main::Session &session() const override; Main::Session &session() const override;
void prepare() override; void prepare() override;
@ -221,6 +229,7 @@ public:
return _fullCount.value(); return _fullCount.value();
} }
[[nodiscard]] rpl::producer<MuteRequest> toggleMuteRequests() const; [[nodiscard]] rpl::producer<MuteRequest> toggleMuteRequests() const;
[[nodiscard]] rpl::producer<VolumeRequest> changeVolumeRequests() const;
[[nodiscard]] auto kickMemberRequests() const [[nodiscard]] auto kickMemberRequests() const
-> rpl::producer<not_null<UserData*>>; -> rpl::producer<not_null<UserData*>>;
@ -231,7 +240,8 @@ public:
QRect rect, QRect rect,
float64 speaking, float64 speaking,
float64 active, float64 active,
float64 muted) override; float64 muted,
bool mutedByMe) override;
private: private:
[[nodiscard]] std::unique_ptr<Row> createSelfRow(); [[nodiscard]] std::unique_ptr<Row> createSelfRow();
@ -271,6 +281,7 @@ private:
bool _prepared = false; bool _prepared = false;
rpl::event_stream<MuteRequest> _toggleMuteRequests; rpl::event_stream<MuteRequest> _toggleMuteRequests;
rpl::event_stream<VolumeRequest> _changeVolumeRequests;
rpl::event_stream<not_null<UserData*>> _kickMemberRequests; rpl::event_stream<not_null<UserData*>> _kickMemberRequests;
rpl::variable<int> _fullCount = 1; rpl::variable<int> _fullCount = 1;
rpl::variable<int> _fullCountMin = 0; rpl::variable<int> _fullCountMin = 0;
@ -305,17 +316,20 @@ void Row::setSkipLevelUpdate(bool value) {
void Row::updateState(const Data::GroupCall::Participant *participant) { void Row::updateState(const Data::GroupCall::Participant *participant) {
setSsrc(participant ? participant->ssrc : 0); setSsrc(participant ? participant->ssrc : 0);
setVolume(participant
? participant->volume
: Data::GroupCall::kDefaultVolume);
if (!participant) { if (!participant) {
setState(State::Invited); setState(State::Invited);
setSounding(false); setSounding(false);
setSpeaking(false); setSpeaking(false);
} else if (!participant->muted } else if (!participant->muted
|| (participant->sounding && participant->ssrc != 0)) { || (participant->sounding && participant->ssrc != 0)) {
setState(State::Active); setState(participant->mutedByMe ? State::MutedByMe : State::Active);
setSounding(participant->sounding && participant->ssrc != 0); setSounding(participant->sounding && participant->ssrc != 0);
setSpeaking(participant->speaking && participant->ssrc != 0); setSpeaking(participant->speaking && participant->ssrc != 0);
} else if (participant->canSelfUnmute) { } else if (participant->canSelfUnmute) {
setState(State::Inactive); setState(participant->mutedByMe ? State::MutedByMe : State::Inactive);
setSounding(false); setSounding(false);
setSpeaking(false); setSpeaking(false);
} else { } else {
@ -384,6 +398,10 @@ void Row::setSsrc(uint32 ssrc) {
_ssrc = ssrc; _ssrc = ssrc;
} }
void Row::setVolume(int volume) {
_volume = volume;
}
void Row::updateLevel(float level) { void Row::updateLevel(float level) {
Expects(_blobsAnimation != nullptr); Expects(_blobsAnimation != nullptr);
@ -451,13 +469,16 @@ auto Row::generatePaintUserpicCallback() -> PaintRoundImageCallback {
auto userpic = ensureUserpicView(); auto userpic = ensureUserpicView();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable { return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
if (_blobsAnimation) { if (_blobsAnimation) {
const auto mutedByMe = (_state == State::MutedByMe);
const auto shift = QPointF(x + size / 2., y + size / 2.); const auto shift = QPointF(x + size / 2., y + size / 2.);
auto hq = PainterHighQualityEnabler(p); auto hq = PainterHighQualityEnabler(p);
p.translate(shift); p.translate(shift);
const auto brush = anim::brush( const auto brush = mutedByMe
st::groupCallMemberInactiveStatus, ? st::groupCallMemberMutedIcon->b
st::groupCallMemberActiveStatus, : anim::brush(
_speakingAnimation.value(_speaking ? 1. : 0.)); st::groupCallMemberInactiveStatus,
st::groupCallMemberActiveStatus,
_speakingAnimation.value(_speaking ? 1. : 0.));
_blobsAnimation->blobs.paint(p, brush); _blobsAnimation->blobs.paint(p, brush);
p.translate(-shift); p.translate(-shift);
p.setOpacity(1.); p.setOpacity(1.);
@ -502,7 +523,7 @@ void Row::paintStatusText(
int availableWidth, int availableWidth,
int outerWidth, int outerWidth,
bool selected) { bool selected) {
if (_state != State::Invited) { if (_state != State::Invited && _state != State::MutedByMe) {
PeerListRow::paintStatusText( PeerListRow::paintStatusText(
p, p,
st, st,
@ -514,12 +535,18 @@ void Row::paintStatusText(
return; return;
} }
p.setFont(st::normalFont); p.setFont(st::normalFont);
p.setPen(st::groupCallMemberNotJoinedStatus); if (_state == State::MutedByMe) {
p.setPen(st::groupCallMemberMutedIcon);
} else {
p.setPen(st::groupCallMemberNotJoinedStatus);
}
p.drawTextLeft( p.drawTextLeft(
x, x,
y, y,
outerWidth, outerWidth,
(peer()->isSelf() (_state == State::MutedByMe
? "muted by me"
: peer()->isSelf()
? tr::lng_status_connecting(tr::now) ? tr::lng_status_connecting(tr::now)
: tr::lng_group_call_invited_status(tr::now))); : tr::lng_group_call_invited_status(tr::now)));
} }
@ -561,7 +588,8 @@ void Row::paintAction(
(_state == State::Active) ? 1. : 0.); (_state == State::Active) ? 1. : 0.);
const auto muted = _mutedAnimation.value( const auto muted = _mutedAnimation.value(
(_state == State::Muted) ? 1. : 0.); (_state == State::Muted) ? 1. : 0.);
_delegate->rowPaintIcon(p, iconRect, speaking, active, muted); const auto mutedByMe = (_state == State::MutedByMe);
_delegate->rowPaintIcon(p, iconRect, speaking, active, muted, mutedByMe);
} }
void Row::refreshStatus() { void Row::refreshStatus() {
@ -984,6 +1012,11 @@ auto MembersController::toggleMuteRequests() const
return _toggleMuteRequests.events(); return _toggleMuteRequests.events();
} }
auto MembersController::changeVolumeRequests() const
-> rpl::producer<VolumeRequest> {
return _changeVolumeRequests.events();
}
bool MembersController::rowCanMuteMembers() { bool MembersController::rowCanMuteMembers() {
return _peer->canManageGroupCall(); return _peer->canManageGroupCall();
} }
@ -997,11 +1030,12 @@ void MembersController::rowPaintIcon(
QRect rect, QRect rect,
float64 speaking, float64 speaking,
float64 active, float64 active,
float64 muted) { float64 muted,
bool mutedByMe) {
const auto &greenIcon = st::groupCallMemberColoredCrossLine.icon; const auto &greenIcon = st::groupCallMemberColoredCrossLine.icon;
const auto left = rect.x() + (rect.width() - greenIcon.width()) / 2; const auto left = rect.x() + (rect.width() - greenIcon.width()) / 2;
const auto top = rect.y() + (rect.height() - greenIcon.height()) / 2; const auto top = rect.y() + (rect.height() - greenIcon.height()) / 2;
if (speaking == 1.) { if (speaking == 1. && !mutedByMe) {
// Just green icon, no cross, no coloring. // Just green icon, no cross, no coloring.
greenIcon.paintInCenter(p, rect); greenIcon.paintInCenter(p, rect);
return; return;
@ -1029,7 +1063,9 @@ void MembersController::rowPaintIcon(
} }
const auto activeInactiveColor = anim::color( const auto activeInactiveColor = anim::color(
st::groupCallMemberInactiveIcon, st::groupCallMemberInactiveIcon,
st::groupCallMemberActiveIcon, (mutedByMe
? st::groupCallMemberMutedIcon
: st::groupCallMemberActiveIcon),
speaking); speaking);
const auto iconColor = anim::color( const auto iconColor = anim::color(
activeInactiveColor, activeInactiveColor,
@ -1119,7 +1155,8 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
} }
return false; return false;
}(); }();
const auto mute = admin const auto amCallAdmin = _peer->canManageGroupCall();
const auto mute = (admin || !amCallAdmin)
? (muteState == Row::State::Active) ? (muteState == Row::State::Active)
: (muteState != Row::State::Muted); : (muteState != Row::State::Muted);
const auto toggleMute = crl::guard(this, [=] { const auto toggleMute = crl::guard(this, [=] {
@ -1128,6 +1165,12 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
.mute = mute, .mute = mute,
}); });
}); });
const auto changeVolume = crl::guard(this, [=](int volume) {
_changeVolumeRequests.fire(VolumeRequest{
.user = user,
.volume = std::clamp(volume, 1, 20000),
});
});
const auto session = &user->session(); const auto session = &user->session();
const auto getCurrentWindow = [=]() -> Window::SessionController* { const auto getCurrentWindow = [=]() -> Window::SessionController* {
@ -1179,7 +1222,7 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
}); });
if ((muteState != Row::State::Invited) if ((muteState != Row::State::Invited)
&& _peer->canManageGroupCall() && amCallAdmin
&& (!admin || mute)) { && (!admin || mute)) {
result->addAction( result->addAction(
(mute (mute
@ -1187,6 +1230,24 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
: tr::lng_group_call_context_unmute(tr::now)), : tr::lng_group_call_context_unmute(tr::now)),
toggleMute); toggleMute);
} }
if (real->ssrc() != 0) {
if (!amCallAdmin
&& ((muteState == Row::State::Active)
|| (real->state() == Row::State::MutedByMe))) {
result->addAction(
((muteState == Row::State::Active)
? "Mute for me"
: "Unmute for me"),
toggleMute);
}
const auto volume = real->volume();
result->addAction(QString("Increase volume (%1%)").arg(volume / 100.), [=] {
changeVolume(volume + 2000);
});
result->addAction(QString("Decrease volume (%1%)").arg(volume / 100.), [=] {
changeVolume(volume - 2000);
});
}
result->addAction( result->addAction(
tr::lng_context_view_profile(tr::now), tr::lng_context_view_profile(tr::now),
showProfile); showProfile);
@ -1238,6 +1299,9 @@ std::unique_ptr<Row> MembersController::createInvitedRow(
} // namespace } // namespace
const int GroupMembers::kDefaultVolume = Data::GroupCall::kDefaultVolume;
GroupMembers::GroupMembers( GroupMembers::GroupMembers(
not_null<QWidget*> parent, not_null<QWidget*> parent,
not_null<GroupCall*> call) not_null<GroupCall*> call)
@ -1258,6 +1322,12 @@ auto GroupMembers::toggleMuteRequests() const
_listController.get())->toggleMuteRequests(); _listController.get())->toggleMuteRequests();
} }
auto GroupMembers::changeVolumeRequests() const
-> rpl::producer<GroupMembers::VolumeRequest> {
return static_cast<MembersController*>(
_listController.get())->changeVolumeRequests();
}
auto GroupMembers::kickMemberRequests() const auto GroupMembers::kickMemberRequests() const
-> rpl::producer<not_null<UserData*>> { -> rpl::producer<not_null<UserData*>> {
return static_cast<MembersController*>( return static_cast<MembersController*>(

View file

@ -30,15 +30,23 @@ public:
not_null<QWidget*> parent, not_null<QWidget*> parent,
not_null<GroupCall*> call); not_null<GroupCall*> call);
static const int kDefaultVolume;/* = Data::GroupCall::kDefaultVolume*/
struct MuteRequest { struct MuteRequest {
not_null<UserData*> user; not_null<UserData*> user;
bool mute = false; bool mute = false;
}; };
struct VolumeRequest {
not_null<UserData*> user;
int volume = kDefaultVolume;
bool finalized = true;
};
[[nodiscard]] int desiredHeight() const; [[nodiscard]] int desiredHeight() const;
[[nodiscard]] rpl::producer<int> desiredHeightValue() const override; [[nodiscard]] rpl::producer<int> desiredHeightValue() const override;
[[nodiscard]] rpl::producer<int> fullCountValue() const; [[nodiscard]] rpl::producer<int> fullCountValue() const;
[[nodiscard]] rpl::producer<MuteRequest> toggleMuteRequests() const; [[nodiscard]] rpl::producer<MuteRequest> toggleMuteRequests() const;
[[nodiscard]] rpl::producer<VolumeRequest> changeVolumeRequests() const;
[[nodiscard]] auto kickMemberRequests() const [[nodiscard]] auto kickMemberRequests() const
-> rpl::producer<not_null<UserData*>>; -> rpl::producer<not_null<UserData*>>;
[[nodiscard]] rpl::producer<> addMembersRequests() const { [[nodiscard]] rpl::producer<> addMembersRequests() const {

View file

@ -511,6 +511,13 @@ void GroupPanel::initWithCall(GroupCall *call) {
} }
}, _callLifetime); }, _callLifetime);
_members->changeVolumeRequests(
) | rpl::start_with_next([=](GroupMembers::VolumeRequest request) {
if (_call) {
_call->changeVolume(request.user, request.volume);
}
}, _callLifetime);
_members->kickMemberRequests( _members->kickMemberRequests(
) | rpl::start_with_next([=](not_null<UserData*> user) { ) | rpl::start_with_next([=](not_null<UserData*> user) {
kickMember(user); kickMember(user);

View file

@ -280,8 +280,10 @@ void GroupCall::applyParticipantsSlice(
.date = data.vdate().v, .date = data.vdate().v,
.lastActive = lastActive, .lastActive = lastActive,
.ssrc = uint32(data.vsource().v), .ssrc = uint32(data.vsource().v),
.volume = data.vvolume().value_or(kDefaultVolume),
.speaking = canSelfUnmute && (was ? was->speaking : false), .speaking = canSelfUnmute && (was ? was->speaking : false),
.muted = data.is_muted(), .muted = data.is_muted(),
.mutedByMe = data.is_muted_by_you(),
.canSelfUnmute = canSelfUnmute, .canSelfUnmute = canSelfUnmute,
}; };
if (i == end(_participants)) { if (i == end(_participants)) {

View file

@ -32,14 +32,17 @@ public:
void setPeer(not_null<PeerData*> peer); void setPeer(not_null<PeerData*> peer);
static constexpr auto kDefaultVolume = 10000;
struct Participant { struct Participant {
not_null<UserData*> user; not_null<UserData*> user;
TimeId date = 0; TimeId date = 0;
TimeId lastActive = 0; TimeId lastActive = 0;
uint32 ssrc = 0; uint32 ssrc = 0;
int volume = 0;
bool sounding = false; bool sounding = false;
bool speaking = false; bool speaking = false;
bool muted = false; bool muted = false;
bool mutedByMe = false;
bool canSelfUnmute = false; bool canSelfUnmute = false;
}; };
struct ParticipantUpdate { struct ParticipantUpdate {