mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-07-24 14:33:02 +02:00
Show badges in new tabs.
This commit is contained in:
parent
8512154b45
commit
5943052cd1
5 changed files with 197 additions and 30 deletions
|
@ -93,6 +93,9 @@ struct BadgesState {
|
|||
friend inline constexpr auto operator<=>(
|
||||
BadgesState,
|
||||
BadgesState) = default;
|
||||
friend inline constexpr bool operator==(
|
||||
BadgesState,
|
||||
BadgesState) = default;
|
||||
|
||||
[[nodiscard]] bool empty() const {
|
||||
return !unread && !mention && !reaction;
|
||||
|
|
|
@ -180,11 +180,11 @@ void SubsectionTabs::setupSlider(
|
|||
slider->sectionActivated() | rpl::start_with_next([=](int active) {
|
||||
if (active >= 0
|
||||
&& active < _slice.size()
|
||||
&& _active != _slice[active]) {
|
||||
&& _active != _slice[active].thread) {
|
||||
auto params = Window::SectionShow();
|
||||
params.way = Window::SectionShow::Way::ClearStack;
|
||||
params.animated = anim::type::instant;
|
||||
_controller->showThread(_slice[active], {}, params);
|
||||
_controller->showThread(_slice[active].thread, {}, params);
|
||||
}
|
||||
}, slider->lifetime());
|
||||
|
||||
|
@ -209,11 +209,11 @@ void SubsectionTabs::setupSlider(
|
|||
if (availableFrom < full
|
||||
&& _beforeSkipped.value_or(0) > 0
|
||||
&& !_slice.empty()) {
|
||||
_around = _slice.front();
|
||||
_around = _slice.front().thread;
|
||||
refreshSlice();
|
||||
} else if (availableTill < full) {
|
||||
if (_afterAvailable > 0) {
|
||||
_around = _slice.back();
|
||||
_around = _slice.back().thread;
|
||||
refreshSlice();
|
||||
} else if (!_afterSkipped.has_value()) {
|
||||
_loading = true;
|
||||
|
@ -232,9 +232,9 @@ void SubsectionTabs::setupSlider(
|
|||
};
|
||||
auto sections = std::vector<Ui::SubsectionTab>();
|
||||
auto activeIndex = -1;
|
||||
for (const auto &thread : _slice) {
|
||||
for (const auto &item : _slice) {
|
||||
const auto index = int(sections.size());
|
||||
if (thread == _active) {
|
||||
if (item.thread == _active) {
|
||||
activeIndex = index;
|
||||
}
|
||||
const auto textFg = [=] {
|
||||
|
@ -243,23 +243,24 @@ void SubsectionTabs::setupSlider(
|
|||
st::windowActiveTextFg,
|
||||
slider->buttonActive(slider->buttonAt(index)));
|
||||
};
|
||||
if (const auto topic = thread->asTopic()) {
|
||||
if (const auto topic = item.thread->asTopic()) {
|
||||
if (vertical) {
|
||||
const auto general = topic->isGeneral();
|
||||
sections.push_back({
|
||||
.text = { topic->title() },
|
||||
.userpic = (topic->iconId()
|
||||
.text = { item.name },
|
||||
.userpic = (item.iconId
|
||||
? Ui::MakeEmojiThumbnail(
|
||||
&topic->owner(),
|
||||
Data::SerializeCustomEmojiId(topic->iconId()),
|
||||
Data::SerializeCustomEmojiId(item.iconId),
|
||||
paused,
|
||||
textFg)
|
||||
: Ui::MakeEmojiThumbnail(
|
||||
&topic->owner(),
|
||||
Data::TopicIconEmojiEntity({
|
||||
.title = (topic->isGeneral()
|
||||
.title = (general
|
||||
? Data::ForumGeneralIconTitle()
|
||||
: topic->title()),
|
||||
.colorId = (topic->isGeneral()
|
||||
: item.name),
|
||||
.colorId = (general
|
||||
? Data::ForumGeneralIconColor(
|
||||
st::windowSubTextFg->c)
|
||||
: topic->colorId()),
|
||||
|
@ -272,7 +273,7 @@ void SubsectionTabs::setupSlider(
|
|||
.text = topic->titleWithIcon(),
|
||||
});
|
||||
}
|
||||
} else if (const auto sublist = thread->asSublist()) {
|
||||
} else if (const auto sublist = item.thread->asSublist()) {
|
||||
const auto peer = sublist->sublistPeer();
|
||||
if (vertical) {
|
||||
sections.push_back({
|
||||
|
@ -294,6 +295,8 @@ void SubsectionTabs::setupSlider(
|
|||
.userpic = Ui::MakeAllSubsectionsThumbnail(textFg),
|
||||
});
|
||||
}
|
||||
auto §ion = sections.back();
|
||||
section.badges = item.badges;
|
||||
}
|
||||
|
||||
auto scrollSavingThread = (Data::Thread*)nullptr;
|
||||
|
@ -309,21 +312,24 @@ void SubsectionTabs::setupSlider(
|
|||
? slider->lookupSectionPosition(index + 1)
|
||||
: (indexPosition + scrollValue + 1);
|
||||
if (indexPosition <= scrollValue && nextPosition > scrollValue) {
|
||||
scrollSavingThread = _sectionsSlice[index];
|
||||
scrollSavingThread = _sectionsSlice[index].thread;
|
||||
scrollSavingShift = scrollValue - indexPosition;
|
||||
break;
|
||||
}
|
||||
indexPosition = nextPosition;
|
||||
}
|
||||
scrollSavingIndex = scrollSavingThread
|
||||
? int(ranges::find(_slice, not_null(scrollSavingThread))
|
||||
- begin(_slice))
|
||||
? int(ranges::find(
|
||||
_slice,
|
||||
not_null(scrollSavingThread),
|
||||
&Item::thread
|
||||
) - begin(_slice))
|
||||
: -1;
|
||||
if (scrollSavingIndex == _slice.size()) {
|
||||
scrollSavingIndex = -1;
|
||||
for (auto index = 0; index != count; ++index) {
|
||||
const auto thread = _sectionsSlice[index];
|
||||
if (ranges::contains(_slice, thread)) {
|
||||
const auto thread = _sectionsSlice[index].thread;
|
||||
if (ranges::contains(_slice, thread, &Item::thread)) {
|
||||
scrollSavingThread = thread;
|
||||
scrollSavingShift = scrollValue
|
||||
- slider->lookupSectionPosition(index);
|
||||
|
@ -483,6 +489,7 @@ void SubsectionTabs::setVisible(bool shown) {
|
|||
}
|
||||
|
||||
void SubsectionTabs::track() {
|
||||
using Event = Data::Session::ChatListEntryRefresh;
|
||||
if (const auto forum = _history->peer->forum()) {
|
||||
forum->topicDestroyed(
|
||||
) | rpl::start_with_next([=](not_null<Data::ForumTopic*> topic) {
|
||||
|
@ -491,6 +498,19 @@ void SubsectionTabs::track() {
|
|||
refreshSlice();
|
||||
}
|
||||
}, _lifetime);
|
||||
|
||||
forum->topicsList()->unreadStateChanges(
|
||||
) | rpl::start_with_next([=] {
|
||||
scheduleRefresh();
|
||||
}, _lifetime);
|
||||
|
||||
forum->owner().chatListEntryRefreshes(
|
||||
) | rpl::filter([=](const Event &event) {
|
||||
const auto topic = event.filterId ? nullptr : event.key.topic();
|
||||
return (topic && topic->forum() == forum);
|
||||
}) | rpl::start_with_next([=] {
|
||||
scheduleRefresh();
|
||||
}, _lifetime);
|
||||
} else if (const auto monoforum = _history->peer->monoforum()) {
|
||||
monoforum->sublistDestroyed(
|
||||
) | rpl::start_with_next([=](not_null<Data::SavedSublist*> sublist) {
|
||||
|
@ -499,12 +519,29 @@ void SubsectionTabs::track() {
|
|||
refreshSlice();
|
||||
}
|
||||
}, _lifetime);
|
||||
|
||||
monoforum->chatsList()->unreadStateChanges(
|
||||
) | rpl::start_with_next([=] {
|
||||
scheduleRefresh();
|
||||
}, _lifetime);
|
||||
|
||||
monoforum->owner().chatListEntryRefreshes(
|
||||
) | rpl::filter([=](const Event &event) {
|
||||
const auto sublist = event.filterId
|
||||
? nullptr
|
||||
: event.key.sublist();
|
||||
return (sublist && sublist->parent() == monoforum);
|
||||
}) | rpl::start_with_next([=] {
|
||||
scheduleRefresh();
|
||||
}, _lifetime);
|
||||
} else {
|
||||
Unexpected("Peer in SubsectionTabs::track.");
|
||||
}
|
||||
}
|
||||
|
||||
void SubsectionTabs::refreshSlice() {
|
||||
_refreshScheduled = false;
|
||||
|
||||
const auto forum = _history->peer->forum();
|
||||
const auto monoforum = _history->peer->monoforum();
|
||||
Assert(forum || monoforum);
|
||||
|
@ -512,15 +549,25 @@ void SubsectionTabs::refreshSlice() {
|
|||
const auto list = forum
|
||||
? forum->topicsList()
|
||||
: monoforum->chatsList();
|
||||
auto slice = std::vector<not_null<Data::Thread*>>();
|
||||
auto slice = std::vector<Item>();
|
||||
slice.reserve(_slice.size() + 10);
|
||||
const auto guard = gsl::finally([&] {
|
||||
if (_slice != slice) {
|
||||
_slice = std::move(slice);
|
||||
_refreshed.fire({});
|
||||
}
|
||||
});
|
||||
const auto push = [&](not_null<Data::Thread*> thread) {
|
||||
const auto topic = thread->asTopic();
|
||||
slice.push_back({
|
||||
.thread = thread,
|
||||
.badges = thread->chatListBadgesState(),
|
||||
.iconId = topic ? topic->iconId() : DocumentId(),
|
||||
.name = thread->chatListName(),
|
||||
});
|
||||
};
|
||||
if (!list) {
|
||||
slice.push_back(_history);
|
||||
push(_history);
|
||||
_beforeSkipped = _afterSkipped = 0;
|
||||
_afterAvailable = 0;
|
||||
return;
|
||||
|
@ -542,13 +589,25 @@ void SubsectionTabs::refreshSlice() {
|
|||
_afterAvailable = std::max(0, int(chats.end() - till));
|
||||
_afterSkipped = list->loaded() ? _afterAvailable : std::optional<int>();
|
||||
if (from == chats.begin()) {
|
||||
slice.push_back(_history);
|
||||
push(_history);
|
||||
}
|
||||
for (auto i = from; i != till; ++i) {
|
||||
slice.push_back((*i)->thread());
|
||||
push((*i)->thread());
|
||||
}
|
||||
}
|
||||
|
||||
void SubsectionTabs::scheduleRefresh() {
|
||||
if (_refreshScheduled) {
|
||||
return;
|
||||
}
|
||||
_refreshScheduled = true;
|
||||
InvokeQueued(_shadow, [=] {
|
||||
if (_refreshScheduled) {
|
||||
refreshSlice();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool SubsectionTabs::switchTo(
|
||||
not_null<Data::Thread*> thread,
|
||||
not_null<Ui::RpWidget*> parent) {
|
||||
|
|
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "dialogs/dialogs_common.h"
|
||||
|
||||
class History;
|
||||
|
||||
namespace Data {
|
||||
|
@ -53,12 +55,27 @@ public:
|
|||
void hide();
|
||||
|
||||
private:
|
||||
struct Item {
|
||||
not_null<Data::Thread*> thread;
|
||||
Dialogs::BadgesState badges;
|
||||
DocumentId iconId = 0;
|
||||
QString name;
|
||||
|
||||
friend inline constexpr auto operator<=>(
|
||||
const Item &,
|
||||
const Item &) = default;
|
||||
friend inline constexpr bool operator==(
|
||||
const Item &,
|
||||
const Item &) = default;
|
||||
};
|
||||
|
||||
void track();
|
||||
void setupHorizontal(not_null<QWidget*> parent);
|
||||
void setupVertical(not_null<QWidget*> parent);
|
||||
void toggleModes();
|
||||
void setVisible(bool shown);
|
||||
void refreshSlice();
|
||||
void scheduleRefresh();
|
||||
void loadMore();
|
||||
[[nodiscard]] rpl::producer<> dataChanged() const;
|
||||
|
||||
|
@ -74,8 +91,8 @@ private:
|
|||
Ui::RpWidget *_vertical = nullptr;
|
||||
Ui::RpWidget *_shadow = nullptr;
|
||||
|
||||
std::vector<not_null<Data::Thread*>> _slice;
|
||||
std::vector<not_null<Data::Thread*>> _sectionsSlice;
|
||||
std::vector<Item> _slice;
|
||||
std::vector<Item> _sectionsSlice;
|
||||
|
||||
not_null<Data::Thread*> _active;
|
||||
not_null<Data::Thread*> _around;
|
||||
|
@ -83,6 +100,7 @@ private:
|
|||
int _afterLimit = 0;
|
||||
int _afterAvailable = 0;
|
||||
bool _loading = false;
|
||||
bool _refreshScheduled = false;
|
||||
std::optional<int> _beforeSkipped;
|
||||
std::optional<int> _afterSkipped;
|
||||
|
||||
|
|
|
@ -8,9 +8,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/controls/subsection_tabs_slider.h"
|
||||
|
||||
#include "base/call_delayed.h"
|
||||
#include "dialogs/dialogs_three_state_icon.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/dynamic_image.h"
|
||||
#include "ui/unread_badge_paint.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_filter_icons.h"
|
||||
|
||||
namespace Ui {
|
||||
|
@ -105,6 +108,41 @@ void VerticalButton::paintEvent(QPaintEvent *e) {
|
|||
.align = style::al_top,
|
||||
.paused = _delegate->buttonPaused(),
|
||||
});
|
||||
|
||||
const auto &state = _data.badges;
|
||||
const auto top = _st.userpicTop / 2;
|
||||
auto right = width() - textLeft;
|
||||
UnreadBadgeStyle st;
|
||||
if (state.unread) {
|
||||
st.muted = state.unreadMuted;
|
||||
const auto counter = (state.unreadCounter <= 0)
|
||||
? QString()
|
||||
: ((state.mention || state.reaction)
|
||||
&& (state.unreadCounter > 999))
|
||||
? (u"99+"_q)
|
||||
: (state.unreadCounter > 999999)
|
||||
? (u"99999+"_q)
|
||||
: QString::number(state.unreadCounter);
|
||||
const auto badge = PaintUnreadBadge(p, counter, right, top, st);
|
||||
right -= badge.width() + st.padding;
|
||||
}
|
||||
if (state.mention || state.reaction) {
|
||||
UnreadBadgeStyle st;
|
||||
st.sizeId = state.mention
|
||||
? UnreadBadgeSize::Dialogs
|
||||
: UnreadBadgeSize::ReactionInDialogs;
|
||||
st.muted = state.mention
|
||||
? state.mentionMuted
|
||||
: state.reactionMuted;
|
||||
st.padding = 0;
|
||||
st.textTop = 0;
|
||||
const auto counter = QString();
|
||||
const auto badge = PaintUnreadBadge(p, counter, right, top, st);
|
||||
(state.mention
|
||||
? st::dialogsUnreadMention.icon
|
||||
: st::dialogsUnreadReaction.icon).paintInCenter(p, badge);
|
||||
right -= badge.width() + st.padding + st::dialogsUnreadPadding;
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalButton::HorizontalButton(
|
||||
|
@ -118,7 +156,28 @@ HorizontalButton::HorizontalButton(
|
|||
}
|
||||
|
||||
void HorizontalButton::updateSize() {
|
||||
resize(_st.strictSkip + _text.maxWidth(), _st.height);
|
||||
auto width = _st.strictSkip + _text.maxWidth();
|
||||
|
||||
const auto &state = _data.badges;
|
||||
UnreadBadgeStyle st;
|
||||
if (state.unread) {
|
||||
const auto counter = (state.unreadCounter <= 0)
|
||||
? QString()
|
||||
: QString::number(state.unreadCounter);
|
||||
const auto badge = CountUnreadBadgeSize(counter, st);
|
||||
width += badge.width() + st.padding;
|
||||
}
|
||||
if (state.mention || state.reaction) {
|
||||
st.sizeId = state.mention
|
||||
? UnreadBadgeSize::Dialogs
|
||||
: UnreadBadgeSize::ReactionInDialogs;
|
||||
st.padding = 0;
|
||||
st.textTop = 0;
|
||||
const auto counter = QString();
|
||||
const auto badge = CountUnreadBadgeSize(counter, st);
|
||||
width += badge.width() + st.padding + st::dialogsUnreadPadding;
|
||||
}
|
||||
resize(width, _st.height);
|
||||
}
|
||||
|
||||
void HorizontalButton::dataUpdatedHook() {
|
||||
|
@ -149,6 +208,36 @@ void HorizontalButton::paintEvent(QPaintEvent *e) {
|
|||
.availableWidth = _text.maxWidth(),
|
||||
.paused = _delegate->buttonPaused(),
|
||||
});
|
||||
|
||||
auto right = width() - _st.strictSkip + (_st.strictSkip / 2);
|
||||
UnreadBadgeStyle st;
|
||||
const auto &state = _data.badges;
|
||||
const auto badgeTop = (height() - st.size) / 2;
|
||||
if (state.unread) {
|
||||
st.muted = state.unreadMuted;
|
||||
const auto counter = (state.unreadCounter <= 0)
|
||||
? QString()
|
||||
: QString::number(state.unreadCounter);
|
||||
const auto badge = PaintUnreadBadge(p, counter, right, badgeTop, st);
|
||||
right -= badge.width() + st.padding;
|
||||
}
|
||||
if (state.mention || state.reaction) {
|
||||
UnreadBadgeStyle st;
|
||||
st.sizeId = state.mention
|
||||
? UnreadBadgeSize::Dialogs
|
||||
: UnreadBadgeSize::ReactionInDialogs;
|
||||
st.muted = state.mention
|
||||
? state.mentionMuted
|
||||
: state.reactionMuted;
|
||||
st.padding = 0;
|
||||
st.textTop = 0;
|
||||
const auto counter = QString();
|
||||
const auto badge = PaintUnreadBadge(p, counter, right, badgeTop, st);
|
||||
(state.mention
|
||||
? st::dialogsUnreadMention.icon
|
||||
: st::dialogsUnreadReaction.icon).paintInCenter(p, badge);
|
||||
right -= badge.width() + st.padding + st::dialogsUnreadPadding;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "dialogs/dialogs_common.h"
|
||||
#include "ui/round_rect.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
|
@ -25,10 +26,7 @@ class SubsectionButton;
|
|||
struct SubsectionTab {
|
||||
TextWithEntities text;
|
||||
std::shared_ptr<DynamicImage> userpic;
|
||||
int counter = 0;
|
||||
bool muted = false;
|
||||
bool mention = false;
|
||||
bool reaciton = false;
|
||||
Dialogs::BadgesState badges;
|
||||
};
|
||||
|
||||
struct SubsectionTabs {
|
||||
|
|
Loading…
Add table
Reference in a new issue