Add more-chats-added bar to cloud folders.

This commit is contained in:
John Preston 2023-04-03 16:16:38 +04:00
parent 63960c647b
commit 77939ae9bd
13 changed files with 599 additions and 24 deletions

Binary file not shown.

View File

@ -3,6 +3,7 @@
<file alias="change_number.tgs">../../animations/change_number.tgs</file>
<file alias="blocked_peers_empty.tgs">../../animations/blocked_peers_empty.tgs</file>
<file alias="filters.tgs">../../animations/filters.tgs</file>
<file alias="cloud_filters.tgs">../../animations/cloud_filters.tgs</file>
<file alias="local_passcode_enter.tgs">../../animations/local_passcode_enter.tgs</file>
<file alias="cloud_password/intro.tgs">../../animations/cloud_password/intro.tgs</file>
<file alias="cloud_password/password_input.tgs">../../animations/cloud_password/password_input.tgs</file>

View File

@ -191,35 +191,39 @@ void InitFilterLinkHeader(
}
void ImportInvite(
base::weak_ptr<Window::SessionController> weak,
const QString &slug,
FilterId filterId,
const base::flat_set<not_null<PeerData*>> &peers,
Fn<void()> done,
Fn<void()> fail) {
Fn<void(QString)> fail) {
Expects(!peers.empty());
const auto peer = peers.front();
const auto api = &peer->session().api();
const auto callback = [=](const MTPUpdates &result) {
api->applyUpdates(result);
if (slug.isEmpty()) {
peer->owner().chatsFilters().moreChatsHide(filterId, true);
}
done();
};
const auto error = [=](const MTP::Error &error) {
if (const auto strong = weak.get()) {
Ui::ShowMultilineToast({
.parentOverride = Window::Show(strong).toastParent(),
.text = { error.type() },
});
}
fail();
fail(error.type());
};
auto inputs = peers | ranges::views::transform([](auto peer) {
return MTPInputPeer(peer->input);
}) | ranges::to<QVector>();
api->request(MTPchatlists_JoinChatlistInvite(
MTP_string(slug),
MTP_vector<MTPInputPeer>(std::move(inputs))
)).done(callback).fail(error).send();
if (!slug.isEmpty()) {
api->request(MTPchatlists_JoinChatlistInvite(
MTP_string(slug),
MTP_vector<MTPInputPeer>(std::move(inputs))
)).done(callback).fail(error).send();
} else {
api->request(MTPchatlists_JoinChatlistUpdates(
MTP_inputChatlistDialogFilter(MTP_int(filterId)),
MTP_vector<MTPInputPeer>(std::move(inputs))
)).done(callback).fail(error).send();
}
}
ToggleChatsController::ToggleChatsController(
@ -462,10 +466,17 @@ void ProcessFilterInvite(
// #TODO filters
} else if (!state->importing) {
state->importing = true;
ImportInvite(weak, slug, peers, crl::guard(box, [=] {
ImportInvite(slug, filterId, peers, crl::guard(box, [=] {
ShowImportToast(weak, title, type, peers.size());
box->closeBox();
}), crl::guard(box, [=] {
}), crl::guard(box, [=](QString text) {
if (const auto strong = weak.get()) {
Ui::ShowMultilineToast({
.parentOverride = Window::Show(
strong).toastParent(),
.text = { text },
});
}
state->importing = false;
}));
}
@ -604,6 +615,17 @@ void CheckFilterInvite(
});
}
void ProcessFilterUpdate(
base::weak_ptr<Window::SessionController> weak,
FilterId filterId,
std::vector<not_null<PeerData*>> missing) {
if (const auto strong = missing.empty() ? weak.get() : nullptr) {
strong->session().data().chatsFilters().moreChatsHide(filterId);
return;
}
ProcessFilterInvite(weak, QString(), filterId, std::move(missing), {});
}
void ProcessFilterRemove(
base::weak_ptr<Window::SessionController> weak,
const QString &title,

View File

@ -25,6 +25,11 @@ void CheckFilterInvite(
not_null<Window::SessionController*> controller,
const QString &slug);
void ProcessFilterUpdate(
base::weak_ptr<Window::SessionController> weak,
FilterId filterId,
std::vector<not_null<PeerData*>> missing);
void ProcessFilterRemove(
base::weak_ptr<Window::SessionController> weak,
const QString &title,

View File

@ -102,13 +102,19 @@ struct Errors {
Unexpected("Peer type in ErrorForSharing.");
}
void ShowEmptyLinkError(not_null<Window::SessionController*> window) {
void ShowSaveError(
not_null<Window::SessionController*> window,
QString error) {
Ui::ShowMultilineToast({
.parentOverride = Window::Show(window).toastParent(),
.text = { tr::lng_filters_empty(tr::now) },
.text = { error },
});
}
void ShowEmptyLinkError(not_null<Window::SessionController*> window) {
ShowSaveError(window, tr::lng_filters_empty(tr::now));
}
void ChatFilterLinkBox(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session,
@ -545,7 +551,7 @@ void LinkController::addHeader(not_null<Ui::VerticalLayout*> container) {
auto icon = CreateLottieIcon(
verticalLayout,
{
.name = u"filters"_q,
.name = u"cloud_filters"_q,
.sizeOverride = {
st::settingsFilterIconSize,
st::settingsFilterIconSize,
@ -1084,7 +1090,8 @@ void ExportFilterLink(
void EditLinkChats(
const Data::ChatFilterLink &link,
base::flat_set<not_null<PeerData*>> peers) {
base::flat_set<not_null<PeerData*>> peers,
Fn<void(QString)> done) {
Expects(!peers.empty());
Expects(link.id != 0);
Expects(!link.url.isEmpty());
@ -1104,9 +1111,9 @@ void EditLinkChats(
)).done([=](const MTPExportedChatlistInvite &result) {
const auto &data = result.data();
const auto link = session->data().chatsFilters().add(id, result);
//done(link);
done(QString());
}).fail([=](const MTP::Error &error) {
//done({ .id = id });
done(error.type());
}).send();
}
@ -1122,6 +1129,7 @@ object_ptr<Ui::BoxContent> ShowLinkBox(
? rpl::single(link.title)
: tr::lng_filters_link_title());
const auto saving = std::make_shared<bool>(false);
raw->hasChangesValue(
) | rpl::start_with_next([=](bool has) {
box->setCloseByOutsideClick(!has);
@ -1129,11 +1137,23 @@ object_ptr<Ui::BoxContent> ShowLinkBox(
box->clearButtons();
if (has) {
box->addButton(tr::lng_settings_save(), [=] {
if (*saving) {
return;
}
const auto chosen = raw->selected();
if (chosen.empty()) {
ShowEmptyLinkError(window);
} else {
EditLinkChats(link, chosen);
*saving = true;
EditLinkChats(link, chosen, crl::guard(box, [=](
QString error) {
*saving = false;
if (error.isEmpty()) {
box->closeBox();
} else {
ShowSaveError(window, error);
}
}));
}
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });

View File

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "history/history_unread_things.h"
#include "ui/ui_utility.h"
#include "ui/chat/more_chats_bar.h"
#include "main/main_session.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
@ -31,6 +32,12 @@ constexpr auto kRefreshSuggestedTimeout = 7200 * crl::time(1000);
constexpr auto kLoadExceptionsAfter = 100;
constexpr auto kLoadExceptionsPerRequest = 100;
[[nodiscard]] crl::time RequestUpdatesEach(not_null<Session*> owner) {
const auto appConfig = &owner->session().account().appConfig();
return appConfig->get<int>(u"chatlist_update_period"_q, 3600)
* crl::time(1000);
}
} // namespace
ChatFilter::ChatFilter(
@ -287,7 +294,9 @@ bool ChatFilter::contains(not_null<History*> history) const {
|| _always.contains(history);
}
ChatFilters::ChatFilters(not_null<Session*> owner) : _owner(owner) {
ChatFilters::ChatFilters(not_null<Session*> owner)
: _owner(owner)
, _moreChatsTimer([=] { checkLoadMoreChatsLists(); }) {
_list.emplace_back();
crl::on_main(&owner->session(), [=] { load(); });
}
@ -855,4 +864,119 @@ rpl::producer<> ChatFilters::suggestedUpdated() const {
return _suggestedUpdated.events();
}
rpl::producer<Ui::MoreChatsBarContent> ChatFilters::moreChatsContent(
FilterId id) {
if (!id) {
return rpl::single(Ui::MoreChatsBarContent{ .count = 0 });
}
return [=](auto consumer) {
auto result = rpl::lifetime();
auto &entry = _moreChatsData[id];
auto watching = entry.watching.lock();
if (!watching) {
watching = std::make_shared<bool>(true);
entry.watching = watching;
}
result.add([watching] {});
_moreChatsUpdated.events_starting_with_copy(
id
) | rpl::start_with_next([=] {
consumer.put_next(Ui::MoreChatsBarContent{
.count = int(moreChats(id).size()),
});
}, result);
loadMoreChatsList(id);
return result;
};
}
const std::vector<not_null<PeerData*>> &ChatFilters::moreChats(
FilterId id) const {
static const auto kEmpty = std::vector<not_null<PeerData*>>();
if (!id) {
return kEmpty;
}
const auto i = _moreChatsData.find(id);
return (i != end(_moreChatsData)) ? i->second.missing : kEmpty;
}
void ChatFilters::moreChatsHide(FilterId id, bool localOnly) {
if (!localOnly) {
const auto api = &_owner->session().api();
api->request(MTPchatlists_HideChatlistUpdates(
MTP_inputChatlistDialogFilter(MTP_int(id))
)).send();
}
const auto i = _moreChatsData.find(id);
if (i != end(_moreChatsData)) {
if (const auto requestId = base::take(i->second.requestId)) {
_owner->session().api().request(requestId).cancel();
}
i->second.missing = {};
i->second.lastUpdate = crl::now();
_moreChatsUpdated.fire_copy(id);
}
}
void ChatFilters::loadMoreChatsList(FilterId id) {
Expects(id != 0);
const auto i = ranges::find(_list, id, &ChatFilter::id);
if (i == end(_list) || !i->chatlist()) {
return;
}
auto &entry = _moreChatsData[id];
const auto now = crl::now();
if (!entry.watching.lock() || entry.requestId) {
return;
}
const auto last = entry.lastUpdate;
const auto next = last ? (last + RequestUpdatesEach(_owner)) : 0;
if (next > now) {
if (!_moreChatsTimer.isActive()) {
_moreChatsTimer.callOnce(next - now);
}
return;
}
auto &api = _owner->session().api();
entry.requestId = api.request(MTPchatlists_GetChatlistUpdates(
MTP_inputChatlistDialogFilter(MTP_int(id))
)).done([=](const MTPchatlists_ChatlistUpdates &result) {
const auto &data = result.data();
_owner->processUsers(data.vusers());
_owner->processChats(data.vchats());
auto list = ranges::views::all(
data.vmissing_peers().v
) | ranges::views::transform([&](const MTPPeer &peer) {
return _owner->peer(peerFromMTP(peer));
}) | ranges::to_vector;
auto &entry = _moreChatsData[id];
entry.requestId = 0;
entry.lastUpdate = crl::now();
if (!_moreChatsTimer.isActive()) {
_moreChatsTimer.callOnce(RequestUpdatesEach(_owner));
}
if (entry.missing != list) {
entry.missing = std::move(list);
_moreChatsUpdated.fire_copy(id);
}
}).fail([=] {
auto &entry = _moreChatsData[id];
entry.requestId = 0;
entry.lastUpdate = crl::now();
}).send();
}
void ChatFilters::checkLoadMoreChatsLists() {
for (const auto &[id, entry] : _moreChatsData) {
loadMoreChatsList(id);
}
}
} // namespace Data

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "base/flags.h"
#include "base/timer.h"
class History;
@ -16,6 +17,10 @@ class MainList;
class Key;
} // namespace Dialogs
namespace Ui {
struct MoreChatsBarContent;
} // namespace Ui
namespace Data {
class Session;
@ -159,7 +164,20 @@ public:
FilterId id) const;
void reloadChatlistLinks(FilterId id);
[[nodiscard]] rpl::producer<Ui::MoreChatsBarContent> moreChatsContent(
FilterId id);
[[nodiscard]] const std::vector<not_null<PeerData*>> &moreChats(
FilterId id) const;
void moreChatsHide(FilterId id, bool localOnly = false);
private:
struct MoreChatsData {
std::vector<not_null<PeerData*>> missing;
crl::time lastUpdate = 0;
mtpRequestId requestId = 0;
std::weak_ptr<bool> watching;
};
void load(bool force);
void received(const QVector<MTPDialogFilter> &list);
bool applyOrder(const QVector<MTPint> &order);
@ -167,6 +185,9 @@ private:
void applyInsert(ChatFilter filter, int position);
void applyRemove(int position);
void checkLoadMoreChatsLists();
void loadMoreChatsList(FilterId id);
const not_null<Session*> _owner;
std::vector<ChatFilter> _list;
@ -190,6 +211,10 @@ private:
rpl::event_stream<FilterId> _chatlistLinksUpdated;
mtpRequestId _linksRequestId = 0;
base::flat_map<FilterId, MoreChatsData> _moreChatsData;
rpl::event_stream<FilterId> _moreChatsUpdated;
base::Timer _moreChatsTimer;
};
} // namespace Data

View File

@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/radial_animation.h"
#include "ui/chat/requests_bar.h"
#include "ui/chat/group_call_bar.h"
#include "ui/chat/more_chats_bar.h"
#include "ui/controls/download_bar.h"
#include "ui/controls/jump_down_button.h"
#include "ui/painter.h"
@ -34,6 +35,7 @@ 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 "api/api_chat_filters.h"
#include "apiwrap.h"
#include "base/event_filter.h"
#include "core/application.h"
@ -440,6 +442,7 @@ Widget::Widget(
_searchForNarrowFilters->setRippleColorOverride(color);
}, lifetime());
setupMoreChatsBar();
setupDownloadBar();
}
}
@ -542,6 +545,49 @@ void Widget::setupScrollUpButton() {
updateScrollUpVisibility();
}
void Widget::setupMoreChatsBar() {
if (_layout == Layout::Child) {
return;
}
controller()->activeChatsFilter(
) | rpl::start_with_next([=](FilterId id) {
if (!id) {
_moreChatsBar = nullptr;
updateControlsGeometry();
return;
}
const auto filters = &session().data().chatsFilters();
_moreChatsBar = std::make_unique<Ui::MoreChatsBar>(
this,
filters->moreChatsContent(id));
_moreChatsBar->barClicks(
) | rpl::start_with_next([=] {
if (const auto missing = filters->moreChats(id)
; !missing.empty()) {
Api::ProcessFilterUpdate(controller(), id, missing);
}
}, _moreChatsBar->lifetime());
_moreChatsBar->closeClicks(
) | rpl::start_with_next([=] {
Api::ProcessFilterUpdate(controller(), id, {});
}, _moreChatsBar->lifetime());
if (_showAnimation) {
_moreChatsBar->hide();
} else {
_moreChatsBar->show();
_moreChatsBar->finishAnimating();
}
_moreChatsBar->heightValue(
) | rpl::start_with_next([=] {
updateControlsGeometry();
}, _moreChatsBar->lifetime());
}, lifetime());
}
void Widget::setupDownloadBar() {
if (_layout == Layout::Child) {
return;
@ -735,6 +781,9 @@ void Widget::updateControlsVisibility(bool fast) {
_updateTelegram->show();
}
_searchControls->setVisible(!_openedFolder && !_openedForum);
if (_moreChatsBar) {
_moreChatsBar->show();
}
if (_openedFolder || _openedForum) {
_subsectionTopBar->show();
if (_forumTopShadow) {
@ -1165,6 +1214,9 @@ void Widget::startSlideAnimation(
if (_subsectionTopBar) {
_subsectionTopBar->hide();
}
if (_moreChatsBar) {
_moreChatsBar->hide();
}
if (_forumTopShadow) {
_forumTopShadow->hide();
}
@ -2417,7 +2469,13 @@ void Widget::updateControlsGeometry() {
barw,
st::lineWidth);
}
const auto forumGroupCallTop = filterAreaTop + filterAreaHeight;
const auto moreChatsBarTop = filterAreaTop + filterAreaHeight;
if (_moreChatsBar) {
_moreChatsBar->move(0, moreChatsBarTop);
_moreChatsBar->resizeToWidth(barw);
}
const auto forumGroupCallTop = moreChatsBarTop
+ (_moreChatsBar ? _moreChatsBar->height() : 0);
if (_forumGroupCallBar) {
_forumGroupCallBar->move(0, forumGroupCallTop);
_forumGroupCallBar->resizeToWidth(barw);

View File

@ -44,6 +44,7 @@ class PlainShadow;
class DownloadBar;
class GroupCallBar;
class RequestsBar;
class MoreChatsBar;
class JumpDownButton;
template <typename Widget>
class FadeWrapScaled;
@ -158,6 +159,7 @@ private:
void setupSupportMode();
void setupConnectingWidget();
void setupMainMenuToggle();
void setupMoreChatsBar();
void setupDownloadBar();
void setupShortcuts();
[[nodiscard]] bool searchForPeersRequired(const QString &query) const;
@ -234,6 +236,8 @@ private:
object_ptr<Ui::CrossButton> _cancelSearch;
object_ptr<Ui::IconButton> _lockUnlock;
std::unique_ptr<Ui::MoreChatsBar> _moreChatsBar;
std::unique_ptr<Ui::PlainShadow> _forumTopShadow;
std::unique_ptr<Ui::GroupCallBar> _forumGroupCallBar;
std::unique_ptr<Ui::RequestsBar> _forumRequestsBar;

View File

@ -1281,3 +1281,21 @@ historySendDisabled: FlatLabel(defaultFlatLabel) {
historySendDisabledIcon: icon {{ "emoji/premium_lock", placeholderFgActive }};
historySendDisabledIconSkip: 20px;
historySendDisabledPosition: point(0px, 0px);
moreChatsBarHeight: 48px;
moreChatsBarTextPosition: point(12px, 4px);
moreChatsBarStatusPosition: point(12px, 24px);
moreChatsBarClose: IconButton(defaultIconButton) {
width: 48px;
height: 48px;
icon: boxTitleCloseIcon;
iconOver: boxTitleCloseIconOver;
iconPosition: point(12px, -1px);
rippleAreaPosition: point(0px, 4px);
rippleAreaSize: 40px;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
}

View File

@ -0,0 +1,223 @@
/*
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/more_chats_bar.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h"
#include "ui/text/text_options.h"
#include "ui/painter.h"
#include "lang/lang_keys.h"
#include "styles/style_chat.h"
#include "styles/style_window.h" // st::columnMinimalWidthLeft
namespace Ui {
MoreChatsBar::MoreChatsBar(
not_null<QWidget*> parent,
rpl::producer<MoreChatsBarContent> content)
: _wrap(parent, object_ptr<RpWidget>(parent))
, _inner(_wrap.entity())
, _shadow(std::make_unique<PlainShadow>(_wrap.parentWidget()))
, _close(_inner.get(), st::moreChatsBarClose) {
_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([=](MoreChatsBarContent &&content) {
_content = content;
if (_content.count > 0) {
_text.setText(
st::defaultMessageBar.title,
tr::lng_filters_bar_you_can(
tr::now,
lt_count,
_content.count),
Ui::NameTextOptions());
_status.setText(
st::defaultMessageBar.text,
tr::lng_filters_bar_view(
tr::now,
lt_count,
_content.count),
Ui::NameTextOptions());
}
_inner->update();
}, lifetime());
std::move(
copy
) | rpl::map([=](const MoreChatsBarContent &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());
setupInner();
}
MoreChatsBar::~MoreChatsBar() = default;
void MoreChatsBar::setupInner() {
_inner->resize(0, st::moreChatsBarHeight);
_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::to_empty | rpl::start_to_stream(_barClicks, _inner->lifetime());
_wrap.geometryValue(
) | rpl::start_with_next([=](QRect rect) {
updateShadowGeometry(rect);
updateControlsGeometry(rect);
}, _inner->lifetime());
}
void MoreChatsBar::paint(Painter &p) {
p.fillRect(_inner->rect(), st::historyComposeAreaBg);
const auto width = std::max(
_inner->width(),
st::columnMinimalWidthLeft);
const auto available = width
- st::moreChatsBarTextPosition.x()
- st::moreChatsBarClose.width;
p.setPen(st::defaultMessageBar.titleFg);
_text.drawElided(
p,
st::moreChatsBarTextPosition.x(),
st::moreChatsBarTextPosition.y(),
available);
p.setPen(st::defaultMessageBar.textFg);
_status.drawElided(
p,
st::moreChatsBarStatusPosition.x(),
st::moreChatsBarStatusPosition.y(),
available);
}
void MoreChatsBar::updateControlsGeometry(QRect wrapGeometry) {
const auto hidden = _wrap.isHidden() || !wrapGeometry.height();
if (_shadow->isHidden() != hidden) {
_shadow->setVisible(!hidden);
}
const auto width = std::max(
wrapGeometry.width(),
st::columnMinimalWidthLeft);
_close->move(width - _close->width(), 0);
}
void MoreChatsBar::setShadowGeometryPostprocess(Fn<QRect(QRect)> postprocess) {
_shadowGeometryPostprocess = std::move(postprocess);
updateShadowGeometry(_wrap.geometry());
}
void MoreChatsBar::updateShadowGeometry(QRect wrapGeometry) {
const auto regular = QRect(
wrapGeometry.x(),
wrapGeometry.y() + wrapGeometry.height(),
wrapGeometry.width(),
st::lineWidth);
_shadow->setGeometry(_shadowGeometryPostprocess
? _shadowGeometryPostprocess(regular)
: regular);
}
void MoreChatsBar::show() {
if (!_forceHidden) {
return;
}
_forceHidden = false;
if (_shouldBeShown) {
_wrap.show(anim::type::instant);
_shadow->show();
}
}
void MoreChatsBar::hide() {
if (_forceHidden) {
return;
}
_forceHidden = true;
_wrap.hide(anim::type::instant);
_shadow->hide();
}
void MoreChatsBar::raise() {
_wrap.raise();
_shadow->raise();
}
void MoreChatsBar::finishAnimating() {
_wrap.finishAnimating();
}
void MoreChatsBar::move(int x, int y) {
_wrap.move(x, y);
}
void MoreChatsBar::resizeToWidth(int width) {
_wrap.entity()->resizeToWidth(width);
_inner->resizeToWidth(width);
}
int MoreChatsBar::height() const {
return !_forceHidden
? _wrap.height()
: _shouldBeShown
? st::moreChatsBarHeight
: 0;
}
rpl::producer<int> MoreChatsBar::heightValue() const {
return _wrap.heightValue();
}
rpl::producer<> MoreChatsBar::barClicks() const {
return _barClicks.events();
}
rpl::producer<> MoreChatsBar::closeClicks() const {
return _close->clicks() | rpl::to_empty;
}
} // namespace Ui

View File

@ -0,0 +1,73 @@
/*
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 "ui/text/text.h"
#include "base/object_ptr.h"
#include "base/timer.h"
class Painter;
namespace Ui {
class PlainShadow;
class IconButton;
struct MoreChatsBarContent {
int count = 0;
};
class MoreChatsBar final {
public:
MoreChatsBar(
not_null<QWidget*> parent,
rpl::producer<MoreChatsBarContent> content);
~MoreChatsBar();
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::producer<> closeClicks() const;
[[nodiscard]] rpl::lifetime &lifetime() {
return _wrap.lifetime();
}
private:
void updateShadowGeometry(QRect wrapGeometry);
void updateControlsGeometry(QRect wrapGeometry);
void setupInner();
void paint(Painter &p);
SlideWrap<> _wrap;
not_null<RpWidget*> _inner;
std::unique_ptr<PlainShadow> _shadow;
object_ptr<IconButton> _close;
rpl::event_stream<> _barClicks;
Fn<QRect(QRect)> _shadowGeometryPostprocess;
bool _shouldBeShown = false;
bool _forceHidden = false;
MoreChatsBarContent _content;
Ui::Text::String _text;
Ui::Text::String _status;
};
} // namespace Ui

View File

@ -222,6 +222,8 @@ PRIVATE
ui/chat/message_bar.h
ui/chat/message_bubble.cpp
ui/chat/message_bubble.h
ui/chat/more_chats_bar.cpp
ui/chat/more_chats_bar.h
ui/chat/pinned_bar.cpp
ui/chat/pinned_bar.h
ui/chat/requests_bar.cpp