Add pending requests bar in the chat.
This commit is contained in:
		
							parent
							
								
									0dfbd5fa6e
								
							
						
					
					
						commit
						7543351bc9
					
				
					 18 changed files with 682 additions and 66 deletions
				
			
		| 
						 | 
				
			
			@ -621,8 +621,8 @@ PRIVATE
 | 
			
		|||
    history/view/history_view_emoji_interactions.h
 | 
			
		||||
    history/view/history_view_empty_list_bubble.cpp
 | 
			
		||||
    history/view/history_view_empty_list_bubble.h
 | 
			
		||||
    history/view/history_view_group_call_tracker.cpp
 | 
			
		||||
    history/view/history_view_group_call_tracker.h
 | 
			
		||||
    history/view/history_view_group_call_bar.cpp
 | 
			
		||||
    history/view/history_view_group_call_bar.h
 | 
			
		||||
    history/view/history_view_list_widget.cpp
 | 
			
		||||
    history/view/history_view_list_widget.h
 | 
			
		||||
    history/view/history_view_message.cpp
 | 
			
		||||
| 
						 | 
				
			
			@ -636,6 +636,8 @@ PRIVATE
 | 
			
		|||
    history/view/history_view_pinned_tracker.h
 | 
			
		||||
    history/view/history_view_replies_section.cpp
 | 
			
		||||
    history/view/history_view_replies_section.h
 | 
			
		||||
    history/view/history_view_requests_bar.cpp
 | 
			
		||||
    history/view/history_view_requests_bar.h
 | 
			
		||||
    history/view/history_view_schedule_box.cpp
 | 
			
		||||
    history/view/history_view_schedule_box.h
 | 
			
		||||
    history/view/history_view_scheduled_section.cpp
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1350,8 +1350,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		|||
"lng_group_requests_dismiss" = "Dismiss";
 | 
			
		||||
"lng_group_requests_was_added" = "{user} has been added to the group.";
 | 
			
		||||
"lng_group_requests_was_added_channel" = "{user} has been added to the channel.";
 | 
			
		||||
"lng_group_requests_none" = "No member requests";
 | 
			
		||||
"lng_group_requests_none_channel" = "No subscriber requests";
 | 
			
		||||
"lng_group_requests_none" = "You have no pending requests\nto join your group.";
 | 
			
		||||
"lng_group_requests_none_channel" = "You have no pending requests\nto join your channel.";
 | 
			
		||||
 | 
			
		||||
"lng_channel_public_link_copied" = "Link copied to clipboard.";
 | 
			
		||||
"lng_context_about_private_link" = "This link will only work for members of this chat.";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1108,23 +1108,13 @@ void Controller::fillPendingRequestsButton() {
 | 
			
		|||
			_controls.buttonsLayout,
 | 
			
		||||
			object_ptr<Ui::VerticalLayout>(
 | 
			
		||||
				_controls.buttonsLayout)));
 | 
			
		||||
	const auto showPendingRequestsBox = [=] {
 | 
			
		||||
		auto controller = std::make_unique<RequestsBoxController>(
 | 
			
		||||
			_navigation,
 | 
			
		||||
			_peer->migrateToOrMe());
 | 
			
		||||
		const auto initBox = [=](not_null<PeerListBox*> box) {
 | 
			
		||||
			box->addButton(tr::lng_close(), [=] { box->closeBox(); });
 | 
			
		||||
		};
 | 
			
		||||
		_navigation->parentController()->show(
 | 
			
		||||
			Box<PeerListBox>(std::move(controller), initBox));
 | 
			
		||||
	};
 | 
			
		||||
	AddButtonWithCount(
 | 
			
		||||
		wrap->entity(),
 | 
			
		||||
		(_isGroup
 | 
			
		||||
			? tr::lng_manage_peer_requests()
 | 
			
		||||
			: tr::lng_manage_peer_requests_channel()),
 | 
			
		||||
		rpl::duplicate(pendingRequestsCount) | ToPositiveNumberString(),
 | 
			
		||||
		showPendingRequestsBox,
 | 
			
		||||
		[=] { RequestsBoxController::Start(_navigation, _peer); },
 | 
			
		||||
		st::infoIconRequests);
 | 
			
		||||
	std::move(
 | 
			
		||||
		pendingRequestsCount
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		|||
#include "ui/text/text_utilities.h"
 | 
			
		||||
#include "ui/boxes/edit_invite_link.h"
 | 
			
		||||
#include "boxes/share_box.h"
 | 
			
		||||
#include "history/view/history_view_group_call_tracker.h" // GenerateUs...
 | 
			
		||||
#include "history/view/history_view_group_call_bar.h" // GenerateUserpics...
 | 
			
		||||
#include "history/history_message.h" // GetErrorTextForSending.
 | 
			
		||||
#include "history/history.h"
 | 
			
		||||
#include "ui/boxes/confirm_box.h"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -256,6 +256,19 @@ RequestsBoxController::RequestsBoxController(
 | 
			
		|||
 | 
			
		||||
RequestsBoxController::~RequestsBoxController() = default;
 | 
			
		||||
 | 
			
		||||
void RequestsBoxController::Start(
 | 
			
		||||
		not_null<Window::SessionNavigation*> navigation,
 | 
			
		||||
		not_null<PeerData*> peer) {
 | 
			
		||||
	auto controller = std::make_unique<RequestsBoxController>(
 | 
			
		||||
		navigation,
 | 
			
		||||
		peer->migrateToOrMe());
 | 
			
		||||
	const auto initBox = [=](not_null<PeerListBox*> box) {
 | 
			
		||||
		box->addButton(tr::lng_close(), [=] { box->closeBox(); });
 | 
			
		||||
	};
 | 
			
		||||
	navigation->parentController()->show(
 | 
			
		||||
		Box<PeerListBox>(std::move(controller), initBox));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Main::Session &RequestsBoxController::session() const {
 | 
			
		||||
	return _peer->session();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,10 @@ public:
 | 
			
		|||
		not_null<PeerData*> peer);
 | 
			
		||||
	~RequestsBoxController();
 | 
			
		||||
 | 
			
		||||
	static void Start(
 | 
			
		||||
		not_null<Window::SessionNavigation*> navigation,
 | 
			
		||||
		not_null<PeerData*> peer);
 | 
			
		||||
 | 
			
		||||
	Main::Session &session() const override;
 | 
			
		||||
	void prepare() override;
 | 
			
		||||
	void rowClicked(not_null<PeerListRow*> row) override;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		|||
#include "calls/calls_signal_bars.h"
 | 
			
		||||
#include "calls/group/calls_group_call.h"
 | 
			
		||||
#include "calls/group/calls_group_menu.h" // Group::LeaveBox.
 | 
			
		||||
#include "history/view/history_view_group_call_tracker.h" // ContentByCall.
 | 
			
		||||
#include "history/view/history_view_group_call_bar.h" // ContentByCall.
 | 
			
		||||
#include "data/data_user.h"
 | 
			
		||||
#include "data/data_group_call.h"
 | 
			
		||||
#include "data/data_peer.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -582,7 +582,7 @@ void TopBar::subscribeToMembersChanges(not_null<GroupCall*> call) {
 | 
			
		|||
		}, lifetime());
 | 
			
		||||
	}) | rpl::map([=](not_null<Data::GroupCall*> real) {
 | 
			
		||||
 | 
			
		||||
		return HistoryView::GroupCallTracker::ContentByCall(
 | 
			
		||||
		return HistoryView::GroupCallBarContentByCall(
 | 
			
		||||
			real,
 | 
			
		||||
			st::groupCallTopBarUserpics.size);
 | 
			
		||||
	}) | rpl::flatten_latest(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		|||
#include "boxes/share_box.h"
 | 
			
		||||
#include "boxes/edit_caption_box.h"
 | 
			
		||||
#include "boxes/peers/edit_peer_permissions_box.h" // ShowAboutGigagroup.
 | 
			
		||||
#include "boxes/peers/edit_peer_requests_box.h"
 | 
			
		||||
#include "core/file_utilities.h"
 | 
			
		||||
#include "ui/toast/toast.h"
 | 
			
		||||
#include "ui/toasts/common_toasts.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -81,7 +82,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		|||
#include "history/view/history_view_pinned_tracker.h"
 | 
			
		||||
#include "history/view/history_view_pinned_section.h"
 | 
			
		||||
#include "history/view/history_view_pinned_bar.h"
 | 
			
		||||
#include "history/view/history_view_group_call_tracker.h"
 | 
			
		||||
#include "history/view/history_view_group_call_bar.h"
 | 
			
		||||
#include "history/view/history_view_requests_bar.h"
 | 
			
		||||
#include "history/view/media/history_view_media.h"
 | 
			
		||||
#include "profile/profile_block_group_members.h"
 | 
			
		||||
#include "info/info_memento.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +111,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		|||
#include "ui/boxes/report_box.h"
 | 
			
		||||
#include "ui/chat/pinned_bar.h"
 | 
			
		||||
#include "ui/chat/group_call_bar.h"
 | 
			
		||||
#include "ui/chat/requests_bar.h"
 | 
			
		||||
#include "ui/chat/chat_theme.h"
 | 
			
		||||
#include "ui/chat/chat_style.h"
 | 
			
		||||
#include "ui/chat/continuous_scroll.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -1416,6 +1419,9 @@ void HistoryWidget::orderWidgets() {
 | 
			
		|||
	if (_groupCallBar) {
 | 
			
		||||
		_groupCallBar->raise();
 | 
			
		||||
	}
 | 
			
		||||
	if (_requestsBar) {
 | 
			
		||||
		_requestsBar->raise();
 | 
			
		||||
	}
 | 
			
		||||
	if (_chooseTheme) {
 | 
			
		||||
		_chooseTheme->raise();
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -2007,7 +2013,7 @@ void HistoryWidget::showHistory(
 | 
			
		|||
		_pinnedBar = nullptr;
 | 
			
		||||
		_pinnedTracker = nullptr;
 | 
			
		||||
		_groupCallBar = nullptr;
 | 
			
		||||
		_groupCallTracker = nullptr;
 | 
			
		||||
		_requestsBar = nullptr;
 | 
			
		||||
		_chooseTheme = nullptr;
 | 
			
		||||
		_membersDropdown.destroy();
 | 
			
		||||
		_scrollToAnimation.stop();
 | 
			
		||||
| 
						 | 
				
			
			@ -2134,7 +2140,8 @@ void HistoryWidget::showHistory(
 | 
			
		|||
		_updateHistoryItems.cancel();
 | 
			
		||||
 | 
			
		||||
		setupPinnedTracker();
 | 
			
		||||
		setupGroupCallTracker();
 | 
			
		||||
		setupGroupCallBar();
 | 
			
		||||
		setupRequestsBar();
 | 
			
		||||
		checkMessagesTTL();
 | 
			
		||||
		if (_history->scrollTopItem
 | 
			
		||||
			|| (_migrated && _migrated->scrollTopItem)
 | 
			
		||||
| 
						 | 
				
			
			@ -2404,6 +2411,9 @@ void HistoryWidget::updateControlsVisibility() {
 | 
			
		|||
	if (_groupCallBar) {
 | 
			
		||||
		_groupCallBar->show();
 | 
			
		||||
	}
 | 
			
		||||
	if (_requestsBar) {
 | 
			
		||||
		_requestsBar->show();
 | 
			
		||||
	}
 | 
			
		||||
	if (_firstLoadRequest && !_scroll->isHidden()) {
 | 
			
		||||
		_scroll->hide();
 | 
			
		||||
	} else if (!_firstLoadRequest && _scroll->isHidden()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -3377,6 +3387,9 @@ void HistoryWidget::hideChildWidgets() {
 | 
			
		|||
	if (_groupCallBar) {
 | 
			
		||||
		_groupCallBar->hide();
 | 
			
		||||
	}
 | 
			
		||||
	if (_requestsBar) {
 | 
			
		||||
		_requestsBar->hide();
 | 
			
		||||
	}
 | 
			
		||||
	if (_voiceRecordBar) {
 | 
			
		||||
		_voiceRecordBar->hideFast();
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -3617,6 +3630,9 @@ void HistoryWidget::showAnimated(
 | 
			
		|||
	if (_groupCallBar) {
 | 
			
		||||
		_groupCallBar->finishAnimating();
 | 
			
		||||
	}
 | 
			
		||||
	if (_requestsBar) {
 | 
			
		||||
		_requestsBar->finishAnimating();
 | 
			
		||||
	}
 | 
			
		||||
	_topShadow->setVisible(params.withTopBarShadow ? false : true);
 | 
			
		||||
	_preserveScrollTop = false;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3648,6 +3664,9 @@ void HistoryWidget::animationCallback() {
 | 
			
		|||
		if (_groupCallBar) {
 | 
			
		||||
			_groupCallBar->finishAnimating();
 | 
			
		||||
		}
 | 
			
		||||
		if (_requestsBar) {
 | 
			
		||||
			_requestsBar->finishAnimating();
 | 
			
		||||
		}
 | 
			
		||||
		_cacheUnder = _cacheOver = QPixmap();
 | 
			
		||||
		doneShow();
 | 
			
		||||
		synteticScrollToY(_scroll->scrollTop());
 | 
			
		||||
| 
						 | 
				
			
			@ -3674,6 +3693,9 @@ void HistoryWidget::doneShow() {
 | 
			
		|||
	if (_groupCallBar) {
 | 
			
		||||
		_groupCallBar->finishAnimating();
 | 
			
		||||
	}
 | 
			
		||||
	if (_requestsBar) {
 | 
			
		||||
		_requestsBar->finishAnimating();
 | 
			
		||||
	}
 | 
			
		||||
	checkHistoryActivation();
 | 
			
		||||
	controller()->widget()->setInnerFocus();
 | 
			
		||||
	_preserveScrollTop = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -4815,7 +4837,12 @@ void HistoryWidget::updateControlsGeometry() {
 | 
			
		|||
		_groupCallBar->move(0, groupCallTop);
 | 
			
		||||
		_groupCallBar->resizeToWidth(width());
 | 
			
		||||
	}
 | 
			
		||||
	const auto pinnedBarTop = groupCallTop + (_groupCallBar ? _groupCallBar->height() : 0);
 | 
			
		||||
	const auto requestsTop = groupCallTop + (_groupCallBar ? _groupCallBar->height() : 0);
 | 
			
		||||
	if (_requestsBar) {
 | 
			
		||||
		_requestsBar->move(0, requestsTop);
 | 
			
		||||
		_requestsBar->resizeToWidth(width());
 | 
			
		||||
	}
 | 
			
		||||
	const auto pinnedBarTop = requestsTop + (_requestsBar ? _requestsBar->height() : 0);
 | 
			
		||||
	if (_pinnedBar) {
 | 
			
		||||
		_pinnedBar->move(0, pinnedBarTop);
 | 
			
		||||
		_pinnedBar->resizeToWidth(width());
 | 
			
		||||
| 
						 | 
				
			
			@ -5000,6 +5027,9 @@ void HistoryWidget::updateHistoryGeometry(
 | 
			
		|||
	if (_groupCallBar) {
 | 
			
		||||
		newScrollHeight -= _groupCallBar->height();
 | 
			
		||||
	}
 | 
			
		||||
	if (_requestsBar) {
 | 
			
		||||
		newScrollHeight -= _requestsBar->height();
 | 
			
		||||
	}
 | 
			
		||||
	if (_contactStatus) {
 | 
			
		||||
		newScrollHeight -= _contactStatus->height();
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -5340,6 +5370,7 @@ int HistoryWidget::computeMaxFieldHeight() const {
 | 
			
		|||
		- (_contactStatus ? _contactStatus->height() : 0)
 | 
			
		||||
		- (_pinnedBar ? _pinnedBar->height() : 0)
 | 
			
		||||
		- (_groupCallBar ? _groupCallBar->height() : 0)
 | 
			
		||||
		- (_requestsBar ? _requestsBar->height() : 0)
 | 
			
		||||
		- ((_editMsgId
 | 
			
		||||
			|| replyToId()
 | 
			
		||||
			|| readyToForward()
 | 
			
		||||
| 
						 | 
				
			
			@ -5611,7 +5642,8 @@ void HistoryWidget::handlePeerMigration() {
 | 
			
		|||
		_migrated = _history->migrateFrom();
 | 
			
		||||
		_list->notifyMigrateUpdated();
 | 
			
		||||
		setupPinnedTracker();
 | 
			
		||||
		setupGroupCallTracker();
 | 
			
		||||
		setupGroupCallBar();
 | 
			
		||||
		setupRequestsBar();
 | 
			
		||||
		updateHistoryGeometry();
 | 
			
		||||
	}
 | 
			
		||||
	const auto from = chat->owner().historyLoaded(chat);
 | 
			
		||||
| 
						 | 
				
			
			@ -5979,20 +6011,19 @@ void HistoryWidget::refreshPinnedBarButton(bool many) {
 | 
			
		|||
	_pinnedBar->setRightButton(std::move(button));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HistoryWidget::setupGroupCallTracker() {
 | 
			
		||||
void HistoryWidget::setupGroupCallBar() {
 | 
			
		||||
	Expects(_history != nullptr);
 | 
			
		||||
 | 
			
		||||
	const auto peer = _history->peer;
 | 
			
		||||
	if (!peer->isChannel() && !peer->isChat()) {
 | 
			
		||||
		_groupCallTracker = nullptr;
 | 
			
		||||
		_groupCallBar = nullptr;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	_groupCallTracker = std::make_unique<HistoryView::GroupCallTracker>(
 | 
			
		||||
		peer);
 | 
			
		||||
	_groupCallBar = std::make_unique<Ui::GroupCallBar>(
 | 
			
		||||
		this,
 | 
			
		||||
		_groupCallTracker->content(),
 | 
			
		||||
		HistoryView::GroupCallBarContentByPeer(
 | 
			
		||||
			peer,
 | 
			
		||||
			st::historyGroupCallUserpics.size),
 | 
			
		||||
		Core::App().appDeactivatedValue());
 | 
			
		||||
 | 
			
		||||
	controller()->adaptive().oneColumnValue(
 | 
			
		||||
| 
						 | 
				
			
			@ -6032,6 +6063,52 @@ void HistoryWidget::setupGroupCallTracker() {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HistoryWidget::setupRequestsBar() {
 | 
			
		||||
	Expects(_history != nullptr);
 | 
			
		||||
 | 
			
		||||
	const auto peer = _history->peer;
 | 
			
		||||
	if (!peer->isChannel() && !peer->isChat()) {
 | 
			
		||||
		_requestsBar = nullptr;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	_requestsBar = std::make_unique<Ui::RequestsBar>(
 | 
			
		||||
		this,
 | 
			
		||||
		HistoryView::RequestsBarContentByPeer(
 | 
			
		||||
			peer,
 | 
			
		||||
			st::historyRequestsUserpics.size));
 | 
			
		||||
 | 
			
		||||
	controller()->adaptive().oneColumnValue(
 | 
			
		||||
	) | rpl::start_with_next([=](bool one) {
 | 
			
		||||
		_requestsBar->setShadowGeometryPostprocess([=](QRect geometry) {
 | 
			
		||||
			if (!one) {
 | 
			
		||||
				geometry.setLeft(geometry.left() + st::lineWidth);
 | 
			
		||||
			}
 | 
			
		||||
			return geometry;
 | 
			
		||||
		});
 | 
			
		||||
	}, _requestsBar->lifetime());
 | 
			
		||||
 | 
			
		||||
	_requestsBar->barClicks(
 | 
			
		||||
	) | rpl::start_with_next([=] {
 | 
			
		||||
		RequestsBoxController::Start(controller(), _peer);
 | 
			
		||||
	}, _requestsBar->lifetime());
 | 
			
		||||
 | 
			
		||||
	_requestsBarHeight = 0;
 | 
			
		||||
	_requestsBar->heightValue(
 | 
			
		||||
	) | rpl::start_with_next([=](int height) {
 | 
			
		||||
		_topDelta = _preserveScrollTop ? 0 : (height - _requestsBarHeight);
 | 
			
		||||
		_requestsBarHeight = height;
 | 
			
		||||
		updateHistoryGeometry();
 | 
			
		||||
		updateControlsGeometry();
 | 
			
		||||
		_topDelta = 0;
 | 
			
		||||
	}, _requestsBar->lifetime());
 | 
			
		||||
 | 
			
		||||
	orderWidgets();
 | 
			
		||||
 | 
			
		||||
	if (_a_show.animating()) {
 | 
			
		||||
		_requestsBar->hide();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HistoryWidget::requestMessageData(MsgId msgId) {
 | 
			
		||||
	const auto callback = [=](ChannelData *channel, MsgId msgId) {
 | 
			
		||||
		messageDataReceived(channel, msgId);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -73,6 +73,7 @@ class LinkButton;
 | 
			
		|||
class RoundButton;
 | 
			
		||||
class PinnedBar;
 | 
			
		||||
class GroupCallBar;
 | 
			
		||||
class RequestsBar;
 | 
			
		||||
struct PreparedList;
 | 
			
		||||
class SendFilesWay;
 | 
			
		||||
enum class ReportReason;
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +103,6 @@ class TopBarWidget;
 | 
			
		|||
class ContactStatus;
 | 
			
		||||
class Element;
 | 
			
		||||
class PinnedTracker;
 | 
			
		||||
class GroupCallTracker;
 | 
			
		||||
namespace Controls {
 | 
			
		||||
class RecordLock;
 | 
			
		||||
class VoiceRecordBar;
 | 
			
		||||
| 
						 | 
				
			
			@ -507,7 +507,8 @@ private:
 | 
			
		|||
		int nowScrollTop);
 | 
			
		||||
 | 
			
		||||
	void checkMessagesTTL();
 | 
			
		||||
	void setupGroupCallTracker();
 | 
			
		||||
	void setupGroupCallBar();
 | 
			
		||||
	void setupRequestsBar();
 | 
			
		||||
 | 
			
		||||
	void sendInlineResult(InlineBots::ResultSelected result);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -638,9 +639,10 @@ private:
 | 
			
		|||
	FullMsgId _pinnedClickedId;
 | 
			
		||||
	std::optional<FullMsgId> _minPinnedId;
 | 
			
		||||
 | 
			
		||||
	std::unique_ptr<HistoryView::GroupCallTracker> _groupCallTracker;
 | 
			
		||||
	std::unique_ptr<Ui::GroupCallBar> _groupCallBar;
 | 
			
		||||
	int _groupCallBarHeight = 0;
 | 
			
		||||
	std::unique_ptr<Ui::RequestsBar> _requestsBar;
 | 
			
		||||
	int _requestsBarHeight = 0;
 | 
			
		||||
 | 
			
		||||
	bool _preserveScrollTop = false;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
 | 
			
		|||
For license and copyright information please follow this link:
 | 
			
		||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		||||
*/
 | 
			
		||||
#include "history/view/history_view_group_call_tracker.h"
 | 
			
		||||
#include "history/view/history_view_group_call_bar.h"
 | 
			
		||||
 | 
			
		||||
#include "data/data_channel.h"
 | 
			
		||||
#include "data/data_user.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -62,11 +62,7 @@ void GenerateUserpicsInRow(
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GroupCallTracker::GroupCallTracker(not_null<PeerData*> peer)
 | 
			
		||||
: _peer(peer) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
 | 
			
		||||
rpl::producer<Ui::GroupCallBarContent> GroupCallBarContentByCall(
 | 
			
		||||
		not_null<Data::GroupCall*> call,
 | 
			
		||||
		int userpicSize) {
 | 
			
		||||
	struct State {
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +70,7 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
 | 
			
		|||
		Ui::GroupCallBarContent current;
 | 
			
		||||
		base::has_weak_ptr guard;
 | 
			
		||||
		bool someUserpicsNotLoaded = false;
 | 
			
		||||
		bool scheduled = false;
 | 
			
		||||
		bool pushScheduled = false;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// speaking DESC, std::max(date, lastActive) DESC
 | 
			
		||||
| 
						 | 
				
			
			@ -251,12 +247,12 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
 | 
			
		|||
		state->current.livestream = call->peer()->isBroadcast();
 | 
			
		||||
 | 
			
		||||
		const auto pushNext = [=] {
 | 
			
		||||
			if (state->scheduled) {
 | 
			
		||||
			if (state->pushScheduled) {
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			state->scheduled = true;
 | 
			
		||||
			state->pushScheduled = true;
 | 
			
		||||
			crl::on_main(&state->guard, [=] {
 | 
			
		||||
				state->scheduled = false;
 | 
			
		||||
				state->pushScheduled = false;
 | 
			
		||||
				consumer.put_next_copy(state->current);
 | 
			
		||||
			});
 | 
			
		||||
		};
 | 
			
		||||
| 
						 | 
				
			
			@ -350,8 +346,9 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::ContentByCall(
 | 
			
		|||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::content() const {
 | 
			
		||||
	const auto peer = _peer;
 | 
			
		||||
rpl::producer<Ui::GroupCallBarContent> GroupCallBarContentByPeer(
 | 
			
		||||
		not_null<PeerData*> peer,
 | 
			
		||||
		int userpicSize) {
 | 
			
		||||
	return rpl::combine(
 | 
			
		||||
		peer->session().changes().peerFlagsValue(
 | 
			
		||||
			peer,
 | 
			
		||||
| 
						 | 
				
			
			@ -363,19 +360,15 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::content() const {
 | 
			
		|||
			? call
 | 
			
		||||
			: nullptr;
 | 
			
		||||
	}) | rpl::distinct_until_changed(
 | 
			
		||||
	) | rpl::map([](Data::GroupCall *call)
 | 
			
		||||
	) | rpl::map([=](Data::GroupCall *call)
 | 
			
		||||
	-> rpl::producer<Ui::GroupCallBarContent> {
 | 
			
		||||
		if (!call) {
 | 
			
		||||
			return rpl::single(Ui::GroupCallBarContent{ .shown = false });
 | 
			
		||||
		} else if (!call->fullCount() && !call->participantsLoaded()) {
 | 
			
		||||
			call->reload();
 | 
			
		||||
		}
 | 
			
		||||
		return ContentByCall(call, st::historyGroupCallUserpics.size);
 | 
			
		||||
		return GroupCallBarContentByCall(call, userpicSize);
 | 
			
		||||
	}) | rpl::flatten_latest();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
rpl::producer<> GroupCallTracker::joinClicks() const {
 | 
			
		||||
	return _joinClicks.events();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace HistoryView
 | 
			
		||||
| 
						 | 
				
			
			@ -37,22 +37,14 @@ void GenerateUserpicsInRow(
 | 
			
		|||
	const style::GroupCallUserpics &st,
 | 
			
		||||
	int maxElements = 0);
 | 
			
		||||
 | 
			
		||||
class GroupCallTracker final {
 | 
			
		||||
public:
 | 
			
		||||
	explicit GroupCallTracker(not_null<PeerData*> peer);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] rpl::producer<Ui::GroupCallBarContent> content() const;
 | 
			
		||||
	[[nodiscard]] rpl::producer<> joinClicks() const;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] static rpl::producer<Ui::GroupCallBarContent> ContentByCall(
 | 
			
		||||
[[nodiscard]] auto GroupCallBarContentByCall(
 | 
			
		||||
	not_null<Data::GroupCall*> call,
 | 
			
		||||
		int userpicSize);
 | 
			
		||||
	int userpicSize)
 | 
			
		||||
-> rpl::producer<Ui::GroupCallBarContent>;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	const not_null<PeerData*> _peer;
 | 
			
		||||
 | 
			
		||||
	rpl::event_stream<> _joinClicks;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
[[nodiscard]] auto GroupCallBarContentByPeer(
 | 
			
		||||
	not_null<PeerData*> peer,
 | 
			
		||||
	int userpicSize)
 | 
			
		||||
-> rpl::producer<Ui::GroupCallBarContent>;
 | 
			
		||||
 | 
			
		||||
} // namespace HistoryView
 | 
			
		||||
| 
						 | 
				
			
			@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		|||
#include "history/history_message.h"
 | 
			
		||||
#include "history/view/media/history_view_media.h"
 | 
			
		||||
#include "history/view/media/history_view_web_page.h"
 | 
			
		||||
#include "history/view/history_view_group_call_tracker.h" // UserpicInRow.
 | 
			
		||||
#include "history/view/history_view_group_call_bar.h" // UserpicInRow.
 | 
			
		||||
#include "history/view/history_view_view_button.h" // ViewButton.
 | 
			
		||||
#include "history/history.h"
 | 
			
		||||
#include "ui/effects/ripple_animation.h"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										214
									
								
								Telegram/SourceFiles/history/view/history_view_requests_bar.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								Telegram/SourceFiles/history/view/history_view_requests_bar.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,214 @@
 | 
			
		|||
/*
 | 
			
		||||
This file is part of Telegram Desktop,
 | 
			
		||||
the official desktop application for the Telegram messaging service.
 | 
			
		||||
 | 
			
		||||
For license and copyright information please follow this link:
 | 
			
		||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		||||
*/
 | 
			
		||||
#include "history/view/history_view_requests_bar.h"
 | 
			
		||||
 | 
			
		||||
#include "history/view/history_view_group_call_bar.h"
 | 
			
		||||
#include "data/data_peer.h"
 | 
			
		||||
#include "data/data_user.h"
 | 
			
		||||
#include "data/data_changes.h"
 | 
			
		||||
#include "data/data_session.h"
 | 
			
		||||
#include "main/main_session.h"
 | 
			
		||||
#include "ui/chat/requests_bar.h"
 | 
			
		||||
#include "ui/chat/group_call_userpics.h"
 | 
			
		||||
#include "info/profile/info_profile_values.h"
 | 
			
		||||
#include "apiwrap.h"
 | 
			
		||||
 | 
			
		||||
namespace HistoryView {
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
// If less than 10 requests we request userpics each time the count changes.
 | 
			
		||||
constexpr auto kRequestAgainThreshold = 10;
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
rpl::producer<Ui::RequestsBarContent> RequestsBarContentByPeer(
 | 
			
		||||
		not_null<PeerData*> peer,
 | 
			
		||||
		int userpicSize) {
 | 
			
		||||
	struct State {
 | 
			
		||||
		explicit State(not_null<PeerData*> peer)
 | 
			
		||||
		: api(&peer->session().api()) {
 | 
			
		||||
			current.isGroup = !peer->isBroadcast();
 | 
			
		||||
		}
 | 
			
		||||
		~State() {
 | 
			
		||||
			if (requestId) {
 | 
			
		||||
				api->request(requestId).cancel();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		not_null<ApiWrap*> api;
 | 
			
		||||
		std::vector<UserpicInRow> userpics;
 | 
			
		||||
		std::vector<not_null<UserData*>> users;
 | 
			
		||||
		Ui::RequestsBarContent current;
 | 
			
		||||
		base::has_weak_ptr guard;
 | 
			
		||||
		mtpRequestId requestId = 0;
 | 
			
		||||
		Fn<void()> requestUsers;
 | 
			
		||||
		bool someUserpicsNotLoaded = false;
 | 
			
		||||
		bool pushScheduled = false;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	static constexpr auto kLimit = 3;
 | 
			
		||||
	static const auto FillUserpics = [](
 | 
			
		||||
			not_null<State*> state) {
 | 
			
		||||
		const auto &users = state->users;
 | 
			
		||||
		const auto same = ranges::equal(
 | 
			
		||||
			state->userpics,
 | 
			
		||||
			users,
 | 
			
		||||
			ranges::equal_to(),
 | 
			
		||||
			&UserpicInRow::peer);
 | 
			
		||||
		if (same) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		for (auto b = begin(users), e = end(users), i = b; i != e; ++i) {
 | 
			
		||||
			const auto user = *i;
 | 
			
		||||
			const auto j = ranges::find(
 | 
			
		||||
				state->userpics,
 | 
			
		||||
				user,
 | 
			
		||||
				&UserpicInRow::peer);
 | 
			
		||||
			const auto place = begin(state->userpics) + (i - b);
 | 
			
		||||
			if (j == end(state->userpics)) {
 | 
			
		||||
				state->userpics.insert(
 | 
			
		||||
					place,
 | 
			
		||||
					UserpicInRow{ .peer = user });
 | 
			
		||||
			} else if (j > place) {
 | 
			
		||||
				ranges::rotate(place, j, j + 1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		while (state->userpics.size() > users.size()) {
 | 
			
		||||
			state->userpics.pop_back();
 | 
			
		||||
		}
 | 
			
		||||
		return true;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	static const auto RegenerateUserpics = [](
 | 
			
		||||
			not_null<State*> state,
 | 
			
		||||
			int userpicSize,
 | 
			
		||||
			bool force = false) {
 | 
			
		||||
		const auto result = FillUserpics(state) || force;
 | 
			
		||||
		if (!result) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		state->current.users.reserve(state->userpics.size());
 | 
			
		||||
		state->current.users.clear();
 | 
			
		||||
		state->someUserpicsNotLoaded = false;
 | 
			
		||||
		for (auto &userpic : state->userpics) {
 | 
			
		||||
			userpic.peer->loadUserpic();
 | 
			
		||||
			const auto pic = userpic.peer->genUserpic(
 | 
			
		||||
				userpic.view,
 | 
			
		||||
				userpicSize);
 | 
			
		||||
			userpic.uniqueKey = userpic.peer->userpicUniqueKey(userpic.view);
 | 
			
		||||
			state->current.users.push_back({
 | 
			
		||||
				.userpic = pic.toImage(),
 | 
			
		||||
				.userpicKey = userpic.uniqueKey,
 | 
			
		||||
				.id = userpic.peer->id.value,
 | 
			
		||||
			});
 | 
			
		||||
			if (userpic.peer->hasUserpic()
 | 
			
		||||
				&& userpic.peer->useEmptyUserpic(userpic.view)) {
 | 
			
		||||
				state->someUserpicsNotLoaded = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return true;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	return [=](auto consumer) {
 | 
			
		||||
		const auto api = &peer->session().api();
 | 
			
		||||
 | 
			
		||||
		auto lifetime = rpl::lifetime();
 | 
			
		||||
		auto state = lifetime.make_state<State>(peer);
 | 
			
		||||
 | 
			
		||||
		const auto pushNext = [=] {
 | 
			
		||||
			if (state->pushScheduled) {
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			state->pushScheduled = true;
 | 
			
		||||
			crl::on_main(&state->guard, [=] {
 | 
			
		||||
				state->pushScheduled = false;
 | 
			
		||||
				consumer.put_next_copy(state->current);
 | 
			
		||||
			});
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		state->requestUsers = [=] {
 | 
			
		||||
			if (state->requestId) {
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			using Flag = MTPmessages_GetChatInviteImporters::Flag;
 | 
			
		||||
			state->requestId = state->api->request(
 | 
			
		||||
				MTPmessages_GetChatInviteImporters(
 | 
			
		||||
					MTP_flags(Flag::f_requested),
 | 
			
		||||
					peer->input,
 | 
			
		||||
					MTPstring(), // link
 | 
			
		||||
					MTPstring(), // q
 | 
			
		||||
					MTP_int(0), // offset_date
 | 
			
		||||
					MTP_inputUserEmpty(), // offset_user
 | 
			
		||||
					MTP_int(kLimit))
 | 
			
		||||
			).done([=](const MTPmessages_ChatInviteImporters &result) {
 | 
			
		||||
				state->requestId = 0;
 | 
			
		||||
 | 
			
		||||
				result.match([&](
 | 
			
		||||
						const MTPDmessages_chatInviteImporters &data) {
 | 
			
		||||
					const auto count = data.vcount().v;
 | 
			
		||||
					const auto changed = (state->current.count != count);
 | 
			
		||||
					const auto &importers = data.vimporters().v;
 | 
			
		||||
					auto &owner = peer->owner();
 | 
			
		||||
					state->users = std::vector<not_null<UserData*>>();
 | 
			
		||||
					state->users.reserve(importers.size());
 | 
			
		||||
					for (const auto &importer : importers) {
 | 
			
		||||
						importer.match([&](
 | 
			
		||||
								const MTPDchatInviteImporter &data) {
 | 
			
		||||
							state->users.push_back(
 | 
			
		||||
								owner.user(data.vuser_id()));
 | 
			
		||||
						});
 | 
			
		||||
					}
 | 
			
		||||
					if (changed) {
 | 
			
		||||
						state->current.count = count;
 | 
			
		||||
					}
 | 
			
		||||
					if (RegenerateUserpics(state, userpicSize) || changed) {
 | 
			
		||||
						pushNext();
 | 
			
		||||
					}
 | 
			
		||||
					if (state->userpics.size() > state->current.count) {
 | 
			
		||||
						state->requestUsers();
 | 
			
		||||
					}
 | 
			
		||||
				});
 | 
			
		||||
			}).fail([=](const MTP::Error &error) {
 | 
			
		||||
				state->requestId = 0;
 | 
			
		||||
			}).send();
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		peer->session().downloaderTaskFinished(
 | 
			
		||||
		) | rpl::filter([=] {
 | 
			
		||||
			return state->someUserpicsNotLoaded;
 | 
			
		||||
		}) | rpl::start_with_next([=] {
 | 
			
		||||
			for (const auto &userpic : state->userpics) {
 | 
			
		||||
				if (userpic.peer->userpicUniqueKey(userpic.view)
 | 
			
		||||
					!= userpic.uniqueKey) {
 | 
			
		||||
					RegenerateUserpics(state, userpicSize, true);
 | 
			
		||||
					pushNext();
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}, lifetime);
 | 
			
		||||
 | 
			
		||||
		Info::Profile::PendingRequestsCountValue(
 | 
			
		||||
			peer
 | 
			
		||||
		) | rpl::filter([=](int count) {
 | 
			
		||||
			return (state->current.count != count);
 | 
			
		||||
		}) | rpl::start_with_next([=](int count) {
 | 
			
		||||
			const auto was = state->current.count;
 | 
			
		||||
			const auto requestUsersNeeded = (was < kRequestAgainThreshold)
 | 
			
		||||
				|| (count < kRequestAgainThreshold);
 | 
			
		||||
			state->current.count = count;
 | 
			
		||||
			if (requestUsersNeeded) {
 | 
			
		||||
				state->requestUsers();
 | 
			
		||||
			}
 | 
			
		||||
			pushNext();
 | 
			
		||||
		}, lifetime);
 | 
			
		||||
 | 
			
		||||
		return lifetime;
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace HistoryView
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
/*
 | 
			
		||||
This file is part of Telegram Desktop,
 | 
			
		||||
the official desktop application for the Telegram messaging service.
 | 
			
		||||
 | 
			
		||||
For license and copyright information please follow this link:
 | 
			
		||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		||||
*/
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "ui/rp_widget.h"
 | 
			
		||||
 | 
			
		||||
namespace Ui {
 | 
			
		||||
struct RequestsBarContent;
 | 
			
		||||
} // namespace Ui
 | 
			
		||||
 | 
			
		||||
namespace HistoryView {
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] rpl::producer<Ui::RequestsBarContent> RequestsBarContentByPeer(
 | 
			
		||||
	not_null<PeerData*> peer,
 | 
			
		||||
	int userpicSize);
 | 
			
		||||
 | 
			
		||||
} // namespace HistoryView
 | 
			
		||||
| 
						 | 
				
			
			@ -909,3 +909,11 @@ whoReadChecksDisabled: icon{{ "chat/seen_checks", menuFgDisabled }};
 | 
			
		|||
whoReadPlayed: icon{{ "chat/seen_played", windowFg }};
 | 
			
		||||
whoReadPlayedOver: icon{{ "chat/seen_played", windowFgOver }};
 | 
			
		||||
whoReadPlayedDisabled: icon {{ "chat/seen_played", menuFgDisabled }};
 | 
			
		||||
 | 
			
		||||
historyRequestsUserpics: GroupCallUserpics {
 | 
			
		||||
	size: 22px;
 | 
			
		||||
	shift: 8px;
 | 
			
		||||
	stroke: 4px;
 | 
			
		||||
	align: align(left);
 | 
			
		||||
}
 | 
			
		||||
historyRequestsHeight: 33px;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										220
									
								
								Telegram/SourceFiles/ui/chat/requests_bar.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								Telegram/SourceFiles/ui/chat/requests_bar.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,220 @@
 | 
			
		|||
/*
 | 
			
		||||
This file is part of Telegram Desktop,
 | 
			
		||||
the official desktop application for the Telegram messaging service.
 | 
			
		||||
 | 
			
		||||
For license and copyright information please follow this link:
 | 
			
		||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		||||
*/
 | 
			
		||||
#include "ui/chat/requests_bar.h"
 | 
			
		||||
 | 
			
		||||
#include "ui/chat/group_call_userpics.h"
 | 
			
		||||
#include "ui/widgets/shadow.h"
 | 
			
		||||
#include "lang/lang_keys.h"
 | 
			
		||||
#include "styles/style_chat.h"
 | 
			
		||||
#include "styles/style_calls.h"
 | 
			
		||||
#include "styles/style_info.h" // st::topBarArrowPadding, like TopBarWidget.
 | 
			
		||||
#include "styles/palette.h"
 | 
			
		||||
 | 
			
		||||
#include <QtGui/QtEvents>
 | 
			
		||||
 | 
			
		||||
namespace Ui {
 | 
			
		||||
 | 
			
		||||
RequestsBar::RequestsBar(
 | 
			
		||||
	not_null<QWidget*> parent,
 | 
			
		||||
	rpl::producer<RequestsBarContent> content)
 | 
			
		||||
: _wrap(parent, object_ptr<RpWidget>(parent))
 | 
			
		||||
, _inner(_wrap.entity())
 | 
			
		||||
, _shadow(std::make_unique<PlainShadow>(_wrap.parentWidget()))
 | 
			
		||||
, _userpics(std::make_unique<GroupCallUserpics>(
 | 
			
		||||
		st::historyRequestsUserpics,
 | 
			
		||||
		rpl::single(false),
 | 
			
		||||
		[=] { _inner->update(); })) {
 | 
			
		||||
	_wrap.hide(anim::type::instant);
 | 
			
		||||
	_shadow->hide();
 | 
			
		||||
 | 
			
		||||
	_wrap.entity()->paintRequest(
 | 
			
		||||
	) | rpl::start_with_next([=](QRect clip) {
 | 
			
		||||
		QPainter(_wrap.entity()).fillRect(clip, st::historyPinnedBg);
 | 
			
		||||
	}, lifetime());
 | 
			
		||||
	_wrap.setAttribute(Qt::WA_OpaquePaintEvent);
 | 
			
		||||
 | 
			
		||||
	auto copy = std::move(
 | 
			
		||||
		content
 | 
			
		||||
	) | rpl::start_spawning(_wrap.lifetime());
 | 
			
		||||
 | 
			
		||||
	rpl::duplicate(
 | 
			
		||||
		copy
 | 
			
		||||
	) | rpl::start_with_next([=](RequestsBarContent &&content) {
 | 
			
		||||
		_content = content;
 | 
			
		||||
		if (_content.count > 0) {
 | 
			
		||||
			_text = (_content.isGroup
 | 
			
		||||
				? tr::lng_group_requests_pending
 | 
			
		||||
				: tr::lng_group_requests_pending_channel)(
 | 
			
		||||
					tr::now,
 | 
			
		||||
					lt_count_decimal,
 | 
			
		||||
					_content.count);
 | 
			
		||||
		}
 | 
			
		||||
		_userpics->update(_content.users, !_wrap.isHidden());
 | 
			
		||||
		_inner->update();
 | 
			
		||||
	}, lifetime());
 | 
			
		||||
 | 
			
		||||
	std::move(
 | 
			
		||||
		copy
 | 
			
		||||
	) | rpl::map([=](const RequestsBarContent &content) {
 | 
			
		||||
		return !content.count;
 | 
			
		||||
	}) | rpl::start_with_next_done([=](bool hidden) {
 | 
			
		||||
		_shouldBeShown = !hidden;
 | 
			
		||||
		if (!_forceHidden) {
 | 
			
		||||
			_wrap.toggle(_shouldBeShown, anim::type::normal);
 | 
			
		||||
		}
 | 
			
		||||
	}, [=] {
 | 
			
		||||
		_forceHidden = true;
 | 
			
		||||
		_wrap.toggle(false, anim::type::normal);
 | 
			
		||||
	}, lifetime());
 | 
			
		||||
 | 
			
		||||
	_userpics->widthValue(
 | 
			
		||||
	) | rpl::start_with_next([=](int width) {
 | 
			
		||||
		_userpicsWidth = width;
 | 
			
		||||
	}, lifetime());
 | 
			
		||||
 | 
			
		||||
	setupInner();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RequestsBar::~RequestsBar() = default;
 | 
			
		||||
 | 
			
		||||
void RequestsBar::setupInner() {
 | 
			
		||||
	_inner->resize(0, st::historyRequestsHeight);
 | 
			
		||||
	_inner->paintRequest(
 | 
			
		||||
	) | rpl::start_with_next([=](QRect rect) {
 | 
			
		||||
		auto p = Painter(_inner);
 | 
			
		||||
		paint(p);
 | 
			
		||||
	}, _inner->lifetime());
 | 
			
		||||
 | 
			
		||||
	// Clicks.
 | 
			
		||||
	_inner->setCursor(style::cur_pointer);
 | 
			
		||||
	_inner->events(
 | 
			
		||||
	) | rpl::filter([=](not_null<QEvent*> event) {
 | 
			
		||||
		return (event->type() == QEvent::MouseButtonPress);
 | 
			
		||||
	}) | rpl::map([=] {
 | 
			
		||||
		return _inner->events(
 | 
			
		||||
		) | rpl::filter([=](not_null<QEvent*> event) {
 | 
			
		||||
			return (event->type() == QEvent::MouseButtonRelease);
 | 
			
		||||
		}) | rpl::take(1) | rpl::filter([=](not_null<QEvent*> event) {
 | 
			
		||||
			return _inner->rect().contains(
 | 
			
		||||
				static_cast<QMouseEvent*>(event.get())->pos());
 | 
			
		||||
		});
 | 
			
		||||
	}) | rpl::flatten_latest(
 | 
			
		||||
	) | rpl::map([] {
 | 
			
		||||
		return rpl::empty_value();
 | 
			
		||||
	}) | rpl::start_to_stream(_barClicks, _inner->lifetime());
 | 
			
		||||
 | 
			
		||||
	_wrap.geometryValue(
 | 
			
		||||
	) | rpl::start_with_next([=](QRect rect) {
 | 
			
		||||
		updateShadowGeometry(rect);
 | 
			
		||||
		updateControlsGeometry(rect);
 | 
			
		||||
	}, _inner->lifetime());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RequestsBar::paint(Painter &p) {
 | 
			
		||||
	p.fillRect(_inner->rect(), st::historyComposeAreaBg);
 | 
			
		||||
 | 
			
		||||
	const auto userpicsSize = st::historyRequestsUserpics.size;
 | 
			
		||||
	const auto userpicsTop = st::lineWidth + (st::historyRequestsHeight
 | 
			
		||||
		- st::lineWidth
 | 
			
		||||
		- userpicsSize) / 2;
 | 
			
		||||
	const auto userpicsLeft = userpicsTop * 2;
 | 
			
		||||
	const auto textTop = st::lineWidth + (st::historyRequestsHeight
 | 
			
		||||
		- st::lineWidth
 | 
			
		||||
		- st::msgServiceNameFont->height) / 2;
 | 
			
		||||
	const auto width = _inner->width();
 | 
			
		||||
	const auto &font = st::defaultMessageBar.title.font;
 | 
			
		||||
	p.setPen(st::defaultMessageBar.titleFg);
 | 
			
		||||
	p.setFont(font);
 | 
			
		||||
 | 
			
		||||
	const auto textLeft = userpicsLeft + _userpicsWidth + userpicsLeft;
 | 
			
		||||
	const auto available = width - textLeft - userpicsLeft;
 | 
			
		||||
	p.drawTextLeft(textLeft, textTop, width, _text);
 | 
			
		||||
 | 
			
		||||
	// Skip shadow of the bar above.
 | 
			
		||||
	_userpics->paint(p, userpicsLeft, userpicsTop, userpicsSize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RequestsBar::updateControlsGeometry(QRect wrapGeometry) {
 | 
			
		||||
	const auto hidden = _wrap.isHidden() || !wrapGeometry.height();
 | 
			
		||||
	if (_shadow->isHidden() != hidden) {
 | 
			
		||||
		_shadow->setVisible(!hidden);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RequestsBar::setShadowGeometryPostprocess(Fn<QRect(QRect)> postprocess) {
 | 
			
		||||
	_shadowGeometryPostprocess = std::move(postprocess);
 | 
			
		||||
	updateShadowGeometry(_wrap.geometry());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RequestsBar::updateShadowGeometry(QRect wrapGeometry) {
 | 
			
		||||
	const auto regular = QRect(
 | 
			
		||||
		wrapGeometry.x(),
 | 
			
		||||
		wrapGeometry.y() + wrapGeometry.height(),
 | 
			
		||||
		wrapGeometry.width(),
 | 
			
		||||
		st::lineWidth);
 | 
			
		||||
	_shadow->setGeometry(_shadowGeometryPostprocess
 | 
			
		||||
		? _shadowGeometryPostprocess(regular)
 | 
			
		||||
		: regular);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RequestsBar::show() {
 | 
			
		||||
	if (!_forceHidden) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	_forceHidden = false;
 | 
			
		||||
	if (_shouldBeShown) {
 | 
			
		||||
		_wrap.show(anim::type::instant);
 | 
			
		||||
		_shadow->show();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RequestsBar::hide() {
 | 
			
		||||
	if (_forceHidden) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	_forceHidden = true;
 | 
			
		||||
	_wrap.hide(anim::type::instant);
 | 
			
		||||
	_shadow->hide();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RequestsBar::raise() {
 | 
			
		||||
	_wrap.raise();
 | 
			
		||||
	_shadow->raise();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RequestsBar::finishAnimating() {
 | 
			
		||||
	_wrap.finishAnimating();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RequestsBar::move(int x, int y) {
 | 
			
		||||
	_wrap.move(x, y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RequestsBar::resizeToWidth(int width) {
 | 
			
		||||
	_wrap.entity()->resizeToWidth(width);
 | 
			
		||||
	_inner->resizeToWidth(width);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int RequestsBar::height() const {
 | 
			
		||||
	return !_forceHidden
 | 
			
		||||
		? _wrap.height()
 | 
			
		||||
		: _shouldBeShown
 | 
			
		||||
		? st::historyRequestsHeight
 | 
			
		||||
		: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
rpl::producer<int> RequestsBar::heightValue() const {
 | 
			
		||||
	return _wrap.heightValue();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
rpl::producer<> RequestsBar::barClicks() const {
 | 
			
		||||
	return _barClicks.events();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
} // namespace Ui
 | 
			
		||||
							
								
								
									
										77
									
								
								Telegram/SourceFiles/ui/chat/requests_bar.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								Telegram/SourceFiles/ui/chat/requests_bar.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,77 @@
 | 
			
		|||
/*
 | 
			
		||||
This file is part of Telegram Desktop,
 | 
			
		||||
the official desktop application for the Telegram messaging service.
 | 
			
		||||
 | 
			
		||||
For license and copyright information please follow this link:
 | 
			
		||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		||||
*/
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "ui/wrap/slide_wrap.h"
 | 
			
		||||
#include "ui/effects/animations.h"
 | 
			
		||||
#include "base/object_ptr.h"
 | 
			
		||||
#include "base/timer.h"
 | 
			
		||||
 | 
			
		||||
class Painter;
 | 
			
		||||
 | 
			
		||||
namespace Ui {
 | 
			
		||||
 | 
			
		||||
class PlainShadow;
 | 
			
		||||
struct GroupCallUser;
 | 
			
		||||
class GroupCallUserpics;
 | 
			
		||||
 | 
			
		||||
struct RequestsBarContent {
 | 
			
		||||
	std::vector<GroupCallUser> users;
 | 
			
		||||
	int count = 0;
 | 
			
		||||
	bool isGroup = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class RequestsBar final {
 | 
			
		||||
public:
 | 
			
		||||
	RequestsBar(
 | 
			
		||||
		not_null<QWidget*> parent,
 | 
			
		||||
		rpl::producer<RequestsBarContent> content);
 | 
			
		||||
	~RequestsBar();
 | 
			
		||||
 | 
			
		||||
	void show();
 | 
			
		||||
	void hide();
 | 
			
		||||
	void raise();
 | 
			
		||||
	void finishAnimating();
 | 
			
		||||
 | 
			
		||||
	void setShadowGeometryPostprocess(Fn<QRect(QRect)> postprocess);
 | 
			
		||||
 | 
			
		||||
	void move(int x, int y);
 | 
			
		||||
	void resizeToWidth(int width);
 | 
			
		||||
	[[nodiscard]] int height() const;
 | 
			
		||||
	[[nodiscard]] rpl::producer<int> heightValue() const;
 | 
			
		||||
	[[nodiscard]] rpl::producer<> barClicks() const;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] rpl::lifetime &lifetime() {
 | 
			
		||||
		return _wrap.lifetime();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	using User = GroupCallUser;
 | 
			
		||||
 | 
			
		||||
	void updateShadowGeometry(QRect wrapGeometry);
 | 
			
		||||
	void updateControlsGeometry(QRect wrapGeometry);
 | 
			
		||||
	void updateUserpics();
 | 
			
		||||
	void setupInner();
 | 
			
		||||
	void paint(Painter &p);
 | 
			
		||||
 | 
			
		||||
	SlideWrap<> _wrap;
 | 
			
		||||
	not_null<RpWidget*> _inner;
 | 
			
		||||
	std::unique_ptr<PlainShadow> _shadow;
 | 
			
		||||
	rpl::event_stream<> _barClicks;
 | 
			
		||||
	Fn<QRect(QRect)> _shadowGeometryPostprocess;
 | 
			
		||||
	bool _shouldBeShown = false;
 | 
			
		||||
	bool _forceHidden = false;
 | 
			
		||||
 | 
			
		||||
	RequestsBarContent _content;
 | 
			
		||||
	std::unique_ptr<GroupCallUserpics> _userpics;
 | 
			
		||||
	int _userpicsWidth = 0;
 | 
			
		||||
	QString _text;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Ui
 | 
			
		||||
| 
						 | 
				
			
			@ -162,6 +162,8 @@ PRIVATE
 | 
			
		|||
    ui/chat/message_bubble.h
 | 
			
		||||
    ui/chat/pinned_bar.cpp
 | 
			
		||||
    ui/chat/pinned_bar.h
 | 
			
		||||
    ui/chat/requests_bar.cpp
 | 
			
		||||
    ui/chat/requests_bar.h
 | 
			
		||||
    ui/controls/call_mute_button.cpp
 | 
			
		||||
    ui/controls/call_mute_button.h
 | 
			
		||||
    ui/controls/delete_message_context_action.cpp
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue