Added favorite link button to folders strip.
@@ -1985,6 +1985,8 @@ PRIVATE
|
||||
window/window_connecting_widget.h
|
||||
window/window_controller.cpp
|
||||
window/window_controller.h
|
||||
window/window_filters_favorite.cpp
|
||||
window/window_filters_favorite.h
|
||||
window/window_filters_menu.cpp
|
||||
window/window_filters_menu.h
|
||||
window/window_history_hider.cpp
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="36px" height="36px" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M27 18.5c0 3.6-4 6.5-9 6.5a11 11 0 0 1-3-.4L9.5 27l1-3.4A6.3 6.3 0 0 1 9 18.5C9 14.9 13 12 18 12s9 2.9 9 6.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 307 B |
@@ -0,0 +1 @@
|
||||
<svg width="36px" height="36px" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 13a2 2 0 0 1 2-2h5l2.5 3H26a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H10a2 2 0 0 1-2-2z"/></svg>
|
||||
|
After Width: | Height: | Size: 276 B |
@@ -0,0 +1 @@
|
||||
<svg width="36px" height="36px" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="14.5" r="4.5"/><path d="M9.5 27.5a8.5 8.5 0 0 1 17 0"/></svg>
|
||||
|
After Width: | Height: | Size: 261 B |
@@ -0,0 +1 @@
|
||||
<svg width="36px" height="36px" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="18" r="10"/><path d="M8 18h20"/><path d="M18 8c3 2.5 4.5 6 4.5 10s-1.5 7.5-4.5 10c-3-2.5-4.5-6-4.5-10s1.5-7.5 4.5-10z"/></svg>
|
||||
|
After Width: | Height: | Size: 326 B |
@@ -0,0 +1 @@
|
||||
<svg width="36px" height="36px" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8a6.5 6.5 0 0 1 6.5 6.5v4l2 3h-17l2-3v-4A6.5 6.5 0 0 1 18 8z"/><path d="M15.5 25a2.5 2.5 0 0 0 5 0"/></svg>
|
||||
|
After Width: | Height: | Size: 300 B |
@@ -0,0 +1 @@
|
||||
<svg width="36px" height="36px" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="10" y="16.5" width="16" height="11.5" rx="2.5"/><path d="M13.5 16.5v-3.5a4.5 4.5 0 0 1 9 0v3.5"/></svg>
|
||||
|
After Width: | Height: | Size: 292 B |
@@ -0,0 +1 @@
|
||||
<svg width="36px" height="36px" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M26.07 16.06 28.92 16.71 28.92 19.29 26.07 19.94 25.08 22.34 26.64 24.81 24.81 26.64 22.34 25.08 19.94 26.07 19.29 28.92 16.71 28.92 16.06 26.07 13.66 25.08 11.19 26.64 9.36 24.81 10.92 22.34 9.93 19.94 7.08 19.29 7.08 16.71 9.93 16.06 10.92 13.66 9.36 11.19 11.19 9.36 13.66 10.92 16.06 9.93 16.71 7.08 19.29 7.08 19.94 9.93 22.34 10.92 24.81 9.36 26.64 11.19 25.08 13.66Z"/><circle cx="18" cy="18" r="3.6"/></svg>
|
||||
|
After Width: | Height: | Size: 604 B |
@@ -0,0 +1 @@
|
||||
<svg width="36px" height="36px" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="18" r="10"/><circle cx="14.5" cy="15.5" r="1.1" fill="#FFFFFF" stroke="none"/><circle cx="21.5" cy="15.5" r="1.1" fill="#FFFFFF" stroke="none"/><path d="M13.5 21a5 5 0 0 0 9 0"/></svg>
|
||||
|
After Width: | Height: | Size: 384 B |
@@ -7,7 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "settings/settings_experimental.h"
|
||||
|
||||
#include "settings/settings_common.h"
|
||||
#include "data/components/passkeys.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/search_field_controller.h"
|
||||
@@ -48,6 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mtproto/session_private.h"
|
||||
#include "webview/webview_embed.h"
|
||||
#include "window/main_window.h"
|
||||
#include "window/window_filters_favorite.h"
|
||||
#include "window/window_peer_menu.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/window_controller.h"
|
||||
@@ -215,6 +218,61 @@ void AddOption(
|
||||
}, wrap->lifetime());
|
||||
}
|
||||
|
||||
void AddFavoriteLinkButton(
|
||||
not_null<Window::Controller*> window,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<QString> query,
|
||||
Fn<void(const QString&, not_null<QWidget*>)> registerHighlight) {
|
||||
const auto option = &base::options::lookup<QString>(
|
||||
Window::kOptionFolderFavoriteLink);
|
||||
const auto name = option->name().isEmpty()
|
||||
? option->id()
|
||||
: option->name();
|
||||
const auto &description = option->description();
|
||||
|
||||
const auto wrap = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
container,
|
||||
object_ptr<Ui::VerticalLayout>(container)));
|
||||
const auto inner = wrap->entity();
|
||||
|
||||
auto label = rpl::single(
|
||||
rpl::empty
|
||||
) | rpl::then(
|
||||
option->changes()
|
||||
) | rpl::map([option] {
|
||||
return option->value();
|
||||
});
|
||||
const auto button = AddButtonWithLabel(
|
||||
inner,
|
||||
rpl::single(name),
|
||||
std::move(label),
|
||||
st::settingsButtonNoIcon);
|
||||
button->setClickedCallback([=] {
|
||||
window->show(Box(Window::EditFolderFavoriteLinkBox));
|
||||
});
|
||||
|
||||
if (registerHighlight) {
|
||||
registerHighlight(u"experimental/"_q + option->id(), button);
|
||||
}
|
||||
|
||||
if (!description.isEmpty()) {
|
||||
Ui::AddSkip(inner, st::settingsCheckboxesSkip);
|
||||
Ui::AddDividerText(inner, rpl::single(description));
|
||||
Ui::AddSkip(inner, st::settingsCheckboxesSkip);
|
||||
}
|
||||
|
||||
std::move(
|
||||
query
|
||||
) | rpl::on_next([=](const QString &text) {
|
||||
const auto trimmed = text.trimmed();
|
||||
const auto matches = trimmed.isEmpty()
|
||||
|| name.contains(trimmed, Qt::CaseInsensitive)
|
||||
|| description.contains(trimmed, Qt::CaseInsensitive);
|
||||
wrap->toggle(matches, anim::type::instant);
|
||||
}, wrap->lifetime());
|
||||
}
|
||||
|
||||
void SetupExperimental(
|
||||
not_null<Window::Controller*> window,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
@@ -317,6 +375,12 @@ void SetupExperimental(
|
||||
addToggle(HistoryView::Controls::kOptionMacCmdReplyImmediately);
|
||||
addToggle(Ui::kOptionQScroller);
|
||||
addToggle(FFmpeg::kOptionFFmpegMultiThread);
|
||||
|
||||
AddFavoriteLinkButton(
|
||||
window,
|
||||
container,
|
||||
rpl::duplicate(query),
|
||||
registerHighlight);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -85,3 +85,12 @@ foldersPoo: icon {{ "folders/folders_poo", sideBarIconFg }};
|
||||
foldersPooActive: icon {{ "folders/folders_poo", sideBarIconFgActive }};
|
||||
|
||||
filtersEdit: icon {{ "folders/folders_edit", sideBarIconFg }};
|
||||
|
||||
foldersSectionSettings: icon {{ "folders/svg/section_settings-36x36", sideBarIconFg }};
|
||||
foldersSectionFolders: icon {{ "folders/svg/section_folders-36x36", sideBarIconFg }};
|
||||
foldersSectionNotifications: icon {{ "folders/svg/section_notifications-36x36", sideBarIconFg }};
|
||||
foldersSectionPrivacy: icon {{ "folders/svg/section_privacy-36x36", sideBarIconFg }};
|
||||
foldersSectionStickers: icon {{ "folders/svg/section_stickers-36x36", sideBarIconFg }};
|
||||
foldersSectionChat: icon {{ "folders/svg/section_chat-36x36", sideBarIconFg }};
|
||||
foldersSectionLanguage: icon {{ "folders/svg/section_language-36x36", sideBarIconFg }};
|
||||
foldersSectionInfo: icon {{ "folders/svg/section_info-36x36", sideBarIconFg }};
|
||||
|
||||
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
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 "window/window_filters_favorite.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "base/options.h"
|
||||
#include "base/qthelp_url.h"
|
||||
#include "base/weak_ptr.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "core/local_url_handlers.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_msg_id.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_peer_id.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_types.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/stickers/data_stickers_set.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/basic_click_handlers.h"
|
||||
#include "ui/dynamic_image.h"
|
||||
#include "ui/dynamic_thumbnails.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/widgets/fields/input_field.h"
|
||||
#include "ui/widgets/menu/menu_add_action_callback.h"
|
||||
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_filter_icons.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_window.h"
|
||||
|
||||
#include <QtGui/QtEvents>
|
||||
|
||||
namespace Window {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxLabelLines = 3;
|
||||
|
||||
base::options::option<QString> FavoriteLink({
|
||||
.id = kOptionFolderFavoriteLink,
|
||||
.name = "Favorite folder button",
|
||||
.description = "Favorite button at the bottom of the folders strip "
|
||||
"that opens a tg:// or t.me/ link.",
|
||||
});
|
||||
|
||||
enum class Category {
|
||||
Generic,
|
||||
Peer,
|
||||
Invite,
|
||||
StickerSet,
|
||||
EmojiSet,
|
||||
ChatFolder,
|
||||
Settings,
|
||||
};
|
||||
|
||||
struct ParsedLink {
|
||||
Category category = Category::Generic;
|
||||
QString value;
|
||||
bool phone = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] ParsedLink ParseFavoriteLink(const QString &link) {
|
||||
const auto local = Core::TryConvertUrlToLocal(link.trimmed());
|
||||
const auto prefix = u"tg://"_q;
|
||||
if (!local.startsWith(prefix, Qt::CaseInsensitive)) {
|
||||
return {};
|
||||
}
|
||||
const auto command = local.mid(prefix.size());
|
||||
const auto queryStart = command.indexOf('?');
|
||||
const auto name = (queryStart < 0)
|
||||
? command
|
||||
: command.mid(0, queryStart);
|
||||
const auto params = (queryStart < 0)
|
||||
? QMap<QString, QString>()
|
||||
: qthelp::url_parse_params(
|
||||
command.mid(queryStart + 1),
|
||||
qthelp::UrlParamNameTransform::ToLower);
|
||||
const auto starts = [&](const QString &n) {
|
||||
return name.startsWith(n, Qt::CaseInsensitive);
|
||||
};
|
||||
const auto with = [](Category category, const QString &value) {
|
||||
return value.isEmpty() ? ParsedLink() : ParsedLink{ category, value };
|
||||
};
|
||||
if (starts(u"resolve"_q)) {
|
||||
const auto phone = params.value(u"phone"_q);
|
||||
if (!phone.isEmpty()) {
|
||||
return { Category::Peer, phone, true };
|
||||
}
|
||||
const auto domain = params.value(u"domain"_q);
|
||||
return (domain.isEmpty() || domain == u"giftcode"_q)
|
||||
? ParsedLink()
|
||||
: ParsedLink{ Category::Peer, domain };
|
||||
} else if (starts(u"join"_q)) {
|
||||
return with(Category::Invite, params.value(u"invite"_q));
|
||||
} else if (starts(u"addstickers"_q)) {
|
||||
return with(Category::StickerSet, params.value(u"set"_q));
|
||||
} else if (starts(u"addemoji"_q)) {
|
||||
return with(Category::EmojiSet, params.value(u"set"_q));
|
||||
} else if (starts(u"addlist"_q)) {
|
||||
return with(Category::ChatFolder, params.value(u"slug"_q));
|
||||
} else if (starts(u"settings"_q)) {
|
||||
auto path = name.mid(u"settings"_q.size());
|
||||
while (path.startsWith('/')) {
|
||||
path = path.mid(1);
|
||||
}
|
||||
return { Category::Settings, path };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
struct KnownContent {
|
||||
const style::icon *icon = nullptr;
|
||||
QString label;
|
||||
};
|
||||
|
||||
[[nodiscard]] KnownContent SettingsContent(const QString &path) {
|
||||
const auto starts = [&](const char *prefix) {
|
||||
return path.startsWith(QString::fromLatin1(prefix), Qt::CaseInsensitive);
|
||||
};
|
||||
if (path.isEmpty()) {
|
||||
return { &st::foldersSectionSettings, tr::lng_menu_settings(tr::now) };
|
||||
} else if (starts("folders")) {
|
||||
return { &st::foldersSectionFolders, tr::lng_filters_title(tr::now) };
|
||||
} else if (starts("notifications")) {
|
||||
return {
|
||||
&st::foldersSectionNotifications,
|
||||
tr::lng_settings_section_notify(tr::now),
|
||||
};
|
||||
} else if (starts("privacy") || starts("phone_privacy")) {
|
||||
return {
|
||||
&st::foldersSectionPrivacy,
|
||||
tr::lng_settings_section_privacy(tr::now),
|
||||
};
|
||||
} else if (starts("appearance/stickers") || starts("stickers")) {
|
||||
return {
|
||||
&st::foldersSectionStickers,
|
||||
tr::lng_settings_stickers_emoji(tr::now),
|
||||
};
|
||||
} else if (starts("appearance") || starts("chat") || starts("themes")) {
|
||||
return {
|
||||
&st::foldersSectionChat,
|
||||
tr::lng_settings_section_chat_settings(tr::now),
|
||||
};
|
||||
} else if (starts("devices")) {
|
||||
return {
|
||||
&st::foldersSectionSettings,
|
||||
tr::lng_settings_sessions_title(tr::now),
|
||||
};
|
||||
} else if (starts("language")) {
|
||||
return {
|
||||
&st::foldersSectionLanguage,
|
||||
tr::lng_settings_language(tr::now),
|
||||
};
|
||||
} else if (starts("advanced")) {
|
||||
return {
|
||||
&st::foldersSectionSettings,
|
||||
tr::lng_settings_advanced(tr::now),
|
||||
};
|
||||
} else if (starts("experiment")) {
|
||||
return {
|
||||
&st::foldersSectionSettings,
|
||||
tr::lng_settings_experimental(tr::now),
|
||||
};
|
||||
} else if (starts("edit")
|
||||
|| starts("information")
|
||||
|| starts("my-profile")
|
||||
|| starts("edit_profile")) {
|
||||
return {
|
||||
&st::foldersSectionInfo,
|
||||
tr::lng_settings_section_info(tr::now),
|
||||
};
|
||||
}
|
||||
return { &st::foldersSectionSettings, tr::lng_menu_settings(tr::now) };
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const char kOptionFolderFavoriteLink[] = "folder-favorite-link";
|
||||
|
||||
bool ValidFolderFavoriteLink(const QString &link) {
|
||||
return Core::TryConvertUrlToLocal(link.trimmed())
|
||||
.startsWith(u"tg://"_q, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
void EditFolderFavoriteLinkBox(not_null<Ui::GenericBox*> box) {
|
||||
box->setTitle(tr::lng_menu_formatting_link_edit());
|
||||
const auto field = box->addRow(object_ptr<Ui::InputField>(
|
||||
box,
|
||||
st::defaultInputField,
|
||||
tr::lng_info_link_label(),
|
||||
FavoriteLink.value()));
|
||||
box->setFocusCallback([=] { field->setFocusFast(); });
|
||||
const auto submit = [=] {
|
||||
const auto link = field->getLastText().trimmed();
|
||||
if (link.isEmpty()) {
|
||||
FavoriteLink.set(QString());
|
||||
box->closeBox();
|
||||
} else if (!ValidFolderFavoriteLink(link)) {
|
||||
field->showError();
|
||||
} else {
|
||||
FavoriteLink.set(link);
|
||||
box->closeBox();
|
||||
}
|
||||
};
|
||||
field->submits(
|
||||
) | rpl::on_next([=](auto) { submit(); }, field->lifetime());
|
||||
box->addButton(tr::lng_settings_save(), submit);
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}
|
||||
|
||||
FolderFavoriteButton::FolderFavoriteButton(
|
||||
QWidget *parent,
|
||||
not_null<SessionController*> controller,
|
||||
const style::SideBarButton &st)
|
||||
: RippleButton(parent, st.ripple)
|
||||
, _controller(controller)
|
||||
, _st(st)
|
||||
, _api(&controller->session().mtp())
|
||||
, _label(st.minTextWidth) {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
|
||||
setClickedCallback([=] { openLink(); });
|
||||
|
||||
events(
|
||||
) | rpl::filter([](not_null<QEvent*> e) {
|
||||
return (e->type() == QEvent::ContextMenu);
|
||||
}) | rpl::on_next([=](not_null<QEvent*> e) {
|
||||
showMenu();
|
||||
e->accept();
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
FolderFavoriteButton::~FolderFavoriteButton() = default;
|
||||
|
||||
rpl::producer<bool> FolderFavoriteButton::shownValue() const {
|
||||
return _shown.value();
|
||||
}
|
||||
|
||||
bool FolderFavoriteButton::shown() const {
|
||||
return _shown.current();
|
||||
}
|
||||
|
||||
int FolderFavoriteButton::resizeGetHeight(int newWidth) {
|
||||
const auto result = _st.minHeight;
|
||||
const auto text = std::min(
|
||||
_label.countHeight(newWidth - _st.textSkip * 2),
|
||||
_st.style.font->height * kMaxLabelLines);
|
||||
const auto add = text - _st.style.font->height;
|
||||
return result + std::max(add, 0);
|
||||
}
|
||||
|
||||
void FolderFavoriteButton::paintEvent(QPaintEvent *e) {
|
||||
auto p = Painter(this);
|
||||
p.fillRect(e->rect(), _st.textBg);
|
||||
|
||||
RippleButton::paintRipple(p, 0, 0);
|
||||
|
||||
if (_thumbnail) {
|
||||
const auto size = st::foldersFavorite.width();
|
||||
const auto x = (width() - size) / 2;
|
||||
p.drawImage(x, _st.iconPosition.y(), _thumbnail->image(size));
|
||||
}
|
||||
|
||||
p.setPen(_st.textFg);
|
||||
_label.draw(p, {
|
||||
.position = { _st.textSkip, _st.textTop },
|
||||
.availableWidth = width() - 2 * _st.textSkip,
|
||||
.align = style::al_top,
|
||||
.elisionLines = kMaxLabelLines,
|
||||
});
|
||||
}
|
||||
|
||||
void FolderFavoriteButton::setLink(const QString &link) {
|
||||
_resolveLifetime.destroy();
|
||||
_api.request(base::take(_requestId)).cancel();
|
||||
|
||||
_link = link.trimmed();
|
||||
if (_link.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto parsed = ParseFavoriteLink(_link);
|
||||
switch (parsed.category) {
|
||||
case Category::Peer:
|
||||
resolvePeer(parsed.value, parsed.phone);
|
||||
break;
|
||||
case Category::Invite:
|
||||
resolveInvite(parsed.value);
|
||||
break;
|
||||
case Category::StickerSet:
|
||||
case Category::EmojiSet:
|
||||
resolveStickerSet(parsed.value);
|
||||
break;
|
||||
case Category::ChatFolder:
|
||||
apply({
|
||||
.thumbnail = Ui::MakeIconThumbnail(st::foldersCustom),
|
||||
.label = tr::lng_filters_title(tr::now),
|
||||
});
|
||||
break;
|
||||
case Category::Settings: {
|
||||
const auto content = SettingsContent(parsed.value);
|
||||
apply({
|
||||
.thumbnail = Ui::MakeIconThumbnail(*content.icon),
|
||||
.label = content.label,
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
showGeneric();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FolderFavoriteButton::resolvePeer(const QString &value, bool phone) {
|
||||
const auto session = &_controller->session();
|
||||
if (const auto cached = phone
|
||||
? session->data().userByPhone(value)
|
||||
: session->data().peerByUsername(value)) {
|
||||
applyPeer(cached);
|
||||
return;
|
||||
}
|
||||
const auto done = [=](const MTPcontacts_ResolvedPeer &result) {
|
||||
_requestId = 0;
|
||||
result.match([&](const MTPDcontacts_resolvedPeer &data) {
|
||||
session->data().processUsers(data.vusers());
|
||||
session->data().processChats(data.vchats());
|
||||
if (const auto peerId = peerFromMTP(data.vpeer())) {
|
||||
applyPeer(session->data().peer(peerId));
|
||||
} else {
|
||||
showGeneric();
|
||||
}
|
||||
});
|
||||
};
|
||||
const auto fail = [=](const MTP::Error &) {
|
||||
_requestId = 0;
|
||||
showGeneric();
|
||||
};
|
||||
if (phone) {
|
||||
_requestId = _api.request(MTPcontacts_ResolvePhone(
|
||||
MTP_string(value)
|
||||
)).done(done).fail(fail).send();
|
||||
} else {
|
||||
_requestId = _api.request(MTPcontacts_ResolveUsername(
|
||||
MTP_flags(0),
|
||||
MTP_string(value),
|
||||
MTP_string(QString())
|
||||
)).done(done).fail(fail).send();
|
||||
}
|
||||
}
|
||||
|
||||
void FolderFavoriteButton::resolveInvite(const QString &hash) {
|
||||
const auto session = &_controller->session();
|
||||
const auto done = [=](const MTPChatInvite &result) {
|
||||
_requestId = 0;
|
||||
result.match([&](const MTPDchatInvite &data) {
|
||||
const auto photo = session->data().processPhoto(data.vphoto());
|
||||
auto thumbnail = std::shared_ptr<Ui::DynamicImage>();
|
||||
if (!photo->isNull()) {
|
||||
thumbnail = Ui::MakePhotoThumbnail(photo, FullMsgId());
|
||||
} else {
|
||||
const auto icon = data.is_broadcast()
|
||||
? &st::foldersChannels
|
||||
: &st::foldersGroups;
|
||||
thumbnail = Ui::MakeIconThumbnail(*icon);
|
||||
}
|
||||
apply({
|
||||
.thumbnail = std::move(thumbnail),
|
||||
.label = qs(data.vtitle()),
|
||||
});
|
||||
}, [&](const MTPDchatInviteAlready &data) {
|
||||
applyPeer(session->data().processChat(data.vchat()));
|
||||
}, [&](const MTPDchatInvitePeek &data) {
|
||||
applyPeer(session->data().processChat(data.vchat()));
|
||||
});
|
||||
};
|
||||
const auto fail = [=](const MTP::Error &) {
|
||||
_requestId = 0;
|
||||
showGeneric();
|
||||
};
|
||||
_requestId = _api.request(MTPmessages_CheckChatInvite(
|
||||
MTP_string(hash)
|
||||
)).done(done).fail(fail).send();
|
||||
}
|
||||
|
||||
void FolderFavoriteButton::resolveStickerSet(const QString &shortName) {
|
||||
const auto session = &_controller->session();
|
||||
_requestId = _api.request(MTPmessages_GetStickerSet(
|
||||
Data::InputStickerSet(StickerSetIdentifier{ .shortName = shortName }),
|
||||
MTP_int(0)
|
||||
)).done([=](const MTPmessages_StickerSet &result) {
|
||||
_requestId = 0;
|
||||
result.match([&](const MTPDmessages_stickerSet &data) {
|
||||
const auto set = session->data().stickers().feedSetFull(data);
|
||||
const auto thumb = set->lookupThumbnailDocument();
|
||||
const auto document = thumb
|
||||
? thumb
|
||||
: set->stickers.isEmpty()
|
||||
? nullptr
|
||||
: set->stickers.front();
|
||||
if (!document) {
|
||||
showGeneric();
|
||||
return;
|
||||
}
|
||||
apply({
|
||||
.thumbnail = Ui::MakeDocumentThumbnail(
|
||||
document,
|
||||
Data::FileOrigin(Data::FileOriginStickerSet(
|
||||
set->id,
|
||||
set->accessHash))),
|
||||
.label = set->title,
|
||||
});
|
||||
}, [&](const MTPDmessages_stickerSetNotModified &) {
|
||||
showGeneric();
|
||||
});
|
||||
}).fail([=](const MTP::Error &) {
|
||||
_requestId = 0;
|
||||
showGeneric();
|
||||
}).send();
|
||||
}
|
||||
|
||||
void FolderFavoriteButton::applyPeer(not_null<PeerData*> peer) {
|
||||
peer->loadUserpic();
|
||||
apply({
|
||||
.thumbnail = Ui::MakeUserpicThumbnail(peer),
|
||||
.label = peer->name(),
|
||||
});
|
||||
peer->session().changes().peerUpdates(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::Name
|
||||
) | rpl::on_next([=] {
|
||||
_label.setText(_st.style, peer->name());
|
||||
resizeToWidth(width());
|
||||
update();
|
||||
}, _resolveLifetime);
|
||||
}
|
||||
|
||||
void FolderFavoriteButton::apply(Presentation presentation) {
|
||||
_thumbnail = std::move(presentation.thumbnail);
|
||||
_label.setText(_st.style, presentation.label);
|
||||
if (_thumbnail) {
|
||||
_thumbnail->subscribeToUpdates([=] { update(); });
|
||||
}
|
||||
_shown = true;
|
||||
resizeToWidth(width());
|
||||
update();
|
||||
}
|
||||
|
||||
void FolderFavoriteButton::showGeneric() {
|
||||
apply({
|
||||
.thumbnail = Ui::MakeIconThumbnail(st::foldersFavorite),
|
||||
.label = tr::lng_info_link_label(tr::now),
|
||||
});
|
||||
}
|
||||
|
||||
void FolderFavoriteButton::openLink() {
|
||||
if (_link.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
UrlClickHandler::Open(
|
||||
Core::TryConvertUrlToLocal(_link),
|
||||
QVariant::fromValue(ClickHandlerContext{
|
||||
.sessionWindow = base::make_weak(_controller.get()),
|
||||
}));
|
||||
}
|
||||
|
||||
void FolderFavoriteButton::showMenu() {
|
||||
_menu = base::make_unique_q<Ui::PopupMenu>(this, st::popupMenuWithIcons);
|
||||
const auto addAction = Ui::Menu::CreateAddActionCallback(_menu);
|
||||
addAction(
|
||||
tr::lng_menu_formatting_link_edit(tr::now),
|
||||
[=] { editLink(); },
|
||||
&st::menuIconEdit);
|
||||
addAction({
|
||||
.text = tr::lng_filters_context_remove(tr::now),
|
||||
.handler = [=] { FavoriteLink.set(QString()); },
|
||||
.icon = &st::menuIconDeleteAttention,
|
||||
.isAttention = true,
|
||||
});
|
||||
_menu->popup(QCursor::pos());
|
||||
}
|
||||
|
||||
void FolderFavoriteButton::editLink() {
|
||||
_controller->show(Box(EditFolderFavoriteLinkBox));
|
||||
}
|
||||
|
||||
} // namespace Window
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
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 "base/unique_qptr.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "ui/text/text.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
|
||||
class PeerData;
|
||||
|
||||
namespace style {
|
||||
struct SideBarButton;
|
||||
} // namespace style
|
||||
|
||||
namespace Ui {
|
||||
class DynamicImage;
|
||||
class GenericBox;
|
||||
class PopupMenu;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window {
|
||||
|
||||
class SessionController;
|
||||
|
||||
extern const char kOptionFolderFavoriteLink[];
|
||||
|
||||
[[nodiscard]] bool ValidFolderFavoriteLink(const QString &link);
|
||||
|
||||
void EditFolderFavoriteLinkBox(not_null<Ui::GenericBox*> box);
|
||||
|
||||
class FolderFavoriteButton final : public Ui::RippleButton {
|
||||
public:
|
||||
FolderFavoriteButton(
|
||||
QWidget *parent,
|
||||
not_null<SessionController*> controller,
|
||||
const style::SideBarButton &st);
|
||||
~FolderFavoriteButton();
|
||||
|
||||
[[nodiscard]] rpl::producer<bool> shownValue() const;
|
||||
[[nodiscard]] bool shown() const;
|
||||
|
||||
void setLink(const QString &link);
|
||||
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
private:
|
||||
struct Presentation {
|
||||
std::shared_ptr<Ui::DynamicImage> thumbnail;
|
||||
QString label;
|
||||
};
|
||||
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
void resolvePeer(const QString &usernameOrPhone, bool phone);
|
||||
void resolveInvite(const QString &hash);
|
||||
void resolveStickerSet(const QString &shortName);
|
||||
void applyPeer(not_null<PeerData*> peer);
|
||||
void apply(Presentation presentation);
|
||||
void showGeneric();
|
||||
|
||||
void openLink();
|
||||
void showMenu();
|
||||
void editLink();
|
||||
|
||||
const not_null<SessionController*> _controller;
|
||||
const style::SideBarButton &_st;
|
||||
MTP::Sender _api;
|
||||
|
||||
QString _link;
|
||||
std::shared_ptr<Ui::DynamicImage> _thumbnail;
|
||||
Ui::Text::String _label;
|
||||
rpl::variable<bool> _shown = false;
|
||||
|
||||
mtpRequestId _requestId = 0;
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
rpl::lifetime _resolveLifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Window
|
||||
@@ -12,8 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_main_menu.h"
|
||||
#include "window/window_peer_menu.h"
|
||||
#include "window/window_filters_favorite.h"
|
||||
#include "main/main_session.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "base/options.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
@@ -25,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/filter_icons.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/wrap/vertical_layout_reorder.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
@@ -298,7 +301,13 @@ void FiltersMenu::setListTabStop(not_null<Ui::SideBarButton*> stop) {
|
||||
stop->setFocusPolicy(Qt::TabFocus);
|
||||
_tabStop = stop.get();
|
||||
QWidget::setTabOrder(&_menu, stop.get());
|
||||
QWidget::setTabOrder(stop.get(), _setup.get());
|
||||
const auto favorite = _favorite ? _favorite->entity() : nullptr;
|
||||
if (favorite) {
|
||||
QWidget::setTabOrder(stop.get(), favorite);
|
||||
QWidget::setTabOrder(favorite, _setup.get());
|
||||
} else {
|
||||
QWidget::setTabOrder(stop.get(), _setup.get());
|
||||
}
|
||||
}
|
||||
|
||||
bool FiltersMenu::listFocused() const {
|
||||
@@ -421,6 +430,69 @@ void FiltersMenu::setupList() {
|
||||
}
|
||||
}
|
||||
}, _outer.lifetime());
|
||||
|
||||
base::options::lookup<QString>(kOptionFolderFavoriteLink).changes(
|
||||
) | rpl::on_next([=] {
|
||||
updateFavorite();
|
||||
}, _outer.lifetime());
|
||||
updateFavorite();
|
||||
}
|
||||
|
||||
void FiltersMenu::updateFavorite() {
|
||||
const auto link = base::options::lookup<QString>(
|
||||
kOptionFolderFavoriteLink).value().trimmed();
|
||||
if (link.isEmpty()) {
|
||||
if (_favorite && _favorite->toggled()) {
|
||||
// Animate out; the finished callback drops it afterwards.
|
||||
_favorite->toggle(false, anim::type::normal);
|
||||
} else if (_favorite) {
|
||||
// Hidden already (e.g. still resolving), so nothing to animate.
|
||||
destroyFavorite();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!_favorite) {
|
||||
createFavorite();
|
||||
}
|
||||
const auto button = _favorite->entity();
|
||||
button->setLink(link);
|
||||
if (button->shown() && !_favorite->toggled()) {
|
||||
_favorite->toggle(true, anim::type::normal);
|
||||
}
|
||||
}
|
||||
|
||||
void FiltersMenu::createFavorite() {
|
||||
_favorite = base::unique_qptr<Ui::SlideWrap<FolderFavoriteButton>>(
|
||||
_container->insert(
|
||||
_container->count() - 1,
|
||||
object_ptr<Ui::SlideWrap<FolderFavoriteButton>>(
|
||||
_container,
|
||||
object_ptr<FolderFavoriteButton>(
|
||||
_container,
|
||||
_session,
|
||||
st::windowFiltersButton))));
|
||||
_favorite->toggle(false, anim::type::instant);
|
||||
_favorite->setFinishedCallback([=] {
|
||||
if (_favorite && !_favorite->toggled()) {
|
||||
destroyFavorite();
|
||||
}
|
||||
});
|
||||
_favorite->entity()->shownValue(
|
||||
) | rpl::on_next([=](bool shown) {
|
||||
if (_favorite && shown) {
|
||||
_favorite->toggle(true, anim::type::normal);
|
||||
}
|
||||
}, _favorite->lifetime());
|
||||
}
|
||||
|
||||
void FiltersMenu::destroyFavorite() {
|
||||
Ui::PostponeCall(&_outer, [=] {
|
||||
const auto empty = base::options::lookup<QString>(
|
||||
kOptionFolderFavoriteLink).value().trimmed().isEmpty();
|
||||
if (_favorite && !_favorite->toggled() && empty) {
|
||||
_favorite = nullptr;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool FiltersMenu::premium() const {
|
||||
|
||||
@@ -23,11 +23,14 @@ class VerticalLayout;
|
||||
class VerticalLayoutReorder;
|
||||
enum class FilterIcon : uchar;
|
||||
class PopupMenu;
|
||||
template <typename Widget>
|
||||
class SlideWrap;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window {
|
||||
|
||||
class SessionController;
|
||||
class FolderFavoriteButton;
|
||||
|
||||
class FiltersMenu final {
|
||||
public:
|
||||
@@ -40,6 +43,9 @@ private:
|
||||
void setup();
|
||||
void refresh();
|
||||
void setupList();
|
||||
void updateFavorite();
|
||||
void createFavorite();
|
||||
void destroyFavorite();
|
||||
void applyReorder(
|
||||
not_null<Ui::RpWidget*> widget,
|
||||
int oldPosition,
|
||||
@@ -73,6 +79,7 @@ private:
|
||||
Ui::VerticalLayout *_list = nullptr;
|
||||
std::unique_ptr<Ui::VerticalLayoutReorder> _reorder;
|
||||
base::unique_qptr<Ui::SideBarButton> _setup;
|
||||
base::unique_qptr<Ui::SlideWrap<FolderFavoriteButton>> _favorite;
|
||||
base::flat_map<FilterId, base::unique_qptr<Ui::SideBarButton>> _filters;
|
||||
base::weak_qptr<Ui::SideBarButton> _tabStop;
|
||||
rpl::variable<bool> _includeMuted;
|
||||
|
||||