mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Mute by me / change participant volume.
This commit is contained in:
parent
b396244606
commit
f63f0a7668
7 changed files with 188 additions and 26 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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*>(
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Reference in a new issue