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" = "Invite Members";
"lng_group_call_invite_conf" = "Add People"; "lng_group_call_invite_conf" = "Add People";
"lng_group_call_invited_status" = "invited"; "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_muted_by_me_status" = "muted for you";
"lng_group_call_invite_title" = "Invite members"; "lng_group_call_invite_title" = "Invite members";
"lng_group_call_invite_button" = "Invite"; "lng_group_call_invite_button" = "Invite";

View file

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

View file

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

View file

@ -75,11 +75,14 @@ public:
Muted, Muted,
RaisedHand, RaisedHand,
Invited, Invited,
WithAccess,
}; };
void setAbout(const QString &about); void setAbout(const QString &about);
void setSkipLevelUpdate(bool value); 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 updateLevel(float level);
void updateBlobAnimation(crl::time now); void updateBlobAnimation(crl::time now);
void clearRaisedHandStatus(); void clearRaisedHandStatus();
@ -122,6 +125,8 @@ public:
bool selected, bool selected,
bool actionSelected) override; bool actionSelected) override;
QString generateName() override;
QString generateShortName() override;
PaintRoundImageCallback generatePaintUserpicCallback( PaintRoundImageCallback generatePaintUserpicCallback(
bool forceRound) override; bool forceRound) override;
void paintComplexUserpic( void paintComplexUserpic(