mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-07 15:43:55 +02:00
Add blob animations to group call participants.
This commit is contained in:
parent
80597e190a
commit
498d6226e3
6 changed files with 216 additions and 171 deletions
|
@ -660,3 +660,6 @@ groupCallSettingsAttentionButton: SettingsButton(groupCallSettingsButton) {
|
||||||
groupCallBoxLabel: FlatLabel(boxLabel) {
|
groupCallBoxLabel: FlatLabel(boxLabel) {
|
||||||
textFg: groupCallMembersFg;
|
textFg: groupCallMembersFg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
groupCallRowBlobMinRadius: 26px;
|
||||||
|
groupCallRowBlobMaxRadius: 30px;
|
||||||
|
|
|
@ -152,7 +152,7 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) {
|
||||||
}) | rpl::start_with_next([=](const Update &update) {
|
}) | rpl::start_with_next([=](const Update &update) {
|
||||||
Expects(update.was.has_value());
|
Expects(update.was.has_value());
|
||||||
|
|
||||||
_instance->removeSsrcs({ update.was->source });
|
_instance->removeSsrcs({ update.was->ssrc });
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ void GroupCall::rejoin() {
|
||||||
root.insert("fingerprints", fingerprints);
|
root.insert("fingerprints", fingerprints);
|
||||||
root.insert("ssrc", double(payload.ssrc));
|
root.insert("ssrc", double(payload.ssrc));
|
||||||
|
|
||||||
LOG(("Call Info: Join payload received, joining with source: %1."
|
LOG(("Call Info: Join payload received, joining with ssrc: %1."
|
||||||
).arg(ssrc));
|
).arg(ssrc));
|
||||||
|
|
||||||
const auto json = QJsonDocument(root).toJson(
|
const auto json = QJsonDocument(root).toJson(
|
||||||
|
@ -418,12 +418,12 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
|
||||||
}
|
}
|
||||||
if (data.is_left() && data.vsource().v == _mySsrc) {
|
if (data.is_left() && data.vsource().v == _mySsrc) {
|
||||||
// I was removed from the call, rejoin.
|
// I was removed from the call, rejoin.
|
||||||
LOG(("Call Info: Rejoin after got 'left' with my source."));
|
LOG(("Call Info: Rejoin after got 'left' with my ssrc."));
|
||||||
setState(State::Joining);
|
setState(State::Joining);
|
||||||
rejoin();
|
rejoin();
|
||||||
} else if (!data.is_left() && data.vsource().v != _mySsrc) {
|
} else if (!data.is_left() && data.vsource().v != _mySsrc) {
|
||||||
// I joined from another device, hangup.
|
// I joined from another device, hangup.
|
||||||
LOG(("Call Info: Hangup after '!left' with source %1, my %2."
|
LOG(("Call Info: Hangup after '!left' with ssrc %1, my %2."
|
||||||
).arg(data.vsource().v
|
).arg(data.vsource().v
|
||||||
).arg(_mySsrc));
|
).arg(_mySsrc));
|
||||||
_mySsrc = 0;
|
_mySsrc = 0;
|
||||||
|
@ -495,10 +495,10 @@ void GroupCall::handleLevelsUpdated(
|
||||||
auto check = false;
|
auto check = false;
|
||||||
auto checkNow = false;
|
auto checkNow = false;
|
||||||
const auto now = crl::now();
|
const auto now = crl::now();
|
||||||
for (const auto &[source, level] : data) {
|
for (const auto &[ssrc, level] : data) {
|
||||||
const auto self = (source == _mySsrc);
|
const auto self = (ssrc == _mySsrc);
|
||||||
_levelUpdates.fire(LevelUpdate{
|
_levelUpdates.fire(LevelUpdate{
|
||||||
.source = source,
|
.ssrc = ssrc,
|
||||||
.value = level,
|
.value = level,
|
||||||
.self = self
|
.self = self
|
||||||
});
|
});
|
||||||
|
@ -515,9 +515,9 @@ void GroupCall::handleLevelsUpdated(
|
||||||
}
|
}
|
||||||
|
|
||||||
check = true;
|
check = true;
|
||||||
const auto i = _lastSpoke.find(source);
|
const auto i = _lastSpoke.find(ssrc);
|
||||||
if (i == _lastSpoke.end()) {
|
if (i == _lastSpoke.end()) {
|
||||||
_lastSpoke.emplace(source, now);
|
_lastSpoke.emplace(ssrc, now);
|
||||||
checkNow = true;
|
checkNow = true;
|
||||||
} else {
|
} else {
|
||||||
if (i->second + kCheckLastSpokeInterval / 3 <= now) {
|
if (i->second + kCheckLastSpokeInterval / 3 <= now) {
|
||||||
|
@ -553,14 +553,14 @@ void GroupCall::checkLastSpoke() {
|
||||||
const auto now = crl::now();
|
const auto now = crl::now();
|
||||||
auto list = base::take(_lastSpoke);
|
auto list = base::take(_lastSpoke);
|
||||||
for (auto i = list.begin(); i != list.end();) {
|
for (auto i = list.begin(); i != list.end();) {
|
||||||
const auto [source, when] = *i;
|
const auto [ssrc, when] = *i;
|
||||||
if (when + kCheckLastSpokeInterval >= now) {
|
if (when + kCheckLastSpokeInterval >= now) {
|
||||||
hasRecent = true;
|
hasRecent = true;
|
||||||
++i;
|
++i;
|
||||||
} else {
|
} else {
|
||||||
i = list.erase(i);
|
i = list.erase(i);
|
||||||
}
|
}
|
||||||
real->applyLastSpoke(source, when, now);
|
real->applyLastSpoke(ssrc, when, now);
|
||||||
}
|
}
|
||||||
_lastSpoke = std::move(list);
|
_lastSpoke = std::move(list);
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ enum class MuteState {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LevelUpdate {
|
struct LevelUpdate {
|
||||||
uint32 source = 0;
|
uint32 ssrc = 0;
|
||||||
float value = 0.;
|
float value = 0.;
|
||||||
bool self = false;
|
bool self = false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_group_call.h"
|
#include "data/data_group_call.h"
|
||||||
#include "data/data_peer_values.h" // Data::CanWriteValue.
|
#include "data/data_peer_values.h" // Data::CanWriteValue.
|
||||||
|
#include "ui/paint/blobs.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
|
@ -32,12 +33,37 @@ namespace Calls {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kLevelThreshold = 0.2;
|
constexpr auto kLevelThreshold = 0.2;
|
||||||
|
constexpr auto kRowBlobRadiusFactor = (float)(50. / 57.);
|
||||||
|
constexpr auto kLevelDuration = 100. + 500. * 0.33;
|
||||||
|
constexpr auto kScaleSmall = 0.704 - 0.1;
|
||||||
|
constexpr auto kScaleSmallMin = 0.926;
|
||||||
|
constexpr auto kScaleSmallMax = (float)(kScaleSmallMin + kScaleSmall);
|
||||||
|
constexpr auto kMaxLevel = 1.;
|
||||||
|
|
||||||
struct UpdateLevelResult {
|
auto RowBlobs() -> std::array<Ui::Paint::Blobs::BlobData, 2> {
|
||||||
bool levelChanged = false;
|
return { {
|
||||||
bool stateChanged = false;
|
{
|
||||||
crl::time nextUpdateTime = 0;
|
.segmentsCount = 6,
|
||||||
};
|
.minScale = kScaleSmallMin / kScaleSmallMax,
|
||||||
|
.minRadius = st::groupCallRowBlobMinRadius
|
||||||
|
* kRowBlobRadiusFactor,
|
||||||
|
.maxRadius = st::groupCallRowBlobMaxRadius
|
||||||
|
* kRowBlobRadiusFactor,
|
||||||
|
.speedScale = 1.,
|
||||||
|
.alpha = (76. / 255.),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.segmentsCount = 8,
|
||||||
|
.minScale = kScaleSmallMin / kScaleSmallMax,
|
||||||
|
.minRadius = st::groupCallRowBlobMinRadius
|
||||||
|
* kRowBlobRadiusFactor,
|
||||||
|
.maxRadius = st::groupCallRowBlobMaxRadius
|
||||||
|
* kRowBlobRadiusFactor,
|
||||||
|
.speedScale = 1.,
|
||||||
|
.alpha = (76. / 255.),
|
||||||
|
},
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
class Row final : public PeerListRow {
|
class Row final : public PeerListRow {
|
||||||
public:
|
public:
|
||||||
|
@ -50,10 +76,14 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
void updateState(const Data::GroupCall::Participant *participant);
|
void updateState(const Data::GroupCall::Participant *participant);
|
||||||
//UpdateLevelResult updateLevel(float level);
|
void updateLevel(float level);
|
||||||
|
void updateBlobAnimation(crl::time now);
|
||||||
[[nodiscard]] State state() const {
|
[[nodiscard]] State state() const {
|
||||||
return _state;
|
return _state;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] uint32 ssrc() const {
|
||||||
|
return _ssrc;
|
||||||
|
}
|
||||||
[[nodiscard]] bool speaking() const {
|
[[nodiscard]] bool speaking() const {
|
||||||
return _speaking;
|
return _speaking;
|
||||||
}
|
}
|
||||||
|
@ -85,9 +115,12 @@ public:
|
||||||
bool selected,
|
bool selected,
|
||||||
bool actionSelected) override;
|
bool actionSelected) override;
|
||||||
|
|
||||||
|
auto generatePaintUserpicCallback() -> PaintRoundImageCallback override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void refreshStatus() override;
|
void refreshStatus() override;
|
||||||
void setSpeaking(bool speaking);
|
void setSpeaking(bool speaking);
|
||||||
|
void setSsrc(uint32 ssrc);
|
||||||
|
|
||||||
[[nodiscard]] static State ComputeState(
|
[[nodiscard]] static State ComputeState(
|
||||||
not_null<ChannelData*> channel,
|
not_null<ChannelData*> channel,
|
||||||
|
@ -98,10 +131,12 @@ private:
|
||||||
State _state = State::Inactive;
|
State _state = State::Inactive;
|
||||||
not_null<ChannelData*> _channel;
|
not_null<ChannelData*> _channel;
|
||||||
not_null<const style::IconButton*> _st;
|
not_null<const style::IconButton*> _st;
|
||||||
bool _speaking = false;
|
|
||||||
//float _level = 0.;
|
|
||||||
|
|
||||||
std::unique_ptr<Ui::RippleAnimation> _actionRipple;
|
std::unique_ptr<Ui::RippleAnimation> _actionRipple;
|
||||||
|
std::unique_ptr<Ui::Paint::Blobs> _blobs;
|
||||||
|
crl::time _blobsLastTime = 0;
|
||||||
|
uint32 _ssrc = 0;
|
||||||
|
float _level = 0.;
|
||||||
|
bool _speaking = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -132,9 +167,9 @@ public:
|
||||||
-> rpl::producer<not_null<UserData*>>;
|
-> rpl::producer<not_null<UserData*>>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] std::unique_ptr<PeerListRow> createSelfRow() const;
|
[[nodiscard]] std::unique_ptr<Row> createSelfRow();
|
||||||
[[nodiscard]] std::unique_ptr<PeerListRow> createRow(
|
[[nodiscard]] std::unique_ptr<Row> createRow(
|
||||||
const Data::GroupCall::Participant &participant) const;
|
const Data::GroupCall::Participant &participant);
|
||||||
|
|
||||||
void prepareRows(not_null<Data::GroupCall*> real);
|
void prepareRows(not_null<Data::GroupCall*> real);
|
||||||
//void repaintByTimer();
|
//void repaintByTimer();
|
||||||
|
@ -146,9 +181,10 @@ private:
|
||||||
const Data::GroupCall::Participant &now);
|
const Data::GroupCall::Participant &now);
|
||||||
void updateRow(
|
void updateRow(
|
||||||
not_null<Row*> row,
|
not_null<Row*> row,
|
||||||
const Data::GroupCall::Participant *participant) const;
|
const Data::GroupCall::Participant *participant);
|
||||||
|
void removeRow(not_null<Row*> row);
|
||||||
|
void updateRowLevel(not_null<Row*> row, float level);
|
||||||
void checkSpeakingRowPosition(not_null<Row*> row);
|
void checkSpeakingRowPosition(not_null<Row*> row);
|
||||||
//void updateRowLevel(not_null<UserData*> user, float level);
|
|
||||||
Row *findRow(not_null<UserData*> user) const;
|
Row *findRow(not_null<UserData*> user) const;
|
||||||
|
|
||||||
[[nodiscard]] Data::GroupCall *resolvedRealCall() const;
|
[[nodiscard]] Data::GroupCall *resolvedRealCall() const;
|
||||||
|
@ -167,8 +203,8 @@ private:
|
||||||
not_null<QWidget*> _menuParent;
|
not_null<QWidget*> _menuParent;
|
||||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||||
|
|
||||||
//base::flat_map<not_null<UserData*>, crl::time> _repaintByTimer;
|
base::flat_map<uint32, not_null<Row*>> _speakingRowBySsrc;
|
||||||
//base::Timer _repaintTimer;
|
Ui::Animations::Basic _speakingAnimation;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
@ -183,6 +219,7 @@ Row::Row(not_null<ChannelData*> channel, not_null<UserData*> user)
|
||||||
}
|
}
|
||||||
|
|
||||||
void Row::updateState(const Data::GroupCall::Participant *participant) {
|
void Row::updateState(const Data::GroupCall::Participant *participant) {
|
||||||
|
setSsrc(participant ? participant->ssrc : 0);
|
||||||
if (!participant) {
|
if (!participant) {
|
||||||
if (peer()->isSelf()) {
|
if (peer()->isSelf()) {
|
||||||
setCustomStatus(tr::lng_group_call_connecting(tr::now));
|
setCustomStatus(tr::lng_group_call_connecting(tr::now));
|
||||||
|
@ -193,7 +230,7 @@ void Row::updateState(const Data::GroupCall::Participant *participant) {
|
||||||
setSpeaking(false);
|
setSpeaking(false);
|
||||||
} else if (!participant->muted) {
|
} else if (!participant->muted) {
|
||||||
_state = State::Active;
|
_state = State::Active;
|
||||||
setSpeaking(participant->speaking);
|
setSpeaking(participant->speaking && participant->ssrc != 0);
|
||||||
} else if (participant->canSelfUnmute) {
|
} else if (participant->canSelfUnmute) {
|
||||||
_state = State::Inactive;
|
_state = State::Inactive;
|
||||||
setSpeaking(false);
|
setSpeaking(false);
|
||||||
|
@ -209,42 +246,49 @@ void Row::setSpeaking(bool speaking) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_speaking = speaking;
|
_speaking = speaking;
|
||||||
|
if (!_speaking) {
|
||||||
|
_blobs = nullptr;
|
||||||
|
}
|
||||||
refreshStatus();
|
refreshStatus();
|
||||||
//if (!_speaking) {
|
|
||||||
// updateLevel(0.);
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//UpdateLevelResult Row::updateLevel(float level) {
|
void Row::setSsrc(uint32 ssrc) {
|
||||||
// if (_level == level) {
|
_ssrc = ssrc;
|
||||||
// return UpdateLevelResult{ .nextUpdateTime = _markInactiveAt };
|
}
|
||||||
// }
|
|
||||||
// const auto now = crl::now();
|
void Row::updateLevel(float level) {
|
||||||
// const auto stillActive = (now < _markInactiveAt);
|
Expects(_speaking);
|
||||||
// const auto wasActive = (_level >= kLevelThreshold) && stillActive;
|
|
||||||
// const auto nowActive = (level >= kLevelThreshold);
|
if (!_blobs) {
|
||||||
// if (nowActive) {
|
_blobs = std::make_unique<Ui::Paint::Blobs>(
|
||||||
// _markInactiveAt = now + kLevelActiveTimeout;
|
RowBlobs() | ranges::to_vector,
|
||||||
// if (_state != State::Active) {
|
kLevelDuration,
|
||||||
// _state = State::Active;
|
kMaxLevel);
|
||||||
// _st = ComputeIconStyle(_state);
|
_blobsLastTime = crl::now();
|
||||||
// }
|
}
|
||||||
// }
|
_blobs->setLevel(level + 0.5);
|
||||||
// _level = level;
|
}
|
||||||
// const auto changed = wasActive != (nowActive || stillActive);
|
|
||||||
// if (!changed) {
|
void Row::updateBlobAnimation(crl::time now) {
|
||||||
// return UpdateLevelResult{
|
if (_blobs) {
|
||||||
// .levelChanged = true,
|
_blobs->updateLevel(now - _blobsLastTime);
|
||||||
// .nextUpdateTime = _markInactiveAt,
|
_blobsLastTime = now;
|
||||||
// };
|
}
|
||||||
// }
|
}
|
||||||
// refreshStatus(now);
|
|
||||||
// return UpdateLevelResult{
|
auto Row::generatePaintUserpicCallback() -> PaintRoundImageCallback {
|
||||||
// .levelChanged = true,
|
auto userpic = ensureUserpicView();
|
||||||
// .stateChanged = true,
|
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
|
||||||
// .nextUpdateTime = _markInactiveAt,
|
if (_blobs) {
|
||||||
// };
|
const auto shift = QPointF(x + size / 2., y + size / 2.);
|
||||||
//}
|
p.translate(shift);
|
||||||
|
_blobs->paint(p, st::groupCallLive1);
|
||||||
|
p.translate(-shift);
|
||||||
|
p.setOpacity(1.);
|
||||||
|
}
|
||||||
|
peer()->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void Row::paintAction(
|
void Row::paintAction(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
|
@ -333,8 +377,15 @@ MembersController::MembersController(
|
||||||
: _call(call)
|
: _call(call)
|
||||||
, _channel(call->channel())
|
, _channel(call->channel())
|
||||||
, _menuParent(menuParent) {
|
, _menuParent(menuParent) {
|
||||||
//, _repaintTimer([=] { repaintByTimer(); }) {
|
|
||||||
setupListChangeViewers(call);
|
setupListChangeViewers(call);
|
||||||
|
|
||||||
|
_speakingAnimation.init([=](crl::time now) {
|
||||||
|
for (const auto [ssrc, row] : _speakingRowBySsrc) {
|
||||||
|
row->updateBlobAnimation(now);
|
||||||
|
delegate()->peerListUpdateRow(row);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
|
void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
|
||||||
|
@ -362,21 +413,13 @@ void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
|
||||||
}
|
}
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
|
||||||
//call->levelUpdates(
|
call->levelUpdates(
|
||||||
//) | rpl::start_with_next([=](const LevelUpdate &update) {
|
) | rpl::start_with_next([=](const LevelUpdate &update) {
|
||||||
// const auto findUserBySource = [&](uint32 source) -> UserData* {
|
const auto i = _speakingRowBySsrc.find(update.ssrc);
|
||||||
// if (const auto real = resolvedRealCall()) {
|
if (i != end(_speakingRowBySsrc)) {
|
||||||
// return real->userBySource(source);
|
updateRowLevel(i->second, update.value);
|
||||||
// }
|
}
|
||||||
// return nullptr;
|
}, _lifetime);
|
||||||
// };
|
|
||||||
// const auto user = update.self
|
|
||||||
// ? _channel->session().user().get()
|
|
||||||
// : findUserBySource(update.source);
|
|
||||||
// if (user) {
|
|
||||||
// updateRowLevel(user, update.value);
|
|
||||||
// }
|
|
||||||
//}, _lifetime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
||||||
|
@ -402,10 +445,9 @@ void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
||||||
if (!update.now) {
|
if (!update.now) {
|
||||||
if (const auto row = findRow(user)) {
|
if (const auto row = findRow(user)) {
|
||||||
if (user->isSelf()) {
|
if (user->isSelf()) {
|
||||||
row->updateState(nullptr);
|
updateRow(row, nullptr);
|
||||||
delegate()->peerListUpdateRow(row);
|
|
||||||
} else {
|
} else {
|
||||||
delegate()->peerListRemoveRow(row);
|
removeRow(row);
|
||||||
delegate()->peerListRefreshRows();
|
delegate()->peerListRefreshRows();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -424,7 +466,7 @@ void MembersController::updateRow(
|
||||||
}
|
}
|
||||||
updateRow(row, &now);
|
updateRow(row, &now);
|
||||||
} else if (auto row = createRow(now)) {
|
} else if (auto row = createRow(now)) {
|
||||||
if (now.speaking) {
|
if (row->speaking()) {
|
||||||
delegate()->peerListPrependRow(std::move(row));
|
delegate()->peerListPrependRow(std::move(row));
|
||||||
} else {
|
} else {
|
||||||
delegate()->peerListAppendRow(std::move(row));
|
delegate()->peerListAppendRow(std::move(row));
|
||||||
|
@ -466,49 +508,49 @@ void MembersController::checkSpeakingRowPosition(not_null<Row*> row) {
|
||||||
|
|
||||||
void MembersController::updateRow(
|
void MembersController::updateRow(
|
||||||
not_null<Row*> row,
|
not_null<Row*> row,
|
||||||
const Data::GroupCall::Participant *participant) const {
|
const Data::GroupCall::Participant *participant) {
|
||||||
|
const auto wasSpeaking = row->speaking();
|
||||||
|
const auto wasSsrc = row->ssrc();
|
||||||
row->updateState(participant);
|
row->updateState(participant);
|
||||||
|
const auto nowSpeaking = row->speaking();
|
||||||
|
const auto nowSsrc = row->ssrc();
|
||||||
|
|
||||||
|
const auto wasNoSpeaking = _speakingRowBySsrc.empty();
|
||||||
|
if (wasSsrc == nowSsrc) {
|
||||||
|
if (nowSpeaking != wasSpeaking) {
|
||||||
|
if (nowSpeaking) {
|
||||||
|
_speakingRowBySsrc.emplace(nowSsrc, row);
|
||||||
|
} else {
|
||||||
|
_speakingRowBySsrc.remove(nowSsrc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_speakingRowBySsrc.remove(wasSsrc);
|
||||||
|
if (nowSpeaking) {
|
||||||
|
Assert(nowSsrc != 0);
|
||||||
|
_speakingRowBySsrc.emplace(nowSsrc, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto nowNoSpeaking = _speakingRowBySsrc.empty();
|
||||||
|
if (wasNoSpeaking && !nowNoSpeaking) {
|
||||||
|
_speakingAnimation.start();
|
||||||
|
} else if (nowNoSpeaking && !wasNoSpeaking) {
|
||||||
|
_speakingAnimation.stop();
|
||||||
|
}
|
||||||
|
|
||||||
delegate()->peerListUpdateRow(row);
|
delegate()->peerListUpdateRow(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
//void MembersController::updateRowLevel(
|
void MembersController::removeRow(not_null<Row*> row) {
|
||||||
// not_null<UserData*> user,
|
_speakingRowBySsrc.remove(row->ssrc());
|
||||||
// float level) {
|
delegate()->peerListRemoveRow(row);
|
||||||
// if (const auto row = findRow(user)) {
|
}
|
||||||
// const auto result = row->updateLevel(level);
|
|
||||||
// if (result.stateChanged) {
|
|
||||||
// delegate()->peerListUpdateRow(row);
|
|
||||||
// }
|
|
||||||
// if (result.nextUpdateTime) {
|
|
||||||
// _repaintByTimer[user] = result.nextUpdateTime;
|
|
||||||
// if (!_repaintTimer.isActive()) {
|
|
||||||
// _repaintTimer.callOnce(kLevelActiveTimeout);
|
|
||||||
// }
|
|
||||||
// } else if (_repaintByTimer.remove(user) && _repaintByTimer.empty()) {
|
|
||||||
// _repaintTimer.cancel();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//void MembersController::repaintByTimer() {
|
void MembersController::updateRowLevel(
|
||||||
// const auto now = crl::now();
|
not_null<Row*> row,
|
||||||
// auto next = crl::time(0);
|
float level) {
|
||||||
// for (auto i = begin(_repaintByTimer); i != end(_repaintByTimer);) {
|
row->updateLevel(level);
|
||||||
// if (i->second > now) {
|
}
|
||||||
// if (!next || next > i->second) {
|
|
||||||
// next = i->second;
|
|
||||||
// }
|
|
||||||
// } else if (const auto row = findRow(i->first)) {
|
|
||||||
// delegate()->peerListUpdateRow(row);
|
|
||||||
// i = _repaintByTimer.erase(i);
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// ++i;
|
|
||||||
// }
|
|
||||||
// if (next) {
|
|
||||||
// _repaintTimer.callOnce(next - now);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
Row *MembersController::findRow(not_null<UserData*> user) const {
|
Row *MembersController::findRow(not_null<UserData*> user) const {
|
||||||
return static_cast<Row*>(delegate()->peerListFindRow(user->id));
|
return static_cast<Row*>(delegate()->peerListFindRow(user->id));
|
||||||
|
@ -564,7 +606,7 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
|
||||||
++i;
|
++i;
|
||||||
} else {
|
} else {
|
||||||
changed = true;
|
changed = true;
|
||||||
delegate()->peerListRemoveRow(row);
|
removeRow(static_cast<Row*>(row.get()));
|
||||||
--count;
|
--count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -708,15 +750,15 @@ base::unique_qptr<Ui::PopupMenu> MembersController::rowContextMenu(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<PeerListRow> MembersController::createSelfRow() const {
|
std::unique_ptr<Row> MembersController::createSelfRow() {
|
||||||
const auto self = _channel->session().user();
|
const auto self = _channel->session().user();
|
||||||
auto result = std::make_unique<Row>(_channel, self);
|
auto result = std::make_unique<Row>(_channel, self);
|
||||||
updateRow(result.get(), nullptr);
|
updateRow(result.get(), nullptr);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<PeerListRow> MembersController::createRow(
|
std::unique_ptr<Row> MembersController::createRow(
|
||||||
const Data::GroupCall::Participant &participant) const {
|
const Data::GroupCall::Participant &participant) {
|
||||||
auto result = std::make_unique<Row>(_channel, participant.user);
|
auto result = std::make_unique<Row>(_channel, participant.user);
|
||||||
updateRow(result.get(), &participant);
|
updateRow(result.get(), &participant);
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -31,7 +31,7 @@ GroupCall::GroupCall(
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupCall::~GroupCall() {
|
GroupCall::~GroupCall() {
|
||||||
api().request(_unknownSourcesRequestId).cancel();
|
api().request(_unknownSsrcsRequestId).cancel();
|
||||||
api().request(_participantsRequestId).cancel();
|
api().request(_participantsRequestId).cancel();
|
||||||
api().request(_reloadRequestId).cancel();
|
api().request(_reloadRequestId).cancel();
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ void GroupCall::requestParticipants() {
|
||||||
_participantsRequestId = api().request(MTPphone_GetGroupParticipants(
|
_participantsRequestId = api().request(MTPphone_GetGroupParticipants(
|
||||||
input(),
|
input(),
|
||||||
MTP_vector<MTPint>(), // ids
|
MTP_vector<MTPint>(), // ids
|
||||||
MTP_vector<MTPint>(), // sources
|
MTP_vector<MTPint>(), // ssrcs
|
||||||
MTP_string(_nextOffset),
|
MTP_string(_nextOffset),
|
||||||
MTP_int(kRequestPerPage)
|
MTP_int(kRequestPerPage)
|
||||||
)).done([=](const MTPphone_GroupParticipants &result) {
|
)).done([=](const MTPphone_GroupParticipants &result) {
|
||||||
|
@ -105,9 +105,9 @@ bool GroupCall::participantsLoaded() const {
|
||||||
return _allReceived;
|
return _allReceived;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserData *GroupCall::userBySource(uint32 source) const {
|
UserData *GroupCall::userBySsrc(uint32 ssrc) const {
|
||||||
const auto i = _userBySource.find(source);
|
const auto i = _userBySsrc.find(ssrc);
|
||||||
return (i != end(_userBySource)) ? i->second.get() : nullptr;
|
return (i != end(_userBySsrc)) ? i->second.get() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<> GroupCall::participantsSliceAdded() {
|
rpl::producer<> GroupCall::participantsSliceAdded() {
|
||||||
|
@ -163,7 +163,7 @@ void GroupCall::reload() {
|
||||||
result.match([&](const MTPDphone_groupCall &data) {
|
result.match([&](const MTPDphone_groupCall &data) {
|
||||||
_channel->owner().processUsers(data.vusers());
|
_channel->owner().processUsers(data.vusers());
|
||||||
_participants.clear();
|
_participants.clear();
|
||||||
_userBySource.clear();
|
_userBySsrc.clear();
|
||||||
applyParticipantsSlice(
|
applyParticipantsSlice(
|
||||||
data.vparticipants().v,
|
data.vparticipants().v,
|
||||||
ApplySliceSource::SliceLoaded);
|
ApplySliceSource::SliceLoaded);
|
||||||
|
@ -194,7 +194,7 @@ void GroupCall::applyParticipantsSlice(
|
||||||
auto update = ParticipantUpdate{
|
auto update = ParticipantUpdate{
|
||||||
.was = *i,
|
.was = *i,
|
||||||
};
|
};
|
||||||
_userBySource.erase(i->source);
|
_userBySsrc.erase(i->ssrc);
|
||||||
_participants.erase(i);
|
_participants.erase(i);
|
||||||
if (sliceSource != ApplySliceSource::SliceLoaded) {
|
if (sliceSource != ApplySliceSource::SliceLoaded) {
|
||||||
_participantUpdates.fire(std::move(update));
|
_participantUpdates.fire(std::move(update));
|
||||||
|
@ -212,20 +212,20 @@ void GroupCall::applyParticipantsSlice(
|
||||||
.user = user,
|
.user = user,
|
||||||
.date = data.vdate().v,
|
.date = data.vdate().v,
|
||||||
.lastActive = was ? was->lastActive : 0,
|
.lastActive = was ? was->lastActive : 0,
|
||||||
.source = uint32(data.vsource().v),
|
.ssrc = uint32(data.vsource().v),
|
||||||
.speaking = !data.is_muted() && (was ? was->speaking : false),
|
.speaking = !data.is_muted() && (was ? was->speaking : false),
|
||||||
.muted = data.is_muted(),
|
.muted = data.is_muted(),
|
||||||
.canSelfUnmute = !data.is_muted() || data.is_can_self_unmute(),
|
.canSelfUnmute = !data.is_muted() || data.is_can_self_unmute(),
|
||||||
};
|
};
|
||||||
if (i == end(_participants)) {
|
if (i == end(_participants)) {
|
||||||
_userBySource.emplace(value.source, user);
|
_userBySsrc.emplace(value.ssrc, user);
|
||||||
_participants.push_back(value);
|
_participants.push_back(value);
|
||||||
_channel->owner().unregisterInvitedToCallUser(_id, user);
|
_channel->owner().unregisterInvitedToCallUser(_id, user);
|
||||||
++changedCount;
|
++changedCount;
|
||||||
} else {
|
} else {
|
||||||
if (i->source != value.source) {
|
if (i->ssrc != value.ssrc) {
|
||||||
_userBySource.erase(i->source);
|
_userBySsrc.erase(i->ssrc);
|
||||||
_userBySource.emplace(value.source, user);
|
_userBySsrc.emplace(value.ssrc, user);
|
||||||
}
|
}
|
||||||
*i = value;
|
*i = value;
|
||||||
}
|
}
|
||||||
|
@ -271,11 +271,11 @@ void GroupCall::applyParticipantsMutes(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupCall::applyLastSpoke(uint32 source, crl::time when, crl::time now) {
|
void GroupCall::applyLastSpoke(uint32 ssrc, crl::time when, crl::time now) {
|
||||||
const auto i = _userBySource.find(source);
|
const auto i = _userBySsrc.find(ssrc);
|
||||||
if (i == end(_userBySource)) {
|
if (i == end(_userBySsrc)) {
|
||||||
_unknownSpokenSources.emplace(source, when);
|
_unknownSpokenSsrcs.emplace(ssrc, when);
|
||||||
requestUnknownSources();
|
requestUnknownSsrcs();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto j = ranges::find(_participants, i->second, &Participant::user);
|
const auto j = ranges::find(_participants, i->second, &Participant::user);
|
||||||
|
@ -292,32 +292,32 @@ void GroupCall::applyLastSpoke(uint32 source, crl::time when, crl::time now) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupCall::requestUnknownSources() {
|
void GroupCall::requestUnknownSsrcs() {
|
||||||
if (_unknownSourcesRequestId || _unknownSpokenSources.empty()) {
|
if (_unknownSsrcsRequestId || _unknownSpokenSsrcs.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto sources = [&] {
|
const auto ssrcs = [&] {
|
||||||
if (_unknownSpokenSources.size() < kRequestPerPage) {
|
if (_unknownSpokenSsrcs.size() < kRequestPerPage) {
|
||||||
return base::take(_unknownSpokenSources);
|
return base::take(_unknownSpokenSsrcs);
|
||||||
}
|
}
|
||||||
auto result = base::flat_map<uint32, crl::time>();
|
auto result = base::flat_map<uint32, crl::time>();
|
||||||
result.reserve(kRequestPerPage);
|
result.reserve(kRequestPerPage);
|
||||||
while (result.size() < kRequestPerPage) {
|
while (result.size() < kRequestPerPage) {
|
||||||
const auto [source, when] = _unknownSpokenSources.back();
|
const auto [ssrc, when] = _unknownSpokenSsrcs.back();
|
||||||
result.emplace(source, when);
|
result.emplace(ssrc, when);
|
||||||
_unknownSpokenSources.erase(_unknownSpokenSources.end() - 1);
|
_unknownSpokenSsrcs.erase(_unknownSpokenSsrcs.end() - 1);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}();
|
}();
|
||||||
auto sourceInputs = QVector<MTPint>();
|
auto inputs = QVector<MTPint>();
|
||||||
sourceInputs.reserve(sources.size());
|
inputs.reserve(ssrcs.size());
|
||||||
for (const auto [source, when] : sources) {
|
for (const auto [ssrc, when] : ssrcs) {
|
||||||
sourceInputs.push_back(MTP_int(source));
|
inputs.push_back(MTP_int(ssrc));
|
||||||
}
|
}
|
||||||
_unknownSourcesRequestId = api().request(MTPphone_GetGroupParticipants(
|
_unknownSsrcsRequestId = api().request(MTPphone_GetGroupParticipants(
|
||||||
input(),
|
input(),
|
||||||
MTP_vector<MTPint>(), // ids
|
MTP_vector<MTPint>(), // ids
|
||||||
MTP_vector<MTPint>(sourceInputs),
|
MTP_vector<MTPint>(inputs),
|
||||||
MTP_string(QString()),
|
MTP_string(QString()),
|
||||||
MTP_int(kRequestPerPage)
|
MTP_int(kRequestPerPage)
|
||||||
)).done([=](const MTPphone_GroupParticipants &result) {
|
)).done([=](const MTPphone_GroupParticipants &result) {
|
||||||
|
@ -327,19 +327,19 @@ void GroupCall::requestUnknownSources() {
|
||||||
data.vparticipants().v,
|
data.vparticipants().v,
|
||||||
ApplySliceSource::UnknownLoaded);
|
ApplySliceSource::UnknownLoaded);
|
||||||
});
|
});
|
||||||
_unknownSourcesRequestId = 0;
|
_unknownSsrcsRequestId = 0;
|
||||||
const auto now = crl::now();
|
const auto now = crl::now();
|
||||||
for (const auto [source, when] : sources) {
|
for (const auto [ssrc, when] : ssrcs) {
|
||||||
applyLastSpoke(source, when, now);
|
applyLastSpoke(ssrc, when, now);
|
||||||
_unknownSpokenSources.remove(source);
|
_unknownSpokenSsrcs.remove(ssrc);
|
||||||
}
|
}
|
||||||
requestUnknownSources();
|
requestUnknownSsrcs();
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
_unknownSourcesRequestId = 0;
|
_unknownSsrcsRequestId = 0;
|
||||||
for (const auto [source, when] : sources) {
|
for (const auto [ssrc, when] : ssrcs) {
|
||||||
_unknownSpokenSources.remove(source);
|
_unknownSpokenSsrcs.remove(ssrc);
|
||||||
}
|
}
|
||||||
requestUnknownSources();
|
requestUnknownSsrcs();
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ public:
|
||||||
not_null<UserData*> user;
|
not_null<UserData*> user;
|
||||||
TimeId date = 0;
|
TimeId date = 0;
|
||||||
TimeId lastActive = 0;
|
TimeId lastActive = 0;
|
||||||
uint32 source = 0;
|
uint32 ssrc = 0;
|
||||||
bool speaking = false;
|
bool speaking = false;
|
||||||
bool muted = false;
|
bool muted = false;
|
||||||
bool canSelfUnmute = false;
|
bool canSelfUnmute = false;
|
||||||
|
@ -41,7 +41,7 @@ public:
|
||||||
-> const std::vector<Participant> &;
|
-> const std::vector<Participant> &;
|
||||||
void requestParticipants();
|
void requestParticipants();
|
||||||
[[nodiscard]] bool participantsLoaded() const;
|
[[nodiscard]] bool participantsLoaded() const;
|
||||||
[[nodiscard]] UserData *userBySource(uint32 source) const;
|
[[nodiscard]] UserData *userBySsrc(uint32 ssrc) const;
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<> participantsSliceAdded();
|
[[nodiscard]] rpl::producer<> participantsSliceAdded();
|
||||||
[[nodiscard]] rpl::producer<ParticipantUpdate> participantUpdated() const;
|
[[nodiscard]] rpl::producer<ParticipantUpdate> participantUpdated() const;
|
||||||
|
@ -50,7 +50,7 @@ public:
|
||||||
void applyUpdate(const MTPDupdateGroupCallParticipants &update);
|
void applyUpdate(const MTPDupdateGroupCallParticipants &update);
|
||||||
void applyUpdateChecked(
|
void applyUpdateChecked(
|
||||||
const MTPDupdateGroupCallParticipants &update);
|
const MTPDupdateGroupCallParticipants &update);
|
||||||
void applyLastSpoke(uint32 source, crl::time when, crl::time now);
|
void applyLastSpoke(uint32 ssrc, crl::time when, crl::time now);
|
||||||
|
|
||||||
[[nodiscard]] int fullCount() const;
|
[[nodiscard]] int fullCount() const;
|
||||||
[[nodiscard]] rpl::producer<int> fullCountValue() const;
|
[[nodiscard]] rpl::producer<int> fullCountValue() const;
|
||||||
|
@ -75,7 +75,7 @@ private:
|
||||||
ApplySliceSource sliceSource);
|
ApplySliceSource sliceSource);
|
||||||
void applyParticipantsMutes(
|
void applyParticipantsMutes(
|
||||||
const MTPDupdateGroupCallParticipants &update);
|
const MTPDupdateGroupCallParticipants &update);
|
||||||
void requestUnknownSources();
|
void requestUnknownSsrcs();
|
||||||
|
|
||||||
const not_null<ChannelData*> _channel;
|
const not_null<ChannelData*> _channel;
|
||||||
const uint64 _id = 0;
|
const uint64 _id = 0;
|
||||||
|
@ -86,12 +86,12 @@ private:
|
||||||
mtpRequestId _reloadRequestId = 0;
|
mtpRequestId _reloadRequestId = 0;
|
||||||
|
|
||||||
std::vector<Participant> _participants;
|
std::vector<Participant> _participants;
|
||||||
base::flat_map<uint32, not_null<UserData*>> _userBySource;
|
base::flat_map<uint32, not_null<UserData*>> _userBySsrc;
|
||||||
QString _nextOffset;
|
QString _nextOffset;
|
||||||
rpl::variable<int> _fullCount = 0;
|
rpl::variable<int> _fullCount = 0;
|
||||||
|
|
||||||
base::flat_map<uint32, crl::time> _unknownSpokenSources;
|
base::flat_map<uint32, crl::time> _unknownSpokenSsrcs;
|
||||||
mtpRequestId _unknownSourcesRequestId = 0;
|
mtpRequestId _unknownSsrcsRequestId = 0;
|
||||||
|
|
||||||
rpl::event_stream<ParticipantUpdate> _participantUpdates;
|
rpl::event_stream<ParticipantUpdate> _participantUpdates;
|
||||||
rpl::event_stream<> _participantsSliceAdded;
|
rpl::event_stream<> _participantsSliceAdded;
|
||||||
|
|
Loading…
Add table
Reference in a new issue