From d3a01b6235160285319ebf83fa323d975ae801d8 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Wed, 29 May 2024 03:50:40 +0300
Subject: [PATCH] Improved style of list of credits history entries for entry
 photo.

---
 Telegram/CMakeLists.txt                       |  2 +
 .../info_statistics_list_controllers.cpp      | 20 ++++++-
 .../settings/settings_credits_graphics.cpp    | 36 ++----------
 .../ui/effects/credits_graphics.cpp           | 55 +++++++++++++++++++
 .../SourceFiles/ui/effects/credits_graphics.h |  6 ++
 Telegram/cmake/td_ui.cmake                    |  2 -
 6 files changed, 85 insertions(+), 36 deletions(-)

diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index d1171a5ef..6e786a6cd 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -1467,6 +1467,8 @@ PRIVATE
     ui/controls/silent_toggle.h
     ui/controls/userpic_button.cpp
     ui/controls/userpic_button.h
+    ui/effects/credits_graphics.cpp
+    ui/effects/credits_graphics.h
     ui/effects/emoji_fly_animation.cpp
     ui/effects/emoji_fly_animation.h
     ui/effects/message_sending_animation_common.h
diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp
index 0028e0fab..51dcc203b 100644
--- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp
+++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp
@@ -713,6 +713,7 @@ public:
 		Data::CreditsHistoryEntry entry;
 		not_null<QImage*> creditIcon;
 		int rowHeight = 0;
+		Fn<void(not_null<PeerListRow*>)> updateCallback;
 	};
 
 	CreditsRow(not_null<PeerData*> peer, const Descriptor &descriptor);
@@ -752,6 +753,14 @@ CreditsRow::CreditsRow(not_null<PeerData*> peer, const Descriptor &descriptor)
 , _entry(descriptor.entry)
 , _creditIcon(descriptor.creditIcon)
 , _rowHeight(descriptor.rowHeight) {
+	const auto photo = _entry.photoId
+		? peer->session().data().photo(_entry.photoId).get()
+		: nullptr;
+	if (photo) {
+		_paintUserpicCallback = Ui::GenerateCreditsPaintEntryCallback(
+			photo,
+			[this, update = descriptor.updateCallback] { update(this); });
+	}
 	init();
 }
 
@@ -772,9 +781,11 @@ void CreditsRow::init() {
 			(!_entry.bareId ? QChar('+') : kMinus)
 				+ Lang::FormatCountDecimal(std::abs(int64(_entry.credits))));
 	}
-	_paintUserpicCallback = !PeerListRow::special()
-		? PeerListRow::generatePaintUserpicCallback(false)
-		: Ui::GenerateCreditsPaintUserpicCallback(_entry);
+	if (!_paintUserpicCallback) {
+		_paintUserpicCallback = !PeerListRow::special()
+			? PeerListRow::generatePaintUserpicCallback(false)
+			: Ui::GenerateCreditsPaintUserpicCallback(_entry);
+	}
 }
 
 const Data::CreditsHistoryEntry &CreditsRow::entry() const {
@@ -912,6 +923,9 @@ void CreditsController::applySlice(const Data::CreditsStatusSlice &slice) {
 				.entry = item,
 				.creditIcon = _creditIcon,
 				.rowHeight = computeListSt().item.height,
+				.updateCallback = [=](not_null<PeerListRow*> row) {
+					delegate()->peerListUpdateRow(row);
+				},
 			};
 			using Type = Data::CreditsHistoryEntry::PeerType;
 			if (item.bareId) {
diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp
index 16c15e44e..b855121a4 100644
--- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp
+++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp
@@ -568,44 +568,18 @@ object_ptr<Ui::RpWidget> HistoryEntryPhoto(
 		not_null<Ui::RpWidget*> parent,
 		not_null<PhotoData*> photo,
 		int photoSize) {
-	struct State {
-		std::shared_ptr<Data::PhotoMedia> view;
-		Image *image = nullptr;
-		rpl::lifetime downloadLifetime;
-	};
-	const auto state = parent->lifetime().make_state<State>();
 	auto owned = object_ptr<Ui::RpWidget>(parent);
 	const auto widget = owned.data();
-	state->view = photo->createMediaView();
-	photo->load(Data::PhotoSize::Thumbnail, {});
-
 	widget->resize(Size(photoSize));
 
-	rpl::single(rpl::empty_value()) | rpl::then(
-		photo->owner().session().downloaderTaskFinished()
-	) | rpl::start_with_next([=] {
-		using Size = Data::PhotoSize;
-		if (const auto large = state->view->image(Size::Large)) {
-			state->image = large;
-		} else if (const auto small = state->view->image(Size::Small)) {
-			state->image = small;
-		} else if (const auto t = state->view->image(Size::Thumbnail)) {
-			state->image = t;
-		}
-		widget->update();
-		if (state->view->loaded()) {
-			state->downloadLifetime.destroy();
-		}
-	}, state->downloadLifetime);
+	const auto draw = Ui::GenerateCreditsPaintEntryCallback(
+		photo,
+		[=] { widget->update(); });
 
 	widget->paintRequest(
 	) | rpl::start_with_next([=] {
-		auto p = QPainter(widget);
-		if (state->image) {
-			p.drawPixmap(0, 0, state->image->pix(widget->width(), {
-				.options = Images::Option::RoundCircle,
-			}));
-		}
+		auto p = Painter(widget);
+		draw(p, 0, 0, photoSize, photoSize);
 	}, widget->lifetime());
 
 	return owned;
diff --git a/Telegram/SourceFiles/ui/effects/credits_graphics.cpp b/Telegram/SourceFiles/ui/effects/credits_graphics.cpp
index 44e82195a..3c7a714f7 100644
--- a/Telegram/SourceFiles/ui/effects/credits_graphics.cpp
+++ b/Telegram/SourceFiles/ui/effects/credits_graphics.cpp
@@ -10,7 +10,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include <QtCore/QDateTime>
 
 #include "data/data_credits.h"
+#include "data/data_file_origin.h"
+#include "data/data_photo.h"
+#include "data/data_photo_media.h"
+#include "data/data_session.h"
 #include "lang/lang_keys.h"
+#include "main/main_session.h"
 #include "ui/empty_userpic.h"
 #include "ui/painter.h"
 #include "styles/style_credits.h"
@@ -67,6 +72,56 @@ PaintRoundImageCallback GenerateCreditsPaintUserpicCallback(
 	};
 }
 
+Fn<void(Painter &, int, int, int, int)> GenerateCreditsPaintEntryCallback(
+		not_null<PhotoData*> photo,
+		Fn<void()> update) {
+	struct State {
+		std::shared_ptr<Data::PhotoMedia> view;
+		Image *imagePtr = nullptr;
+		QImage image;
+		rpl::lifetime downloadLifetime;
+		bool entryImageLoaded = false;
+	};
+	const auto state = std::make_shared<State>();
+	state->view = photo->createMediaView();
+	photo->load(Data::PhotoSize::Thumbnail, {});
+
+	rpl::single(rpl::empty_value()) | rpl::then(
+		photo->owner().session().downloaderTaskFinished()
+	) | rpl::start_with_next([=] {
+		using Size = Data::PhotoSize;
+		if (const auto large = state->view->image(Size::Large)) {
+			state->imagePtr = large;
+		} else if (const auto small = state->view->image(Size::Small)) {
+			state->imagePtr = small;
+		} else if (const auto t = state->view->image(Size::Thumbnail)) {
+			state->imagePtr = t;
+		}
+		update();
+		if (state->view->loaded()) {
+			state->entryImageLoaded = true;
+			state->downloadLifetime.destroy();
+		}
+	}, state->downloadLifetime);
+
+	return [=](Painter &p, int x, int y, int outerWidth, int size) {
+		if (state->imagePtr
+			&& (!state->entryImageLoaded || state->image.isNull())) {
+			const auto image = state->imagePtr->original();
+			const auto minSize = std::min(image.width(), image.height());
+			state->image = Images::Prepare(
+				image.copy(
+					(image.width() - minSize) / 2,
+					(image.height() - minSize) / 2,
+					minSize,
+					minSize),
+				size * style::DevicePixelRatio(),
+				{ .options = Images::Option::RoundCircle });
+		}
+		p.drawImage(x, y, state->image);
+	};
+}
+
 TextWithEntities GenerateEntryName(const Data::CreditsHistoryEntry &entry) {
 	return ((entry.peerType == Data::CreditsHistoryEntry::PeerType::Fragment)
 		? tr::lng_bot_username_description1_link
diff --git a/Telegram/SourceFiles/ui/effects/credits_graphics.h b/Telegram/SourceFiles/ui/effects/credits_graphics.h
index 44d848748..ddfbea07c 100644
--- a/Telegram/SourceFiles/ui/effects/credits_graphics.h
+++ b/Telegram/SourceFiles/ui/effects/credits_graphics.h
@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #pragma once
 
+class PhotoData;
+
 namespace Data {
 struct CreditsHistoryEntry;
 } // namespace Data
@@ -16,6 +18,10 @@ namespace Ui {
 Fn<void(Painter &, int, int, int, int)> GenerateCreditsPaintUserpicCallback(
 	const Data::CreditsHistoryEntry &entry);
 
+Fn<void(Painter &, int, int, int, int)> GenerateCreditsPaintEntryCallback(
+	not_null<PhotoData*> photo,
+	Fn<void()> update);
+
 [[nodiscard]] TextWithEntities GenerateEntryName(
 	const Data::CreditsHistoryEntry &entry);
 
diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake
index 3c0c026a3..29a2c10b0 100644
--- a/Telegram/cmake/td_ui.cmake
+++ b/Telegram/cmake/td_ui.cmake
@@ -358,8 +358,6 @@ PRIVATE
     ui/effects/fireworks_animation.h
     ui/effects/glare.cpp
     ui/effects/glare.h
-    ui/effects/credits_graphics.cpp
-    ui/effects/credits_graphics.h
     ui/effects/loading_element.cpp
     ui/effects/loading_element.h
     ui/effects/outline_segments.cpp