Show confcall users that are only on blockchain.

This commit is contained in:
John Preston 2025-03-30 23:22:33 +05:00
parent e33a866a63
commit be611c1920
4 changed files with 209 additions and 50 deletions

View file

@ -4759,6 +4759,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_invite" = "Invite Members";
"lng_group_call_invite_conf" = "Add People";
"lng_group_call_invited_status" = "invited";
"lng_group_call_blockchain_only_status" = "syncing...";
"lng_group_call_muted_by_me_status" = "muted for you";
"lng_group_call_invite_title" = "Invite members";
"lng_group_call_invite_button" = "Invite";

View file

@ -108,6 +108,8 @@ private:
const Data::GroupCallParticipant &participant);
[[nodiscard]] std::unique_ptr<Row> createInvitedRow(
not_null<PeerData*> participantPeer);
[[nodiscard]] std::unique_ptr<Row> createWithAccessRow(
not_null<PeerData*> participantPeer);
[[nodiscard]] bool isMe(not_null<PeerData*> participantPeer) const;
void prepareRows(not_null<Data::GroupCall*> real);
@ -128,7 +130,8 @@ private:
void updateRow(
not_null<Row*> row,
const std::optional<Data::GroupCallParticipant> &was,
const Data::GroupCallParticipant *participant);
const Data::GroupCallParticipant *participant,
Row::State noParticipantState = Row::State::Invited);
void updateRowInSoundingMap(
not_null<Row*> row,
bool wasSounding,
@ -162,8 +165,13 @@ private:
const VideoEndpoint &endpoint,
bool active);
void appendInvitedUsers();
void partitionRows();
void setupInvitedUsers();
[[nodiscard]] bool appendInvitedUsers();
void setupWithAccessUsers();
[[nodiscard]] bool appendWithAccessUsers();
void scheduleRaisedHandStatusRemove();
void refreshWithAccessRows(base::flat_set<UserId> &&nowIds);
void hideRowsWithVideoExcept(const VideoEndpoint &large);
void showAllHiddenRows();
@ -205,6 +213,8 @@ private:
Ui::RoundRect _narrowRoundRect;
QImage _narrowShadow;
base::flat_set<UserId> _withAccess;
rpl::lifetime _lifetime;
};
@ -414,6 +424,9 @@ void Members::Controller::subscribeToChanges(not_null<Data::GroupCall*> real) {
if (const auto row = findRow(participantPeer)) {
if (isMe(participantPeer)) {
updateRow(row, update.was, nullptr);
} else if (_withAccess.contains(peerToUser(participantPeer->id))) {
updateRow(row, update.was, nullptr, Row::State::WithAccess);
partitionRows();
} else {
removeRow(row);
delegate()->peerListRefreshRows();
@ -431,10 +444,6 @@ void Members::Controller::subscribeToChanges(not_null<Data::GroupCall*> real) {
) | rpl::start_with_next([=](const VideoStateToggle &update) {
toggleVideoEndpointActive(update.endpoint, update.value);
}, _lifetime);
if (_prepared) {
appendInvitedUsers();
}
}
void Members::Controller::toggleVideoEndpointActive(
@ -481,13 +490,21 @@ void Members::Controller::toggleVideoEndpointActive(
}
void Members::Controller::appendInvitedUsers() {
bool Members::Controller::appendInvitedUsers() {
auto changed = false;
if (const auto id = _call->id()) {
for (const auto &user : _peer->owner().invitedToCallUsers(id)) {
if (auto row = createInvitedRow(user)) {
delegate()->peerListAppendRow(std::move(row));
changed = true;
}
}
}
return changed;
}
void Members::Controller::setupInvitedUsers() {
if (appendInvitedUsers()) {
delegate()->peerListRefreshRows();
}
@ -503,15 +520,79 @@ void Members::Controller::appendInvitedUsers() {
}, _lifetime);
}
bool Members::Controller::appendWithAccessUsers() {
auto changed = false;
for (const auto id : _withAccess) {
if (auto row = createWithAccessRow(_peer->owner().user(id))) {
changed = true;
delegate()->peerListAppendRow(std::move(row));
}
}
return changed;
}
void Members::Controller::setupWithAccessUsers() {
const auto conference = _call->conferenceCall().get();
if (!conference) {
return;
}
conference->participantsWithAccessValue(
) | rpl::start_with_next([=](base::flat_set<UserId> &&nowIds) {
for (auto i = begin(_withAccess); i != end(_withAccess);) {
const auto oldId = *i;
if (nowIds.remove(oldId)) {
++i;
continue;
}
const auto user = _peer->owner().user(oldId);
if (const auto row = findRow(user)) {
if (row->state() == Row::State::WithAccess) {
removeRow(row);
}
}
i = _withAccess.erase(i);
}
auto partition = false;
auto partitionChecked = false;
for (const auto nowId : nowIds) {
const auto user = _peer->owner().user(nowId);
if (!findRow(user)) {
if (auto row = createWithAccessRow(user)) {
if (!partitionChecked) {
partitionChecked = true;
if (const auto count = delegate()->peerListFullRowsCount()) {
const auto last = delegate()->peerListRowAt(count - 1);
const auto state = static_cast<Row*>(last.get())->state();
if (state == Row::State::Invited) {
partition = true;
}
}
}
delegate()->peerListAppendRow(std::move(row));
}
}
_withAccess.emplace(nowId);
}
if (partition) {
delegate()->peerListPartitionRows([](const PeerListRow &row) {
const auto state = static_cast<const Row&>(row).state();
return (state != Row::State::Invited);
});
}
delegate()->peerListRefreshRows();
}, _lifetime);
}
void Members::Controller::updateRow(
const std::optional<Data::GroupCallParticipant> &was,
const Data::GroupCallParticipant &now) {
auto reorderIfInvitedBefore = 0;
auto reorderIfNonRealBefore = 0;
auto checkPosition = (Row*)nullptr;
auto addedToBottom = (Row*)nullptr;
if (const auto row = findRow(now.peer)) {
if (row->state() == Row::State::Invited) {
reorderIfInvitedBefore = row->absoluteIndex();
if (row->state() == Row::State::Invited
|| row->state() == Row::State::WithAccess) {
reorderIfNonRealBefore = row->absoluteIndex();
}
updateRow(row, was, &now);
if ((now.speaking && (!was || !was->speaking))
@ -523,7 +604,7 @@ void Members::Controller::updateRow(
if (row->speaking()) {
delegate()->peerListPrependRow(std::move(row));
} else {
reorderIfInvitedBefore = delegate()->peerListFullRowsCount();
reorderIfNonRealBefore = delegate()->peerListFullRowsCount();
if (now.raisedHandRating != 0) {
checkPosition = row.get();
} else {
@ -533,20 +614,19 @@ void Members::Controller::updateRow(
}
delegate()->peerListRefreshRows();
}
static constexpr auto kInvited = Row::State::Invited;
const auto reorder = [&] {
const auto count = reorderIfInvitedBefore;
const auto count = reorderIfNonRealBefore;
if (count <= 0) {
return false;
}
const auto row = delegate()->peerListRowAt(
reorderIfInvitedBefore - 1).get();
return (static_cast<Row*>(row)->state() == kInvited);
reorderIfNonRealBefore - 1).get();
using State = Row::State;
const auto state = static_cast<Row*>(row)->state();
return (state == State::Invited) || (state == State::WithAccess);
}();
if (reorder) {
delegate()->peerListPartitionRows([](const PeerListRow &row) {
return static_cast<const Row&>(row).state() != kInvited;
});
partitionRows();
}
if (checkPosition) {
checkRowPosition(checkPosition);
@ -570,6 +650,24 @@ void Members::Controller::updateRow(
}
}
void Members::Controller::partitionRows() {
auto hadWithAccess = false;
delegate()->peerListPartitionRows([&](const PeerListRow &row) {
using State = Row::State;
const auto state = static_cast<const Row&>(row).state();
if (state == State::WithAccess) {
hadWithAccess = true;
}
return (state != State::Invited) && (state != State::WithAccess);
});
if (hadWithAccess) {
delegate()->peerListPartitionRows([](const PeerListRow &row) {
const auto state = static_cast<const Row&>(row).state();
return (state != Row::State::Invited);
});
}
}
bool Members::Controller::allRowsAboveAreSpeaking(not_null<Row*> row) const {
const auto count = delegate()->peerListFullRowsCount();
for (auto i = 0; i != count; ++i) {
@ -692,14 +790,21 @@ void Members::Controller::checkRowPosition(not_null<Row*> row) {
void Members::Controller::updateRow(
not_null<Row*> row,
const std::optional<Data::GroupCallParticipant> &was,
const Data::GroupCallParticipant *participant) {
const Data::GroupCallParticipant *participant,
Row::State noParticipantState) {
const auto wasSounding = row->sounding();
const auto wasSsrc = was ? was->ssrc : 0;
const auto wasAdditionalSsrc = was
? GetAdditionalAudioSsrc(was->videoParams)
: 0;
row->setSkipLevelUpdate(_skipRowLevelUpdate);
row->updateState(participant);
if (participant) {
row->updateState(*participant);
} else if (noParticipantState == Row::State::WithAccess) {
row->updateStateWithAccess();
} else {
row->updateStateInvited();
}
const auto wasNoSounding = _soundingRowBySsrc.empty();
updateRowInSoundingMap(
@ -842,7 +947,8 @@ void Members::Controller::prepare() {
}
loadMoreRows();
appendInvitedUsers();
setupWithAccessUsers();
setupInvitedUsers();
_prepared = true;
setupListChangeViewers();
@ -893,6 +999,12 @@ void Members::Controller::prepareRows(not_null<Data::GroupCall*> real) {
delegate()->peerListAppendRow(std::move(row));
}
}
if (appendWithAccessUsers()) {
changed = true;
}
if (appendInvitedUsers()) {
changed = true;
}
if (changed) {
delegate()->peerListRefreshRows();
}
@ -1354,8 +1466,9 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
}
const auto canKick = [&] {
const auto user = participantPeer->asUser();
if (static_cast<Row*>(row.get())->state()
== Row::State::Invited) {
const auto state = static_cast<Row*>(row.get())->state();
if (state == Row::State::Invited
|| state == Row::State::WithAccess) {
return false;
} else if (const auto chat = _peer->asChat()) {
return chat->amCreator()
@ -1484,6 +1597,7 @@ void Members::Controller::addMuteActionsToContextMenu(
const auto muteAction = [&]() -> QAction* {
if (muteState == Row::State::Invited
|| muteState == Row::State::WithAccess
|| _call->rtmp()
|| isMe(participantPeer)
|| (muteState == Row::State::Inactive
@ -1547,6 +1661,16 @@ std::unique_ptr<Row> Members::Controller::createInvitedRow(
return result;
}
std::unique_ptr<Row> Members::Controller::createWithAccessRow(
not_null<PeerData*> participantPeer) {
if (findRow(participantPeer)) {
return nullptr;
}
auto result = std::make_unique<Row>(this, participantPeer);
updateRow(result.get(), std::nullopt, nullptr, Row::State::WithAccess);
return result;
}
Members::Members(
not_null<QWidget*> parent,
not_null<GroupCall*> call,

View file

@ -138,41 +138,52 @@ void MembersRow::setSkipLevelUpdate(bool value) {
_skipLevelUpdate = value;
}
void MembersRow::updateState(
const Data::GroupCallParticipant *participant) {
setVolume(participant
? participant->volume
: Group::kDefaultVolume);
if (!participant) {
setState(State::Invited);
setSounding(false);
setSpeaking(false);
_mutedByMe = false;
_raisedHandRating = 0;
} else if (!participant->muted
|| (participant->sounding && participant->ssrc != 0)
|| (participant->additionalSounding
&& GetAdditionalAudioSsrc(participant->videoParams) != 0)) {
void MembersRow::updateStateInvited() {
setVolume(Group::kDefaultVolume);
setState(State::Invited);
setSounding(false);
setSpeaking(false);
_mutedByMe = false;
_raisedHandRating = 0;
refreshStatus();
}
void MembersRow::updateStateWithAccess() {
setVolume(Group::kDefaultVolume);
setState(State::WithAccess);
setSounding(false);
setSpeaking(false);
_mutedByMe = false;
_raisedHandRating = 0;
refreshStatus();
}
void MembersRow::updateState(const Data::GroupCallParticipant &participant) {
setVolume(participant.volume);
if (!participant.muted
|| (participant.sounding && participant.ssrc != 0)
|| (participant.additionalSounding
&& GetAdditionalAudioSsrc(participant.videoParams) != 0)) {
setState(State::Active);
setSounding((participant->sounding && participant->ssrc != 0)
|| (participant->additionalSounding
&& GetAdditionalAudioSsrc(participant->videoParams) != 0));
setSpeaking((participant->speaking && participant->ssrc != 0)
|| (participant->additionalSpeaking
&& GetAdditionalAudioSsrc(participant->videoParams) != 0));
_mutedByMe = participant->mutedByMe;
setSounding((participant.sounding && participant.ssrc != 0)
|| (participant.additionalSounding
&& GetAdditionalAudioSsrc(participant.videoParams) != 0));
setSpeaking((participant.speaking && participant.ssrc != 0)
|| (participant.additionalSpeaking
&& GetAdditionalAudioSsrc(participant.videoParams) != 0));
_mutedByMe = participant.mutedByMe;
_raisedHandRating = 0;
} else if (participant->canSelfUnmute) {
} else if (participant.canSelfUnmute) {
setState(State::Inactive);
setSounding(false);
setSpeaking(false);
_mutedByMe = participant->mutedByMe;
_mutedByMe = participant.mutedByMe;
_raisedHandRating = 0;
} else {
setSounding(false);
setSpeaking(false);
_mutedByMe = participant->mutedByMe;
_raisedHandRating = participant->raisedHandRating;
_mutedByMe = participant.mutedByMe;
_raisedHandRating = participant.raisedHandRating;
setState(_raisedHandRating ? State::RaisedHand : State::Muted);
}
refreshStatus();
@ -450,6 +461,20 @@ void MembersRow::paintMuteIcon(
_delegate->rowPaintIcon(p, iconRect, computeIconState(style));
}
QString MembersRow::generateName() {
const auto result = peer()->name();
return result.isEmpty()
? u"User #%1"_q.arg(peerToUser(peer()->id).bare)
: result;
}
QString MembersRow::generateShortName() {
const auto result = peer()->shortName();
return result.isEmpty()
? u"User #%1"_q.arg(peerToUser(peer()->id).bare)
: result;
}
auto MembersRow::generatePaintUserpicCallback(bool forceRound)
-> PaintRoundImageCallback {
return [=](Painter &p, int x, int y, int outerWidth, int size) {
@ -613,11 +638,13 @@ void MembersRow::paintComplexStatusText(
availableWidth -= skip;
const auto &font = st::normalFont;
const auto useAbout = !_about.isEmpty()
&& (_state != State::WithAccess)
&& (style != MembersRowStyle::Video)
&& ((_state == State::RaisedHand && !_raisedHandStatus)
|| (_state != State::RaisedHand && !_speaking));
if (!useAbout
&& _state != State::Invited
&& _state != State::WithAccess
&& !_mutedByMe) {
paintStatusIcon(p, x, y, st, font, selected, narrowMode);
@ -663,6 +690,8 @@ void MembersRow::paintComplexStatusText(
? tr::lng_group_call_muted_by_me_status(tr::now)
: _delegate->rowIsMe(peer())
? tr::lng_status_connecting(tr::now)
: (_state == State::WithAccess)
? tr::lng_group_call_blockchain_only_status(tr::now)
: tr::lng_group_call_invited_status(tr::now)));
}
}

View file

@ -75,11 +75,14 @@ public:
Muted,
RaisedHand,
Invited,
WithAccess,
};
void setAbout(const QString &about);
void setSkipLevelUpdate(bool value);
void updateState(const Data::GroupCallParticipant *participant);
void updateState(const Data::GroupCallParticipant &participant);
void updateStateInvited();
void updateStateWithAccess();
void updateLevel(float level);
void updateBlobAnimation(crl::time now);
void clearRaisedHandStatus();
@ -122,6 +125,8 @@ public:
bool selected,
bool actionSelected) override;
QString generateName() override;
QString generateShortName() override;
PaintRoundImageCallback generatePaintUserpicCallback(
bool forceRound) override;
void paintComplexUserpic(