mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-02-24 09:16:57 +00:00
Add more-chats-added bar to cloud folders.
This commit is contained in:
parent
63960c647b
commit
77939ae9bd
BIN
Telegram/Resources/animations/cloud_filters.tgs
Normal file
BIN
Telegram/Resources/animations/cloud_filters.tgs
Normal file
Binary file not shown.
@ -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>
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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(); });
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
223
Telegram/SourceFiles/ui/chat/more_chats_bar.cpp
Normal file
223
Telegram/SourceFiles/ui/chat/more_chats_bar.cpp
Normal 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
|
73
Telegram/SourceFiles/ui/chat/more_chats_bar.h
Normal file
73
Telegram/SourceFiles/ui/chat/more_chats_bar.h
Normal 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
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user