mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-11 11:47:09 +02:00
Show common groups userpics in new-peer-info.
This commit is contained in:
parent
c9fb97cd7c
commit
ab58e7a225
9 changed files with 304 additions and 13 deletions
|
@ -69,6 +69,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "main/main_domain.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "info/info_controller.h"
|
||||
#include "info/info_memento.h"
|
||||
#include "inline_bots/bot_attach_web_view.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
|
@ -1034,6 +1036,26 @@ bool EditPaidMessagesFee(
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ShowCommonGroups(
|
||||
Window::SessionController *controller,
|
||||
const Match &match,
|
||||
const QVariant &context) {
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
const auto peerId = PeerId(match->captured(1).toULongLong());
|
||||
if (const auto id = peerToUser(peerId)) {
|
||||
const auto user = controller->session().data().userLoaded(id);
|
||||
if (user) {
|
||||
controller->showSection(
|
||||
std::make_shared<Info::Memento>(
|
||||
user,
|
||||
Info::Section::Type::CommonGroups));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShowStarsExamples(
|
||||
Window::SessionController *controller,
|
||||
const Match &match,
|
||||
|
@ -1541,6 +1563,10 @@ const std::vector<LocalUrlHandler> &InternalUrlHandlers() {
|
|||
u"^edit_paid_messages_fee/([0-9]+)$"_q,
|
||||
EditPaidMessagesFee,
|
||||
},
|
||||
{
|
||||
u"^common_groups/([0-9]+)$"_q,
|
||||
ShowCommonGroups,
|
||||
},
|
||||
{
|
||||
u"^stars_examples$"_q,
|
||||
ShowStarsExamples,
|
||||
|
|
|
@ -4342,6 +4342,9 @@ void HistoryInner::refreshAboutView(bool force) {
|
|||
_aboutView = std::make_unique<HistoryView::AboutView>(
|
||||
_history,
|
||||
_history->delegateMixin()->delegate());
|
||||
_aboutView->refreshRequests() | rpl::start_with_next([=] {
|
||||
updateBotInfo();
|
||||
}, _aboutView->lifetime());
|
||||
}
|
||||
};
|
||||
if (const auto user = _peer->asUser()) {
|
||||
|
|
|
@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_document.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "history/view/history_view_group_call_bar.h"
|
||||
#include "history/view/media/history_view_media_generic.h"
|
||||
#include "history/view/media/history_view_service_box.h"
|
||||
#include "history/view/media/history_view_sticker_player_abstract.h"
|
||||
|
@ -37,17 +38,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "settings/settings_credits.h" // BuyStarsHandler
|
||||
#include "settings/settings_premium.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/text/custom_emoji_instance.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/dynamic_image.h"
|
||||
#include "ui/painter.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_chat_helpers.h" // GroupCallUserpics
|
||||
#include "styles/style_credits.h"
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
constexpr auto kLabelOpacity = 0.85;
|
||||
constexpr auto kMaxCommonChatsUserpics = 3;
|
||||
|
||||
class EmptyChatLockedBox final
|
||||
: public ServiceBoxContent
|
||||
|
@ -95,6 +100,101 @@ private:
|
|||
|
||||
};
|
||||
|
||||
class UserpicsList final : public Ui::DynamicImage {
|
||||
public:
|
||||
UserpicsList(
|
||||
std::vector<not_null<PeerData*>> peers,
|
||||
const style::GroupCallUserpics &st,
|
||||
int countOverride = 0);
|
||||
|
||||
[[nodiscard]] int width() const;
|
||||
|
||||
std::shared_ptr<Ui::DynamicImage> clone() override;
|
||||
|
||||
QImage image(int size) override;
|
||||
void subscribeToUpdates(Fn<void()> callback) override;
|
||||
|
||||
private:
|
||||
struct Subscribed {
|
||||
explicit Subscribed(Fn<void()> callback)
|
||||
: callback(std::move(callback)) {
|
||||
}
|
||||
|
||||
std::vector<HistoryView::UserpicInRow> list;
|
||||
bool someNotLoaded = false;
|
||||
Fn<void()> callback;
|
||||
int paletteVersion = 0;
|
||||
};
|
||||
|
||||
const std::vector<not_null<PeerData*>> _peers;
|
||||
const style::GroupCallUserpics &_st;
|
||||
const int _countOverride = 0;
|
||||
|
||||
QImage _frame;
|
||||
std::unique_ptr<Subscribed> _subscribed;
|
||||
|
||||
};
|
||||
|
||||
UserpicsList::UserpicsList(
|
||||
std::vector<not_null<PeerData*>> peers,
|
||||
const style::GroupCallUserpics &st,
|
||||
int countOverride)
|
||||
: _peers(std::move(peers))
|
||||
, _st(st)
|
||||
, _countOverride(countOverride) {
|
||||
}
|
||||
|
||||
std::shared_ptr<Ui::DynamicImage> UserpicsList::clone() {
|
||||
return std::make_shared<UserpicsList>(_peers, _st);
|
||||
}
|
||||
|
||||
QImage UserpicsList::image(int size) {
|
||||
Expects(_subscribed != nullptr);
|
||||
|
||||
const auto regenerate = [&] {
|
||||
const auto version = style::PaletteVersion();
|
||||
if (_frame.isNull() || _subscribed->paletteVersion != version) {
|
||||
_subscribed->paletteVersion = version;
|
||||
return true;
|
||||
}
|
||||
for (auto &entry : _subscribed->list) {
|
||||
const auto peer = entry.peer;
|
||||
auto &view = entry.view;
|
||||
const auto wasView = view.cloud.get();
|
||||
if (peer->userpicUniqueKey(view) != entry.uniqueKey
|
||||
|| view.cloud.get() != wasView) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
if (regenerate) {
|
||||
const auto max = std::max(_countOverride, int(_peers.size()));
|
||||
GenerateUserpicsInRow(_frame, _subscribed->list, _st, max);
|
||||
}
|
||||
return _frame;
|
||||
}
|
||||
|
||||
void UserpicsList::subscribeToUpdates(Fn<void()> callback) {
|
||||
if (!callback) {
|
||||
_subscribed = nullptr;
|
||||
return;
|
||||
}
|
||||
_subscribed = std::make_unique<Subscribed>(std::move(callback));
|
||||
for (const auto peer : _peers) {
|
||||
_subscribed->list.push_back({ .peer = peer });
|
||||
}
|
||||
}
|
||||
|
||||
int UserpicsList::width() const {
|
||||
const auto count = std::max(_countOverride, int(_peers.size()));
|
||||
if (!count) {
|
||||
return 0;
|
||||
}
|
||||
const auto shifted = count - 1;
|
||||
return _st.size + (shifted * (_st.size - _st.shift));
|
||||
}
|
||||
|
||||
auto GenerateChatIntro(
|
||||
not_null<Element*> parent,
|
||||
Element *replacing,
|
||||
|
@ -165,7 +265,8 @@ auto GenerateChatIntro(
|
|||
auto GenerateNewPeerInfo(
|
||||
not_null<Element*> parent,
|
||||
Element *replacing,
|
||||
not_null<UserData*> user)
|
||||
not_null<UserData*> user,
|
||||
std::vector<not_null<PeerData*>> commonGroups)
|
||||
-> Fn<void(
|
||||
not_null<MediaGeneric*>,
|
||||
Fn<void(std::unique_ptr<MediaGenericPart>)>)> {
|
||||
|
@ -208,9 +309,22 @@ auto GenerateNewPeerInfo(
|
|||
});
|
||||
}
|
||||
|
||||
const auto context = Core::TextContext({
|
||||
.session = &parent->history()->session(),
|
||||
.repaint = [parent] { parent->repaint(); },
|
||||
});
|
||||
const auto kUserpicsPrefix = u"userpics-list/"_q;
|
||||
if (const auto count = user->commonChatsCount()) {
|
||||
const auto url = u"internal:common_groups/"_q
|
||||
+ QString::number(user->id.value);
|
||||
auto ids = QStringList();
|
||||
const auto userpics = std::min(count, kMaxCommonChatsUserpics);
|
||||
for (auto i = 0; i != userpics; ++i) {
|
||||
ids.push_back(QString::number(i < commonGroups.size()
|
||||
? commonGroups[i]->id.value
|
||||
: 0));
|
||||
}
|
||||
auto userpicsData = kUserpicsPrefix + ids.join(',');
|
||||
entries.push_back({
|
||||
tr::lng_new_contact_common_groups(tr::now),
|
||||
Ui::Text::Wrapped(
|
||||
|
@ -219,7 +333,7 @@ auto GenerateNewPeerInfo(
|
|||
lt_count,
|
||||
count,
|
||||
lt_emoji,
|
||||
TextWithEntities(),
|
||||
Ui::Text::SingleCustomEmoji(userpicsData),
|
||||
lt_arrow,
|
||||
Ui::Text::IconEmoji(&st::textMoreIconEmoji),
|
||||
Ui::Text::Bold),
|
||||
|
@ -228,16 +342,40 @@ auto GenerateNewPeerInfo(
|
|||
});
|
||||
}
|
||||
|
||||
auto copy = context;
|
||||
copy.customEmojiFactory = [=, old = copy.customEmojiFactory](
|
||||
QStringView data,
|
||||
const Ui::Text::MarkedContext &context
|
||||
) -> std::unique_ptr<Ui::Text::CustomEmoji> {
|
||||
if (!data.startsWith(kUserpicsPrefix)) {
|
||||
return old(data, context);
|
||||
}
|
||||
const auto ids = data.mid(kUserpicsPrefix.size()).split(',');
|
||||
auto peers = std::vector<not_null<PeerData*>>();
|
||||
for (const auto &id : ids) {
|
||||
if (const auto peerId = PeerId(id.toULongLong())) {
|
||||
peers.push_back(user->owner().peer(peerId));
|
||||
}
|
||||
}
|
||||
auto image = std::make_shared<UserpicsList>(
|
||||
std::move(peers),
|
||||
st::newPeerUserpics,
|
||||
ids.size());
|
||||
const auto size = image->width();
|
||||
return std::make_unique<Ui::CustomEmoji::DynamicImageEmoji>(
|
||||
data.toString(),
|
||||
std::move(image),
|
||||
context.repaint,
|
||||
st::newPeerUserpicsPadding,
|
||||
size);
|
||||
};
|
||||
push(std::make_unique<AttributeTable>(
|
||||
std::move(entries),
|
||||
st::newPeerSubtitleMargin,
|
||||
fadedFg,
|
||||
normalFg));
|
||||
normalFg,
|
||||
copy));
|
||||
|
||||
const auto context = Core::TextContext({
|
||||
.session = &parent->history()->session(),
|
||||
.repaint = [parent] { parent->repaint(); },
|
||||
});
|
||||
const auto details = user->botVerifyDetails();
|
||||
const auto text = details
|
||||
? Data::SingleCustomEmoji(
|
||||
|
@ -380,9 +518,10 @@ bool AboutView::refresh() {
|
|||
if (user
|
||||
&& !user->isContact()
|
||||
&& !user->phoneCountryCode().isEmpty()) {
|
||||
if (_item) {
|
||||
if (_item && !_commonGroupsStale) {
|
||||
return false;
|
||||
}
|
||||
loadCommonGroups();
|
||||
setItem(makeNewPeerInfo(user), nullptr);
|
||||
return true;
|
||||
} else if (user && !user->isSelf() && _history->isDisplayedEmpty()) {
|
||||
|
@ -474,6 +613,14 @@ void AboutView::make(Data::ChatIntro data, bool preview) {
|
|||
setItem(std::move(owned), data.sticker);
|
||||
}
|
||||
|
||||
rpl::producer<> AboutView::refreshRequests() const {
|
||||
return _refreshRequests.events();
|
||||
}
|
||||
|
||||
rpl::lifetime &AboutView::lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
|
||||
void AboutView::toggleStickerRegistered(bool registered) {
|
||||
if (const auto item = _item ? _item->data().get() : nullptr) {
|
||||
if (_sticker) {
|
||||
|
@ -490,6 +637,75 @@ void AboutView::toggleStickerRegistered(bool registered) {
|
|||
}
|
||||
}
|
||||
|
||||
void AboutView::loadCommonGroups() {
|
||||
if (_commonGroupsRequested) {
|
||||
return;
|
||||
}
|
||||
_commonGroupsRequested = true;
|
||||
|
||||
const auto user = _history->peer->asUser();
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct Cached {
|
||||
std::vector<not_null<PeerData*>> list;
|
||||
};
|
||||
struct Session {
|
||||
base::flat_map<not_null<UserData*>, Cached> data;
|
||||
};
|
||||
static auto Map = base::flat_map<not_null<Main::Session*>, Session>();
|
||||
const auto session = &_history->session();
|
||||
auto i = Map.find(session);
|
||||
if (i == end(Map)) {
|
||||
i = Map.emplace(session).first;
|
||||
session->lifetime().add([session] {
|
||||
Map.remove(session);
|
||||
});
|
||||
}
|
||||
auto &cached = i->second.data[user];
|
||||
|
||||
const auto count = user->commonChatsCount();
|
||||
if (!count) {
|
||||
cached = {};
|
||||
return;
|
||||
} else while (cached.list.size() > count) {
|
||||
cached.list.pop_back();
|
||||
}
|
||||
_commonGroups = cached.list;
|
||||
const auto requestId = _history->session().api().request(
|
||||
MTPmessages_GetCommonChats(
|
||||
user->inputUser,
|
||||
MTP_long(0),
|
||||
MTP_int(kMaxCommonChatsUserpics))
|
||||
).done([=](const MTPmessages_Chats &result) {
|
||||
const auto chats = result.match([](const auto &data) {
|
||||
return &data.vchats().v;
|
||||
});
|
||||
auto &owner = user->session().data();
|
||||
auto list = std::vector<not_null<PeerData*>>();
|
||||
list.reserve(chats->size());
|
||||
for (const auto &chat : *chats) {
|
||||
if (const auto peer = owner.processChat(chat)) {
|
||||
list.push_back(peer);
|
||||
if (list.size() == kMaxCommonChatsUserpics) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_commonGroups != list) {
|
||||
Map[session].data[user].list = list;
|
||||
_commonGroups = std::move(list);
|
||||
_commonGroupsStale = true;
|
||||
_refreshRequests.fire({});
|
||||
}
|
||||
}).send();
|
||||
|
||||
_lifetime.add([=] {
|
||||
_history->session().api().request(requestId).cancel();
|
||||
});
|
||||
}
|
||||
|
||||
void AboutView::setHelloChosen(not_null<DocumentData*> sticker) {
|
||||
_helloChosen = sticker;
|
||||
toggleStickerRegistered(false);
|
||||
|
@ -505,6 +721,8 @@ void AboutView::setItem(AdminLog::OwnedItem item, DocumentData *sticker) {
|
|||
}
|
||||
|
||||
AdminLog::OwnedItem AboutView::makeNewPeerInfo(not_null<UserData*> user) {
|
||||
_commonGroupsStale = false;
|
||||
|
||||
const auto text = user->name();
|
||||
const auto item = _history->makeMessage({
|
||||
.id = _history->nextNonHistoryEntryId(),
|
||||
|
@ -517,7 +735,7 @@ AdminLog::OwnedItem AboutView::makeNewPeerInfo(not_null<UserData*> user) {
|
|||
auto owned = AdminLog::OwnedItem(_delegate, item);
|
||||
owned->overrideMedia(std::make_unique<HistoryView::MediaGeneric>(
|
||||
owned.get(),
|
||||
GenerateNewPeerInfo(owned.get(), _item.get(), user),
|
||||
GenerateNewPeerInfo(owned.get(), _item.get(), user, _commonGroups),
|
||||
HistoryView::MediaGenericDescriptor{
|
||||
.service = true,
|
||||
.hideServiceText = true,
|
||||
|
|
|
@ -30,6 +30,9 @@ public:
|
|||
|
||||
void make(Data::ChatIntro data, bool preview = false);
|
||||
|
||||
[[nodiscard]] rpl::producer<> refreshRequests() const;
|
||||
[[nodiscard]] rpl::lifetime &lifetime();
|
||||
|
||||
int top = 0;
|
||||
int height = 0;
|
||||
|
||||
|
@ -50,13 +53,22 @@ private:
|
|||
void setHelloChosen(not_null<DocumentData*> sticker);
|
||||
void toggleStickerRegistered(bool registered);
|
||||
|
||||
void loadCommonGroups();
|
||||
|
||||
const not_null<History*> _history;
|
||||
const not_null<ElementDelegate*> _delegate;
|
||||
AdminLog::OwnedItem _item;
|
||||
|
||||
DocumentData *_helloChosen = nullptr;
|
||||
DocumentData *_sticker = nullptr;
|
||||
int _version = 0;
|
||||
|
||||
bool _commonGroupsStale = false;
|
||||
bool _commonGroupsRequested = false;
|
||||
std::vector<not_null<PeerData*>> _commonGroups;
|
||||
rpl::event_stream<> _refreshRequests;
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -511,6 +511,26 @@ void AttributeTable::draw(
|
|||
}
|
||||
}
|
||||
|
||||
TextState AttributeTable::textState(
|
||||
QPoint point,
|
||||
StateRequest request,
|
||||
int outerWidth) const {
|
||||
auto top = _margins.top();
|
||||
for (const auto &part : _parts) {
|
||||
const auto height = st::normalFont->height + st::chatUniqueRowSkip;
|
||||
if (point.y() >= top && point.y() < top + height) {
|
||||
point -= QPoint((outerWidth - width()) / 2 + _valueLeft, top);
|
||||
auto result = TextState();
|
||||
auto forText = request.forText();
|
||||
forText.align = style::al_topleft;
|
||||
result.link = part.value.getState(point, width(), forText).link;
|
||||
return result;
|
||||
}
|
||||
top += height;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QSize AttributeTable::countOptimalSize() {
|
||||
auto maxLabel = 0;
|
||||
auto maxValue = 0;
|
||||
|
|
|
@ -97,6 +97,10 @@ public:
|
|||
not_null<const MediaGeneric*> owner,
|
||||
const PaintContext &context,
|
||||
int outerWidth) const override;
|
||||
TextState textState(
|
||||
QPoint point,
|
||||
StateRequest request,
|
||||
int outerWidth) const override;
|
||||
|
||||
QSize countOptimalSize() override;
|
||||
QSize countCurrentSize(int newWidth) override;
|
||||
|
|
|
@ -1237,3 +1237,10 @@ newPeerNonOfficial: IconEmoji {
|
|||
icon: icon{{ "chat/mini_info_alert", windowFg }};
|
||||
padding: margins(0px, 2px, 0px, 0px);
|
||||
}
|
||||
newPeerUserpics: GroupCallUserpics {
|
||||
size: 16px;
|
||||
shift: 5px;
|
||||
stroke: 1px;
|
||||
align: align(left);
|
||||
}
|
||||
newPeerUserpicsPadding: margins(0px, 3px, 0px, 0px);
|
||||
|
|
|
@ -296,9 +296,10 @@ void PeerBadge::set(
|
|||
_botVerifiedData = std::make_unique<BotVerifiedData>();
|
||||
}
|
||||
if (details->iconId) {
|
||||
_botVerifiedData->icon = factory(
|
||||
Data::SerializeCustomEmojiId(details->iconId),
|
||||
{ .repaint = repaint });
|
||||
_botVerifiedData->icon = std::make_unique<Ui::Text::FirstFrameEmoji>(
|
||||
factory(
|
||||
Data::SerializeCustomEmojiId(details->iconId),
|
||||
{ .repaint = repaint }));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit e51fe382bda9f2944412078da70f04de5dd821f3
|
||||
Subproject commit b9ef54ad5eb6932930244f30a00ac9169cd09a46
|
Loading…
Add table
Reference in a new issue