[Option][GUI] Default folders and local folders
BIN
Telegram/Resources/icons/folders/folders_local_book.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_book@2x.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_book@3x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_book_active.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_brackets.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_brackets@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_brackets@3x.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_candle.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_candle@2x.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_candle@3x.png
Normal file
|
After Width: | Height: | Size: 3 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_candle_active.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_city.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_city@2x.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_city@3x.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_city_active.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_desktop.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_desktop@2x.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_desktop@3x.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_earth.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_earth@2x.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_earth@3x.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_music.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_music@2x.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_music@3x.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_music_active.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_news.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_news@2x.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_news@3x.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_news_active.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_phone.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_phone@2x.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_phone@3x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_phone_active.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_smile.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_smile@2x.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_smile@3x.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_smile_active.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 3 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_sun.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_sun@2x.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_sun@3x.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_sun_active.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_sun_active@2x.png
Normal file
|
After Width: | Height: | Size: 3 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_sun_active@3x.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_video.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_video@2x.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_video@3x.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Telegram/Resources/icons/folders/folders_local_video_active.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_admin.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_admin@2x.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_admin@3x.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_filtered.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_filtered@2x.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_filtered@3x.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_not_admin.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_not_admin@2x.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_not_admin@3x.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_not_owner.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_not_owner@2x.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_not_owner@3x.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_owner.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_owner@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_owner@3x.png
Normal file
|
After Width: | Height: | Size: 3 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_recent.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_recent@2x.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
Telegram/Resources/icons/folders/folders_type_recent@3x.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
Telegram/Resources/icons/settings/cloud.png
Normal file
|
After Width: | Height: | Size: 698 B |
BIN
Telegram/Resources/icons/settings/cloud@2x.png
Normal file
|
After Width: | Height: | Size: 1,003 B |
BIN
Telegram/Resources/icons/settings/cloud@3x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -62,7 +62,10 @@
|
||||||
"other": "Recent stickers: show {count} stickers"
|
"other": "Recent stickers: show {count} stickers"
|
||||||
},
|
},
|
||||||
"ktg_settings_recent_stickers_limit_none": "Recent stickers: hide all",
|
"ktg_settings_recent_stickers_limit_none": "Recent stickers: hide all",
|
||||||
|
"ktg_filters_default": "Default folder",
|
||||||
"ktg_filters_context_edit_all": "Edit folders",
|
"ktg_filters_context_edit_all": "Edit folders",
|
||||||
|
"ktg_filters_context_make_default": "Make folder default",
|
||||||
|
"ktg_filters_context_reset_default": "Reset default folder",
|
||||||
"ktg_settings_filters": "Folders",
|
"ktg_settings_filters": "Folders",
|
||||||
"ktg_settings_filters_only_unmuted_counter": "Do not count muted chats",
|
"ktg_settings_filters_only_unmuted_counter": "Do not count muted chats",
|
||||||
"ktg_settings_filters_hide_edit": "Hide Edit button",
|
"ktg_settings_filters_hide_edit": "Hide Edit button",
|
||||||
|
|
@ -91,6 +94,22 @@
|
||||||
"ktg_supergroup_id_copied": "Supergroup ID copied to clipboard.",
|
"ktg_supergroup_id_copied": "Supergroup ID copied to clipboard.",
|
||||||
"ktg_channel_id_copied": "Channel ID copied to clipboard.",
|
"ktg_channel_id_copied": "Channel ID copied to clipboard.",
|
||||||
"ktg_settings_forward": "Forward",
|
"ktg_settings_forward": "Forward",
|
||||||
|
"ktg_filters_exclude_not_owned": "Not owned",
|
||||||
|
"ktg_filters_exclude_not_admin": "Not administrated",
|
||||||
|
"ktg_filters_exclude_owned": "Owned",
|
||||||
|
"ktg_filters_exclude_admin": "Administrated",
|
||||||
|
"ktg_filters_exclude_not_recent": "Not opened in this session",
|
||||||
|
"ktg_filters_exclude_filtered": "From other folders",
|
||||||
|
"ktg_filters_create_cloud": "Create cloud folder",
|
||||||
|
"ktg_filters_create_local": "Create local folder",
|
||||||
|
"ktg_filters_description": "Cloud folders are synced between all your Telegram apps, but local folders have more features to offer.",
|
||||||
|
"ktg_filters_new_cloud": "New cloud folder",
|
||||||
|
"ktg_filters_new_local": "New local folder",
|
||||||
|
"ktg_filters_edit_cloud": "Edit cloud folder",
|
||||||
|
"ktg_filters_edit_local": "Edit local folder",
|
||||||
|
"ktg_filters_local": "local folder",
|
||||||
|
"ktg_filters_cloud": "cloud folder",
|
||||||
|
"ktg_filters_cloud_limit": "Sorry, you can't create more cloud folders. You can create local folder instead.",
|
||||||
"ktg_filters_hide_folder": "Hide folder",
|
"ktg_filters_hide_folder": "Hide folder",
|
||||||
"ktg_filters_hide_button": "Hide button",
|
"ktg_filters_hide_button": "Hide button",
|
||||||
"ktg_filters_hide_all_chats_toast": "\"All Chats\" folder is hidden.\nYou can enable it back in Kotatogram Settings.",
|
"ktg_filters_hide_all_chats_toast": "\"All Chats\" folder is hidden.\nYou can enable it back in Kotatogram Settings.",
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "api/api_chat_filters.h"
|
#include "api/api_chat_filters.h"
|
||||||
|
|
||||||
|
#include "kotato/kotato_settings.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "boxes/peer_list_box.h"
|
#include "boxes/peer_list_box.h"
|
||||||
#include "boxes/premium_limits_box.h"
|
#include "boxes/premium_limits_box.h"
|
||||||
|
|
@ -671,11 +672,16 @@ void SaveNewFilterPinned(
|
||||||
const auto &order = session->data().pinnedChatsOrder(filterId);
|
const auto &order = session->data().pinnedChatsOrder(filterId);
|
||||||
auto &filters = session->data().chatsFilters();
|
auto &filters = session->data().chatsFilters();
|
||||||
const auto &filter = filters.applyUpdatedPinned(filterId, order);
|
const auto &filter = filters.applyUpdatedPinned(filterId, order);
|
||||||
session->api().request(MTPmessages_UpdateDialogFilter(
|
if (filter.isLocal()) {
|
||||||
MTP_flags(MTPmessages_UpdateDialogFilter::Flag::f_filter),
|
filters.saveLocal();
|
||||||
MTP_int(filterId),
|
Kotato::JsonSettings::Write();
|
||||||
filter.tl()
|
} else {
|
||||||
)).send();
|
session->api().request(MTPmessages_UpdateDialogFilter(
|
||||||
|
MTP_flags(MTPmessages_UpdateDialogFilter::Flag::f_filter),
|
||||||
|
MTP_int(filterId),
|
||||||
|
filter.tl()
|
||||||
|
)).send();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckFilterInvite(
|
void CheckFilterInvite(
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "boxes/filters/edit_filter_box.h"
|
#include "boxes/filters/edit_filter_box.h"
|
||||||
|
|
||||||
|
#include "kotato/kotato_lang.h"
|
||||||
|
#include "kotato/kotato_settings.h"
|
||||||
#include "boxes/filters/edit_filter_chats_list.h"
|
#include "boxes/filters/edit_filter_chats_list.h"
|
||||||
#include "boxes/filters/edit_filter_chats_preview.h"
|
#include "boxes/filters/edit_filter_chats_preview.h"
|
||||||
#include "boxes/filters/edit_filter_links.h"
|
#include "boxes/filters/edit_filter_links.h"
|
||||||
|
|
@ -15,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
|
#include "ui/widgets/checkbox.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/fields/input_field.h"
|
#include "ui/widgets/fields/input_field.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
|
|
@ -37,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
#include "main/main_account.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
|
@ -86,7 +90,9 @@ not_null<FilterChatsPreview*> SetupChatsPreview(
|
||||||
(rules.flags() & ~flag),
|
(rules.flags() & ~flag),
|
||||||
rules.always(),
|
rules.always(),
|
||||||
rules.pinned(),
|
rules.pinned(),
|
||||||
rules.never());
|
rules.never(),
|
||||||
|
rules.isDefault(),
|
||||||
|
rules.isLocal());
|
||||||
updateDefaultTitle(computed);
|
updateDefaultTitle(computed);
|
||||||
*data = std::move(computed);
|
*data = std::move(computed);
|
||||||
}, preview->lifetime());
|
}, preview->lifetime());
|
||||||
|
|
@ -107,7 +113,9 @@ not_null<FilterChatsPreview*> SetupChatsPreview(
|
||||||
rules.flags(),
|
rules.flags(),
|
||||||
std::move(always),
|
std::move(always),
|
||||||
std::move(pinned),
|
std::move(pinned),
|
||||||
std::move(never));
|
std::move(never),
|
||||||
|
rules.isDefault(),
|
||||||
|
rules.isLocal());
|
||||||
updateDefaultTitle(computed);
|
updateDefaultTitle(computed);
|
||||||
*data = std::move(computed);
|
*data = std::move(computed);
|
||||||
}, preview->lifetime());
|
}, preview->lifetime());
|
||||||
|
|
@ -140,7 +148,8 @@ void EditExceptions(
|
||||||
rules.flags() & options,
|
rules.flags() & options,
|
||||||
include ? rules.always() : rules.never(),
|
include ? rules.always() : rules.never(),
|
||||||
limit,
|
limit,
|
||||||
showLimitReached);
|
showLimitReached,
|
||||||
|
rules.isLocal());
|
||||||
const auto rawController = controller.get();
|
const auto rawController = controller.get();
|
||||||
auto initBox = [=](not_null<PeerListBox*> box) {
|
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||||
box->setCloseByOutsideClick(false);
|
box->setCloseByOutsideClick(false);
|
||||||
|
|
@ -174,7 +183,9 @@ void EditExceptions(
|
||||||
| rawController->chosenOptions()),
|
| rawController->chosenOptions()),
|
||||||
include ? std::move(changed) : std::move(removeFrom),
|
include ? std::move(changed) : std::move(removeFrom),
|
||||||
std::move(pinned),
|
std::move(pinned),
|
||||||
include ? std::move(removeFrom) : std::move(changed));
|
include ? std::move(removeFrom) : std::move(changed),
|
||||||
|
rules.isDefault(),
|
||||||
|
rules.isLocal());
|
||||||
updateDefaultTitle(computed);
|
updateDefaultTitle(computed);
|
||||||
*data = computed;
|
*data = computed;
|
||||||
refresh();
|
refresh();
|
||||||
|
|
@ -225,7 +236,8 @@ void CreateIconSelector(
|
||||||
}, toggle->lifetime());
|
}, toggle->lifetime());
|
||||||
|
|
||||||
const auto panel = toggle->lifetime().make_state<Ui::FilterIconPanel>(
|
const auto panel = toggle->lifetime().make_state<Ui::FilterIconPanel>(
|
||||||
outer);
|
outer,
|
||||||
|
rules.isLocal());
|
||||||
toggle->installEventFilter(panel);
|
toggle->installEventFilter(panel);
|
||||||
toggle->addClickHandler([=] {
|
toggle->addClickHandler([=] {
|
||||||
panel->toggleAnimated();
|
panel->toggleAnimated();
|
||||||
|
|
@ -243,7 +255,9 @@ void CreateIconSelector(
|
||||||
rules.flags(),
|
rules.flags(),
|
||||||
rules.always(),
|
rules.always(),
|
||||||
rules.pinned(),
|
rules.pinned(),
|
||||||
rules.never());
|
rules.never(),
|
||||||
|
rules.isDefault(),
|
||||||
|
rules.isLocal());
|
||||||
}, panel->lifetime());
|
}, panel->lifetime());
|
||||||
|
|
||||||
const auto updatePanelGeometry = [=] {
|
const auto updatePanelGeometry = [=] {
|
||||||
|
|
@ -375,10 +389,14 @@ void EditFilterBox(
|
||||||
}, box->lifetime());
|
}, box->lifetime());
|
||||||
|
|
||||||
box->setWidth(st::boxWideWidth);
|
box->setWidth(st::boxWideWidth);
|
||||||
box->setTitle(rpl::conditional(
|
const auto isLocal = filter.isLocal();
|
||||||
state->creating.value(),
|
box->setTitle(rpl::single(state->creating.current()
|
||||||
tr::lng_filters_new(),
|
? (isLocal
|
||||||
tr::lng_filters_edit()));
|
? ktr("ktg_filters_new_local")
|
||||||
|
: ktr("ktg_filters_new_cloud"))
|
||||||
|
: (isLocal
|
||||||
|
? ktr("ktg_filters_edit_local")
|
||||||
|
: ktr("ktg_filters_edit_cloud"))));
|
||||||
box->setCloseByOutsideClick(false);
|
box->setCloseByOutsideClick(false);
|
||||||
|
|
||||||
Data::AmPremiumValue(
|
Data::AmPremiumValue(
|
||||||
|
|
@ -395,10 +413,12 @@ void EditFilterBox(
|
||||||
tr::lng_filters_new_name(),
|
tr::lng_filters_new_name(),
|
||||||
filter.title()),
|
filter.title()),
|
||||||
st::markdownLinkFieldPadding);
|
st::markdownLinkFieldPadding);
|
||||||
name->setMaxLength(kMaxFilterTitleLength);
|
|
||||||
name->setInstantReplaces(Ui::InstantReplaces::Default());
|
name->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||||
name->setInstantReplacesEnabled(
|
name->setInstantReplacesEnabled(
|
||||||
Core::App().settings().replaceEmojiValue());
|
Core::App().settings().replaceEmojiValue());
|
||||||
|
if (!isLocal) {
|
||||||
|
name->setMaxLength(kMaxFilterTitleLength);
|
||||||
|
}
|
||||||
Ui::Emoji::SuggestionsController::Init(
|
Ui::Emoji::SuggestionsController::Init(
|
||||||
box->getDelegate()->outerContainer(),
|
box->getDelegate()->outerContainer(),
|
||||||
name,
|
name,
|
||||||
|
|
@ -422,7 +442,9 @@ void EditFilterBox(
|
||||||
if (nameEditing->custom) {
|
if (nameEditing->custom) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto title = TrimDefaultTitle(DefaultTitle(filter));
|
const auto title = isLocal
|
||||||
|
? DefaultTitle(filter)
|
||||||
|
: TrimDefaultTitle(DefaultTitle(filter));
|
||||||
if (nameEditing->field->getLastText() != title) {
|
if (nameEditing->field->getLastText() != title) {
|
||||||
nameEditing->settingDefault = true;
|
nameEditing->settingDefault = true;
|
||||||
nameEditing->field->setText(title);
|
nameEditing->field->setText(title);
|
||||||
|
|
@ -446,11 +468,32 @@ void EditFilterBox(
|
||||||
constexpr auto kExcludeTypes = Flag::NoMuted
|
constexpr auto kExcludeTypes = Flag::NoMuted
|
||||||
| Flag::NoArchived
|
| Flag::NoArchived
|
||||||
| Flag::NoRead;
|
| Flag::NoRead;
|
||||||
|
constexpr auto kExcludeTypesLocal = kExcludeTypes
|
||||||
|
| Flag::Owned
|
||||||
|
| Flag::Admin
|
||||||
|
| Flag::NotOwned
|
||||||
|
| Flag::NotAdmin
|
||||||
|
| Flag::Recent
|
||||||
|
| Flag::NoFilter;
|
||||||
|
|
||||||
box->setFocusCallback([=] {
|
box->setFocusCallback([=] {
|
||||||
name->setFocusFast();
|
name->setFocusFast();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const auto defaultFilterId = window->session().account().defaultFilterId();
|
||||||
|
const auto isCurrent = filter.id() == defaultFilterId;
|
||||||
|
const auto checkboxDefault = content->add(
|
||||||
|
object_ptr<Ui::Checkbox>(
|
||||||
|
box,
|
||||||
|
ktr("ktg_filters_default"),
|
||||||
|
(state->creating.current() ? false : isCurrent),
|
||||||
|
st::defaultBoxCheckbox),
|
||||||
|
style::margins(
|
||||||
|
st::boxPadding.left(),
|
||||||
|
st::boxPadding.bottom(),
|
||||||
|
st::boxPadding.right(),
|
||||||
|
st::boxPadding.bottom()));
|
||||||
|
|
||||||
Ui::AddSkip(content);
|
Ui::AddSkip(content);
|
||||||
Ui::AddDivider(content);
|
Ui::AddDivider(content);
|
||||||
Ui::AddSkip(content);
|
Ui::AddSkip(content);
|
||||||
|
|
@ -493,7 +536,7 @@ void EditFilterBox(
|
||||||
excludeInner,
|
excludeInner,
|
||||||
data,
|
data,
|
||||||
updateDefaultTitle,
|
updateDefaultTitle,
|
||||||
kExcludeTypes,
|
(isLocal ? kExcludeTypesLocal : kExcludeTypes),
|
||||||
&Data::ChatFilter::never);
|
&Data::ChatFilter::never);
|
||||||
|
|
||||||
Ui::AddSkip(excludeInner);
|
Ui::AddSkip(excludeInner);
|
||||||
|
|
@ -613,7 +656,7 @@ void EditFilterBox(
|
||||||
data->current().flags() & kTypes,
|
data->current().flags() & kTypes,
|
||||||
data->current().always());
|
data->current().always());
|
||||||
exclude->updateData(
|
exclude->updateData(
|
||||||
data->current().flags() & kExcludeTypes,
|
data->current().flags() & (isLocal ? kExcludeTypesLocal : kExcludeTypes),
|
||||||
data->current().never());
|
data->current().never());
|
||||||
};
|
};
|
||||||
includeAdd->setClickedCallback([=] {
|
includeAdd->setClickedCallback([=] {
|
||||||
|
|
@ -629,7 +672,7 @@ void EditFilterBox(
|
||||||
EditExceptions(
|
EditExceptions(
|
||||||
window,
|
window,
|
||||||
box,
|
box,
|
||||||
kExcludeTypes,
|
(isLocal ? kExcludeTypesLocal : kExcludeTypes),
|
||||||
data,
|
data,
|
||||||
updateDefaultTitle,
|
updateDefaultTitle,
|
||||||
refreshPreviews);
|
refreshPreviews);
|
||||||
|
|
@ -656,24 +699,42 @@ void EditExistingFilter(
|
||||||
Expects(id != 0);
|
Expects(id != 0);
|
||||||
|
|
||||||
const auto session = &window->session();
|
const auto session = &window->session();
|
||||||
const auto &list = session->data().chatsFilters().list();
|
const auto filters = &session->data().chatsFilters();
|
||||||
|
const auto &list = filters->list();
|
||||||
const auto i = ranges::find(list, id, &Data::ChatFilter::id);
|
const auto i = ranges::find(list, id, &Data::ChatFilter::id);
|
||||||
if (i == end(list)) {
|
if (i == end(list)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto doneCallback = [=](const Data::ChatFilter &result) {
|
const auto doneCallback = [=](const Data::ChatFilter &result) {
|
||||||
Expects(id == result.id());
|
Expects(id == result.id());
|
||||||
|
auto needSave = false;
|
||||||
|
|
||||||
const auto tl = result.tl();
|
if (result.isLocal()) {
|
||||||
session->data().chatsFilters().apply(MTP_updateDialogFilter(
|
filters->set(result);
|
||||||
MTP_flags(MTPDupdateDialogFilter::Flag::f_filter),
|
filters->saveLocal();
|
||||||
MTP_int(id),
|
needSave = true;
|
||||||
tl));
|
} else {
|
||||||
session->api().request(MTPmessages_UpdateDialogFilter(
|
const auto tl = result.tl();
|
||||||
MTP_flags(MTPmessages_UpdateDialogFilter::Flag::f_filter),
|
session->data().chatsFilters().apply(MTP_updateDialogFilter(
|
||||||
MTP_int(id),
|
MTP_flags(MTPDupdateDialogFilter::Flag::f_filter),
|
||||||
tl
|
MTP_int(id),
|
||||||
)).send();
|
tl));
|
||||||
|
session->api().request(MTPmessages_UpdateDialogFilter(
|
||||||
|
MTP_flags(MTPmessages_UpdateDialogFilter::Flag::f_filter),
|
||||||
|
MTP_int(id),
|
||||||
|
tl
|
||||||
|
)).send();
|
||||||
|
}
|
||||||
|
const auto defaultFilterId = session->account().defaultFilterId();
|
||||||
|
const auto isCurrentDefault = result.id() == defaultFilterId;
|
||||||
|
if ((isCurrentDefault && !result.isDefault())
|
||||||
|
|| (!isCurrentDefault && result.isDefault())) {
|
||||||
|
session->account().setDefaultFilterId(result.isDefault() ? result.id() : 0);
|
||||||
|
needSave = true;
|
||||||
|
}
|
||||||
|
if (needSave) {
|
||||||
|
Kotato::JsonSettings::Write();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const auto saveAnd = [=](
|
const auto saveAnd = [=](
|
||||||
const Data::ChatFilter &data,
|
const Data::ChatFilter &data,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "boxes/filters/edit_filter_chats_list.h"
|
#include "boxes/filters/edit_filter_chats_list.h"
|
||||||
|
|
||||||
|
#include "kotato/kotato_lang.h"
|
||||||
#include "data/data_premium_limits.h"
|
#include "data/data_premium_limits.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
|
|
@ -38,6 +39,12 @@ constexpr auto kAllTypes = {
|
||||||
Flag::NoMuted,
|
Flag::NoMuted,
|
||||||
Flag::NoRead,
|
Flag::NoRead,
|
||||||
Flag::NoArchived,
|
Flag::NoArchived,
|
||||||
|
Flag::Owned,
|
||||||
|
Flag::Admin,
|
||||||
|
Flag::NotOwned,
|
||||||
|
Flag::NotAdmin,
|
||||||
|
Flag::Recent,
|
||||||
|
Flag::NoFilter,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RowSelectionChange {
|
struct RowSelectionChange {
|
||||||
|
|
@ -232,6 +239,12 @@ auto TypeController::rowSelectionChanges() const
|
||||||
case Flag::NoMuted: return tr::lng_filters_type_no_muted(tr::now);
|
case Flag::NoMuted: return tr::lng_filters_type_no_muted(tr::now);
|
||||||
case Flag::NoArchived: return tr::lng_filters_type_no_archived(tr::now);
|
case Flag::NoArchived: return tr::lng_filters_type_no_archived(tr::now);
|
||||||
case Flag::NoRead: return tr::lng_filters_type_no_read(tr::now);
|
case Flag::NoRead: return tr::lng_filters_type_no_read(tr::now);
|
||||||
|
case Flag::Owned: return ktr("ktg_filters_exclude_not_owned");
|
||||||
|
case Flag::Admin: return ktr("ktg_filters_exclude_not_admin");
|
||||||
|
case Flag::NotOwned: return ktr("ktg_filters_exclude_owned");
|
||||||
|
case Flag::NotAdmin: return ktr("ktg_filters_exclude_admin");
|
||||||
|
case Flag::Recent: return ktr("ktg_filters_exclude_not_recent");
|
||||||
|
case Flag::NoFilter: return ktr("ktg_filters_exclude_filtered");
|
||||||
}
|
}
|
||||||
Unexpected("Flag in TypeName.");
|
Unexpected("Flag in TypeName.");
|
||||||
}
|
}
|
||||||
|
|
@ -255,6 +268,12 @@ void PaintFilterChatsTypeIcon(
|
||||||
case Flag::NoMuted: return st::historyPeer6UserpicBg;
|
case Flag::NoMuted: return st::historyPeer6UserpicBg;
|
||||||
case Flag::NoArchived: return st::historyPeer4UserpicBg;
|
case Flag::NoArchived: return st::historyPeer4UserpicBg;
|
||||||
case Flag::NoRead: return st::historyPeer7UserpicBg;
|
case Flag::NoRead: return st::historyPeer7UserpicBg;
|
||||||
|
case Flag::Owned: return st::historyPeer2UserpicBg;
|
||||||
|
case Flag::Admin: return st::historyPeer3UserpicBg;
|
||||||
|
case Flag::NotOwned: return st::historyPeer2UserpicBg;
|
||||||
|
case Flag::NotAdmin: return st::historyPeer3UserpicBg;
|
||||||
|
case Flag::Recent: return st::historyPeer6UserpicBg;
|
||||||
|
case Flag::NoFilter: return st::historyPeer7UserpicBg;
|
||||||
}
|
}
|
||||||
Unexpected("Flag in color paintFlagIcon.");
|
Unexpected("Flag in color paintFlagIcon.");
|
||||||
}();
|
}();
|
||||||
|
|
@ -270,6 +289,12 @@ void PaintFilterChatsTypeIcon(
|
||||||
case Flag::NoMuted: return st::historyPeer6UserpicBg2;
|
case Flag::NoMuted: return st::historyPeer6UserpicBg2;
|
||||||
case Flag::NoArchived: return st::historyPeer4UserpicBg2;
|
case Flag::NoArchived: return st::historyPeer4UserpicBg2;
|
||||||
case Flag::NoRead: return st::historyPeer7UserpicBg2;
|
case Flag::NoRead: return st::historyPeer7UserpicBg2;
|
||||||
|
case Flag::Owned: return st::historyPeer2UserpicBg2;
|
||||||
|
case Flag::Admin: return st::historyPeer3UserpicBg2;
|
||||||
|
case Flag::NotOwned: return st::historyPeer2UserpicBg2;
|
||||||
|
case Flag::NotAdmin: return st::historyPeer3UserpicBg2;
|
||||||
|
case Flag::Recent: return st::historyPeer6UserpicBg2;
|
||||||
|
case Flag::NoFilter: return st::historyPeer7UserpicBg2;
|
||||||
}
|
}
|
||||||
Unexpected("Flag in color paintFlagIcon.");
|
Unexpected("Flag in color paintFlagIcon.");
|
||||||
}();
|
}();
|
||||||
|
|
@ -285,6 +310,12 @@ void PaintFilterChatsTypeIcon(
|
||||||
case Flag::NoMuted: return st::windowFilterTypeNoMuted;
|
case Flag::NoMuted: return st::windowFilterTypeNoMuted;
|
||||||
case Flag::NoArchived: return st::windowFilterTypeNoArchived;
|
case Flag::NoArchived: return st::windowFilterTypeNoArchived;
|
||||||
case Flag::NoRead: return st::windowFilterTypeNoRead;
|
case Flag::NoRead: return st::windowFilterTypeNoRead;
|
||||||
|
case Flag::Owned: return st::windowFilterTypeOwned;
|
||||||
|
case Flag::Admin: return st::windowFilterTypeAdmin;
|
||||||
|
case Flag::NotOwned: return st::windowFilterTypeNotOwned;
|
||||||
|
case Flag::NotAdmin: return st::windowFilterTypeNotAdmin;
|
||||||
|
case Flag::Recent: return st::windowFilterTypeRecent;
|
||||||
|
case Flag::NoFilter: return st::windowFilterTypeNoFilter;
|
||||||
}
|
}
|
||||||
Unexpected("Flag in icon paintFlagIcon.");
|
Unexpected("Flag in icon paintFlagIcon.");
|
||||||
}();
|
}();
|
||||||
|
|
@ -334,7 +365,8 @@ EditFilterChatsListController::EditFilterChatsListController(
|
||||||
Flags selected,
|
Flags selected,
|
||||||
const base::flat_set<not_null<History*>> &peers,
|
const base::flat_set<not_null<History*>> &peers,
|
||||||
int limit,
|
int limit,
|
||||||
Fn<void()> showLimitReached)
|
Fn<void()> showLimitReached,
|
||||||
|
bool isLocal)
|
||||||
: ChatsListBoxController(session)
|
: ChatsListBoxController(session)
|
||||||
, _session(session)
|
, _session(session)
|
||||||
, _showLimitReached(std::move(showLimitReached))
|
, _showLimitReached(std::move(showLimitReached))
|
||||||
|
|
@ -343,7 +375,8 @@ EditFilterChatsListController::EditFilterChatsListController(
|
||||||
, _options(options & ~Flag::Chatlist)
|
, _options(options & ~Flag::Chatlist)
|
||||||
, _selected(selected)
|
, _selected(selected)
|
||||||
, _limit(limit)
|
, _limit(limit)
|
||||||
, _chatlist(options & Flag::Chatlist) {
|
, _chatlist(options & Flag::Chatlist)
|
||||||
|
, _isLocal(isLocal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Main::Session &EditFilterChatsListController::session() const {
|
Main::Session &EditFilterChatsListController::session() const {
|
||||||
|
|
@ -368,7 +401,7 @@ int EditFilterChatsListController::selectedTypesCount() const {
|
||||||
void EditFilterChatsListController::rowClicked(not_null<PeerListRow*> row) {
|
void EditFilterChatsListController::rowClicked(not_null<PeerListRow*> row) {
|
||||||
const auto count = delegate()->peerListSelectedRowsCount()
|
const auto count = delegate()->peerListSelectedRowsCount()
|
||||||
- selectedTypesCount();
|
- selectedTypesCount();
|
||||||
if (count < _limit || row->checked()) {
|
if (count < _limit || row->checked() || _isLocal) {
|
||||||
delegate()->peerListSetRowChecked(row, !row->checked());
|
delegate()->peerListSetRowChecked(row, !row->checked());
|
||||||
updateTitle();
|
updateTitle();
|
||||||
} else if (const auto copy = _showLimitReached) {
|
} else if (const auto copy = _showLimitReached) {
|
||||||
|
|
@ -492,6 +525,8 @@ auto EditFilterChatsListController::createRow(not_null<History*> history)
|
||||||
void EditFilterChatsListController::updateTitle() {
|
void EditFilterChatsListController::updateTitle() {
|
||||||
const auto count = delegate()->peerListSelectedRowsCount()
|
const auto count = delegate()->peerListSelectedRowsCount()
|
||||||
- selectedTypesCount();
|
- selectedTypesCount();
|
||||||
const auto additional = u"%1 / %2"_q.arg(count).arg(_limit);
|
const auto additional = _isLocal
|
||||||
|
? tr::lng_filters_chats_count(tr::now, lt_count_short, count)
|
||||||
|
: u"%1 / %2"_q.arg(count).arg(_limit);
|
||||||
delegate()->peerListSetAdditionalTitle(rpl::single(additional));
|
delegate()->peerListSetAdditionalTitle(rpl::single(additional));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,8 @@ public:
|
||||||
Flags selected,
|
Flags selected,
|
||||||
const base::flat_set<not_null<History*>> &peers,
|
const base::flat_set<not_null<History*>> &peers,
|
||||||
int limit,
|
int limit,
|
||||||
Fn<void()> showLimitReached);
|
Fn<void()> showLimitReached,
|
||||||
|
bool isLocal = false);
|
||||||
|
|
||||||
[[nodiscard]] Main::Session &session() const override;
|
[[nodiscard]] Main::Session &session() const override;
|
||||||
[[nodiscard]] Flags chosenOptions() const {
|
[[nodiscard]] Flags chosenOptions() const {
|
||||||
|
|
@ -79,6 +80,7 @@ private:
|
||||||
Flags _selected;
|
Flags _selected;
|
||||||
int _limit = 0;
|
int _limit = 0;
|
||||||
bool _chatlist = false;
|
bool _chatlist = false;
|
||||||
|
bool _isLocal;
|
||||||
|
|
||||||
Fn<void(PeerListRowId)> _deselectOption;
|
Fn<void(PeerListRowId)> _deselectOption;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "data/data_chat_filters.h"
|
#include "data/data_chat_filters.h"
|
||||||
|
|
||||||
|
#include "kotato/kotato_settings.h"
|
||||||
|
#include "boxes/premium_limits_box.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
|
@ -15,13 +17,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_folder.h"
|
#include "data/data_folder.h"
|
||||||
#include "data/data_histories.h"
|
#include "data/data_histories.h"
|
||||||
|
#include "data/data_premium_limits.h"
|
||||||
#include "dialogs/dialogs_main_list.h"
|
#include "dialogs/dialogs_main_list.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_unread_things.h"
|
#include "history/history_unread_things.h"
|
||||||
#include "ui/ui_utility.h"
|
#include "ui/ui_utility.h"
|
||||||
#include "ui/chat/more_chats_bar.h"
|
#include "ui/chat/more_chats_bar.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
#include "main/main_account.h"
|
||||||
#include "main/main_app_config.h"
|
#include "main/main_app_config.h"
|
||||||
|
#include "mainwidget.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
@ -37,8 +42,199 @@ constexpr auto kLoadExceptionsPerRequest = 100;
|
||||||
* crl::time(1000);
|
* crl::time(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::map<ChatFilter::Flag, QString> LocalFolderSettingsFlags {
|
||||||
|
{ ChatFilter::Flag::Contacts, qsl("include_contacts") },
|
||||||
|
{ ChatFilter::Flag::NonContacts, qsl("include_non_contacts") },
|
||||||
|
{ ChatFilter::Flag::Groups, qsl("include_groups") },
|
||||||
|
{ ChatFilter::Flag::Channels, qsl("include_channels") },
|
||||||
|
{ ChatFilter::Flag::Bots, qsl("include_bots") },
|
||||||
|
{ ChatFilter::Flag::NoMuted, qsl("exclude_muted") },
|
||||||
|
{ ChatFilter::Flag::NoRead, qsl("exclude_read") },
|
||||||
|
{ ChatFilter::Flag::NoArchived, qsl("exclude_archived") },
|
||||||
|
{ ChatFilter::Flag::Owned, qsl("exclude_not_owned") },
|
||||||
|
{ ChatFilter::Flag::Admin, qsl("exclude_not_admin") },
|
||||||
|
{ ChatFilter::Flag::NotOwned, qsl("exclude_owned") },
|
||||||
|
{ ChatFilter::Flag::NotAdmin, qsl("exclude_admin") },
|
||||||
|
{ ChatFilter::Flag::Recent, qsl("exclude_non_recent") },
|
||||||
|
{ ChatFilter::Flag::NoFilter, qsl("exclude_filtered") },
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ReadOption(QJsonObject obj, QString key, std::function<void(QJsonValue)> callback) {
|
||||||
|
const auto it = obj.constFind(key);
|
||||||
|
if (it == obj.constEnd()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
callback(*it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadStringOption(QJsonObject obj, QString key, std::function<void(QString)> callback) {
|
||||||
|
auto readResult = false;
|
||||||
|
auto readValueResult = ReadOption(obj, key, [&](QJsonValue v) {
|
||||||
|
if (v.isString()) {
|
||||||
|
callback(v.toString());
|
||||||
|
readResult = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return (readValueResult && readResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadIntOption(QJsonObject obj, QString key, std::function<void(int)> callback) {
|
||||||
|
auto readResult = false;
|
||||||
|
auto readValueResult = ReadOption(obj, key, [&](QJsonValue v) {
|
||||||
|
if (v.isDouble()) {
|
||||||
|
callback(v.toInt());
|
||||||
|
readResult = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return (readValueResult && readResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadArrayOption(QJsonObject obj, QString key, std::function<void(QJsonArray)> callback) {
|
||||||
|
auto readResult = false;
|
||||||
|
auto readValueResult = ReadOption(obj, key, [&](QJsonValue v) {
|
||||||
|
if (v.isArray()) {
|
||||||
|
callback(v.toArray());
|
||||||
|
readResult = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return (readValueResult && readResult);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
QJsonObject LocalFolder::toJson() {
|
||||||
|
auto folderObject = QJsonObject();
|
||||||
|
|
||||||
|
folderObject.insert(qsl("id"), id);
|
||||||
|
folderObject.insert(qsl("order"), cloudOrder);
|
||||||
|
folderObject.insert(qsl("name"), name);
|
||||||
|
folderObject.insert(qsl("emoticon"), emoticon);
|
||||||
|
|
||||||
|
for (const auto &[flag, option] : LocalFolderSettingsFlags) {
|
||||||
|
if (flags & flag) {
|
||||||
|
folderObject.insert(option, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto peerToStr = [](uint64 peer) {
|
||||||
|
auto peerId = PeerId(peer);
|
||||||
|
return (peerIsChannel(peerId))
|
||||||
|
? qsl("channel")
|
||||||
|
: (peerIsChat(peerId))
|
||||||
|
? qsl("chat")
|
||||||
|
: qsl("user");
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto peerToLocalBare = [](uint64 peer) {
|
||||||
|
auto peerId = PeerId(peer);
|
||||||
|
return QString::number((peerIsChannel(peerId))
|
||||||
|
? peerToChannel(peerId).bare
|
||||||
|
: (peerIsChat(peerId))
|
||||||
|
? peerToChat(peerId).bare
|
||||||
|
: peerToUser(peerId).bare);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto fillChatsArray = [peerToStr, peerToLocalBare] (const std::vector<uint64> &chats) -> QJsonArray {
|
||||||
|
auto result = QJsonArray();
|
||||||
|
for (auto peer : chats) {
|
||||||
|
auto peerObj = QJsonObject();
|
||||||
|
peerObj.insert(qsl("type"), peerToStr(peer));
|
||||||
|
peerObj.insert(qsl("id"), peerToLocalBare(peer));
|
||||||
|
result << peerObj;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
folderObject.insert(qsl("never"), fillChatsArray(never));
|
||||||
|
folderObject.insert(qsl("pinned"), fillChatsArray(pinned));
|
||||||
|
folderObject.insert(qsl("always"), fillChatsArray(always));
|
||||||
|
|
||||||
|
return folderObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalFolder MakeLocalFolder(const QJsonObject &obj) {
|
||||||
|
auto result = LocalFolder();
|
||||||
|
|
||||||
|
ReadIntOption(obj, "id", [&](auto v) {
|
||||||
|
result.id = v;
|
||||||
|
});
|
||||||
|
|
||||||
|
ReadIntOption(obj, "order", [&](auto v) {
|
||||||
|
result.cloudOrder = v;
|
||||||
|
});
|
||||||
|
|
||||||
|
ReadStringOption(obj, "name", [&](auto v) {
|
||||||
|
result.name = v;
|
||||||
|
});
|
||||||
|
|
||||||
|
ReadStringOption(obj, "emoticon", [&](auto v) {
|
||||||
|
result.emoticon = v;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const auto &[flag, option] : LocalFolderSettingsFlags) {
|
||||||
|
const auto it = obj.constFind(option);
|
||||||
|
if (it != obj.constEnd()) {
|
||||||
|
const auto v = *it;
|
||||||
|
if (v.isBool() && v.toBool()) {
|
||||||
|
result.flags |= flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto readChatsArray = [obj] (const QString &key, std::vector<uint64> &chats) {
|
||||||
|
ReadArrayOption(obj, key, [&](auto a) {
|
||||||
|
for (auto i = a.constBegin(), e = a.constEnd(); i != e; ++i) {
|
||||||
|
if (!(*i).isObject()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto peer = (*i).toObject();
|
||||||
|
BareId peerId = 0;
|
||||||
|
|
||||||
|
auto isPeerIdRead = ReadIntOption(peer, "id", [&](auto v) {
|
||||||
|
peerId = v;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isPeerIdRead) {
|
||||||
|
isPeerIdRead = ReadStringOption(peer, "id", [&](auto v) {
|
||||||
|
peerId = static_cast<BareId>(v.toLongLong());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peerId == 0 || !isPeerIdRead) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto isPeerTypeRead = ReadStringOption(peer, "type", [&](auto v) {
|
||||||
|
peerId = (QString::compare(v.toLower(), "channel") == 0)
|
||||||
|
? peerFromChannel(ChannelId(peerId)).value
|
||||||
|
: (QString::compare(v.toLower(), "chat") == 0)
|
||||||
|
? peerFromChat(ChatId(peerId)).value
|
||||||
|
: peerFromUser(UserId(peerId)).value;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isPeerTypeRead) {
|
||||||
|
peerId = peerFromUser(UserId(peerId)).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
chats.push_back(peerId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
readChatsArray(qsl("never"), result.never);
|
||||||
|
readChatsArray(qsl("pinned"), result.pinned);
|
||||||
|
readChatsArray(qsl("always"), result.always);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatFilter::ChatFilter(FilterId id, bool isLocal)
|
||||||
|
: _id(id)
|
||||||
|
, _isLocal(isLocal) {
|
||||||
|
}
|
||||||
|
|
||||||
ChatFilter::ChatFilter(
|
ChatFilter::ChatFilter(
|
||||||
FilterId id,
|
FilterId id,
|
||||||
const QString &title,
|
const QString &title,
|
||||||
|
|
@ -46,19 +242,78 @@ ChatFilter::ChatFilter(
|
||||||
Flags flags,
|
Flags flags,
|
||||||
base::flat_set<not_null<History*>> always,
|
base::flat_set<not_null<History*>> always,
|
||||||
std::vector<not_null<History*>> pinned,
|
std::vector<not_null<History*>> pinned,
|
||||||
base::flat_set<not_null<History*>> never)
|
base::flat_set<not_null<History*>> never,
|
||||||
|
bool isDefault,
|
||||||
|
bool isLocal,
|
||||||
|
int cloudLocalOrder)
|
||||||
: _id(id)
|
: _id(id)
|
||||||
, _title(title)
|
, _title(title)
|
||||||
, _iconEmoji(iconEmoji)
|
, _iconEmoji(iconEmoji)
|
||||||
, _always(std::move(always))
|
, _always(std::move(always))
|
||||||
, _pinned(std::move(pinned))
|
, _pinned(std::move(pinned))
|
||||||
, _never(std::move(never))
|
, _never(std::move(never))
|
||||||
, _flags(flags) {
|
, _flags(flags)
|
||||||
|
, _isDefault(isDefault)
|
||||||
|
, _isLocal(isLocal)
|
||||||
|
, _cloudLocalOrder(cloudLocalOrder) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatFilter ChatFilter::local(
|
||||||
|
const LocalFolder &data,
|
||||||
|
not_null<Session*> owner) {
|
||||||
|
auto &&to_histories = ranges::views::transform([&](
|
||||||
|
const uint64 &filterPeer) {
|
||||||
|
PeerData *peer = nullptr;
|
||||||
|
auto peerId = PeerId(filterPeer);
|
||||||
|
|
||||||
|
if (peerIsUser(peerId)) {
|
||||||
|
const auto user = owner->user(peerToUser(peerId).bare);
|
||||||
|
peer = (PeerData *)user;
|
||||||
|
} else if (peerIsChat(peerId)) {
|
||||||
|
const auto chat = owner->chat(peerToChat(peerId).bare);
|
||||||
|
peer = (PeerData *)chat;
|
||||||
|
} else if (peerIsChannel(peerId)) {
|
||||||
|
const auto channel = owner->channel(peerToChannel(peerId).bare);
|
||||||
|
peer = (PeerData *)channel;
|
||||||
|
}
|
||||||
|
return peer ? owner->history(peer).get() : nullptr;
|
||||||
|
}) | ranges::views::filter([](History *history) {
|
||||||
|
return history != nullptr;
|
||||||
|
}) | ranges::views::transform([](History *history) {
|
||||||
|
return not_null<History*>(history);
|
||||||
|
});
|
||||||
|
auto &&always = ranges::views::concat(
|
||||||
|
data.always
|
||||||
|
) | to_histories;
|
||||||
|
auto pinned = ranges::views::all(
|
||||||
|
data.pinned
|
||||||
|
) | to_histories | ranges::to_vector;
|
||||||
|
auto &&never = ranges::views::all(
|
||||||
|
data.never
|
||||||
|
) | to_histories;
|
||||||
|
auto &&all = ranges::views::concat(always, pinned);
|
||||||
|
auto list = base::flat_set<not_null<History*>>{
|
||||||
|
all.begin(),
|
||||||
|
all.end()
|
||||||
|
};
|
||||||
|
const auto defaultFilterId = owner->session().account().defaultFilterId();
|
||||||
|
return ChatFilter(
|
||||||
|
data.id,
|
||||||
|
data.name,
|
||||||
|
data.emoticon,
|
||||||
|
data.flags,
|
||||||
|
std::move(list),
|
||||||
|
std::move(pinned),
|
||||||
|
{ never.begin(), never.end() },
|
||||||
|
(data.id == defaultFilterId),
|
||||||
|
true,
|
||||||
|
data.cloudOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatFilter ChatFilter::FromTL(
|
ChatFilter ChatFilter::FromTL(
|
||||||
const MTPDialogFilter &data,
|
const MTPDialogFilter &data,
|
||||||
not_null<Session*> owner) {
|
not_null<Session*> owner,
|
||||||
|
bool isLocal) {
|
||||||
return data.match([&](const MTPDdialogFilter &data) {
|
return data.match([&](const MTPDdialogFilter &data) {
|
||||||
const auto flags = (data.is_contacts() ? Flag::Contacts : Flag(0))
|
const auto flags = (data.is_contacts() ? Flag::Contacts : Flag(0))
|
||||||
| (data.is_non_contacts() ? Flag::NonContacts : Flag(0))
|
| (data.is_non_contacts() ? Flag::NonContacts : Flag(0))
|
||||||
|
|
@ -91,6 +346,7 @@ ChatFilter ChatFilter::FromTL(
|
||||||
all.begin(),
|
all.begin(),
|
||||||
all.end()
|
all.end()
|
||||||
};
|
};
|
||||||
|
const auto defaultFilterId = owner->session().account().defaultFilterId();
|
||||||
return ChatFilter(
|
return ChatFilter(
|
||||||
data.vid().v,
|
data.vid().v,
|
||||||
qs(data.vtitle()),
|
qs(data.vtitle()),
|
||||||
|
|
@ -98,7 +354,9 @@ ChatFilter ChatFilter::FromTL(
|
||||||
flags,
|
flags,
|
||||||
std::move(list),
|
std::move(list),
|
||||||
std::move(pinned),
|
std::move(pinned),
|
||||||
{ never.begin(), never.end() });
|
{ never.begin(), never.end() },
|
||||||
|
(data.vid().v == defaultFilterId),
|
||||||
|
isLocal);
|
||||||
}, [](const MTPDdialogFilterDefault &d) {
|
}, [](const MTPDdialogFilterDefault &d) {
|
||||||
return ChatFilter();
|
return ChatFilter();
|
||||||
}, [&](const MTPDdialogFilterChatlist &data) {
|
}, [&](const MTPDdialogFilterChatlist &data) {
|
||||||
|
|
@ -227,6 +485,39 @@ MTPDialogFilter ChatFilter::tl(FilterId replaceId) const {
|
||||||
MTP_vector<MTPInputPeer>(never));
|
MTP_vector<MTPInputPeer>(never));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LocalFolder ChatFilter::toLocal(FilterId replaceId) const {
|
||||||
|
auto always = _always;
|
||||||
|
auto pinned = std::vector<uint64>();
|
||||||
|
pinned.reserve(_pinned.size());
|
||||||
|
for (const auto &history : _pinned) {
|
||||||
|
const auto &peer = history->peer;
|
||||||
|
pinned.push_back(peer->id.value);
|
||||||
|
always.remove(history);
|
||||||
|
}
|
||||||
|
auto include = std::vector<uint64>();
|
||||||
|
include.reserve(always.size());
|
||||||
|
for (const auto &history : always) {
|
||||||
|
const auto &peer = history->peer;
|
||||||
|
include.push_back(peer->id.value);
|
||||||
|
}
|
||||||
|
auto never = std::vector<uint64>();
|
||||||
|
never.reserve(_never.size());
|
||||||
|
for (const auto &history : _never) {
|
||||||
|
const auto &peer = history->peer;
|
||||||
|
never.push_back(peer->id.value);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
.id = replaceId ? replaceId : _id,
|
||||||
|
.cloudOrder = _cloudLocalOrder,
|
||||||
|
.name = _title,
|
||||||
|
.emoticon = _iconEmoji,
|
||||||
|
.always = include,
|
||||||
|
.never = never,
|
||||||
|
.pinned = pinned,
|
||||||
|
.flags = _flags
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
FilterId ChatFilter::id() const {
|
FilterId ChatFilter::id() const {
|
||||||
return _id;
|
return _id;
|
||||||
}
|
}
|
||||||
|
|
@ -235,6 +526,10 @@ QString ChatFilter::title() const {
|
||||||
return _title;
|
return _title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChatFilter::isDefault() const {
|
||||||
|
return _isDefault;
|
||||||
|
}
|
||||||
|
|
||||||
QString ChatFilter::iconEmoji() const {
|
QString ChatFilter::iconEmoji() const {
|
||||||
return _iconEmoji;
|
return _iconEmoji;
|
||||||
}
|
}
|
||||||
|
|
@ -264,6 +559,9 @@ const base::flat_set<not_null<History*>> &ChatFilter::never() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChatFilter::contains(not_null<History*> history) const {
|
bool ChatFilter::contains(not_null<History*> history) const {
|
||||||
|
if (_never.contains(history)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const auto flag = [&] {
|
const auto flag = [&] {
|
||||||
const auto peer = history->peer;
|
const auto peer = history->peer;
|
||||||
if (const auto user = peer->asUser()) {
|
if (const auto user = peer->asUser()) {
|
||||||
|
|
@ -284,14 +582,77 @@ bool ChatFilter::contains(not_null<History*> history) const {
|
||||||
Unexpected("Peer type in ChatFilter::contains.");
|
Unexpected("Peer type in ChatFilter::contains.");
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
if (_never.contains(history)) {
|
const auto filterAdmin = [&] {
|
||||||
|
if (!(_flags & Flag::Owned)
|
||||||
|
&& !(_flags & Flag::NotOwned)
|
||||||
|
&& !(_flags & Flag::Admin)
|
||||||
|
&& !(_flags & Flag::NotAdmin)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto peer = history->peer;
|
||||||
|
if (const auto chat = peer->asChat()) {
|
||||||
|
// if i created the chat:
|
||||||
|
// // if the filter excludes owned chats, don't add in list
|
||||||
|
// // if the filter excludes non-admin chats, add only if filter includes owned chats
|
||||||
|
// else if i am admin in chat:
|
||||||
|
// // if the filter excludes admin chats, don't add in list
|
||||||
|
// // if the filter excludes non-owned chats, add only if filter includes admin chats
|
||||||
|
// else:
|
||||||
|
// // add in list only if filter doesn't exclude non-owned or non-admin chats
|
||||||
|
if (chat->amCreator()) {
|
||||||
|
return !(_flags & Flag::NotOwned) && ((_flags & Flag::Admin)
|
||||||
|
? (_flags & Flag::Owned)
|
||||||
|
: true);
|
||||||
|
} else if (chat->hasAdminRights()) {
|
||||||
|
return !(_flags & Flag::NotAdmin) && ((_flags & Flag::Owned)
|
||||||
|
? (_flags & Flag::Admin)
|
||||||
|
: true);
|
||||||
|
} else {
|
||||||
|
return !(_flags & Flag::Owned) && !(_flags & Flag::Admin);
|
||||||
|
}
|
||||||
|
} else if (const auto channel = peer->asChannel()) {
|
||||||
|
if (channel->amCreator()) {
|
||||||
|
return !(_flags & Flag::NotOwned) && ((_flags & Flag::Admin)
|
||||||
|
? (_flags & Flag::Owned)
|
||||||
|
: true);
|
||||||
|
} else if (channel->hasAdminRights()) {
|
||||||
|
return !(_flags & Flag::NotAdmin) && ((_flags & Flag::Owned)
|
||||||
|
? (_flags & Flag::Admin)
|
||||||
|
: true);
|
||||||
|
} else {
|
||||||
|
return !(_flags & Flag::Owned) && !(_flags & Flag::Admin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
};
|
||||||
|
const auto filterUnfiltered = [&] {
|
||||||
|
if (!(_flags & Flag::NoFilter)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &list = history->owner().chatsFilters().list();
|
||||||
|
for (auto filter : list) {
|
||||||
|
if (filter.id() == _id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.contains(history)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
const auto state = (_flags & (Flag::NoMuted | Flag::NoRead))
|
const auto state = (_flags & (Flag::NoMuted | Flag::NoRead))
|
||||||
? history->chatListBadgesState()
|
? history->chatListBadgesState()
|
||||||
: Dialogs::BadgesState();
|
: Dialogs::BadgesState();
|
||||||
return false
|
return false
|
||||||
|| ((_flags & flag)
|
|| ((_flags & flag)
|
||||||
|
&& filterAdmin()
|
||||||
|
&& (!(_flags & Flag::Recent)
|
||||||
|
|| history->owner().session().account().isRecent(history->peer->id))
|
||||||
&& (!(_flags & Flag::NoMuted)
|
&& (!(_flags & Flag::NoMuted)
|
||||||
|| !history->muted()
|
|| !history->muted()
|
||||||
|| (state.mention
|
|| (state.mention
|
||||||
|
|
@ -302,10 +663,15 @@ bool ChatFilter::contains(not_null<History*> history) const {
|
||||||
|| state.mention
|
|| state.mention
|
||||||
|| history->fakeUnreadWhileOpened())
|
|| history->fakeUnreadWhileOpened())
|
||||||
&& (!(_flags & Flag::NoArchived)
|
&& (!(_flags & Flag::NoArchived)
|
||||||
|| (history->folderKnown() && !history->folder())))
|
|| (history->folderKnown() && !history->folder()))
|
||||||
|
&& filterUnfiltered())
|
||||||
|| _always.contains(history);
|
|| _always.contains(history);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChatFilter::isLocal() const {
|
||||||
|
return _isLocal;
|
||||||
|
}
|
||||||
|
|
||||||
ChatFilters::ChatFilters(not_null<Session*> owner)
|
ChatFilters::ChatFilters(not_null<Session*> owner)
|
||||||
: _owner(owner)
|
: _owner(owner)
|
||||||
, _moreChatsTimer([=] { checkLoadMoreChatsLists(); }) {
|
, _moreChatsTimer([=] { checkLoadMoreChatsLists(); }) {
|
||||||
|
|
@ -375,10 +741,16 @@ void ChatFilters::load(bool force) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatFilters::received(const QVector<MTPDialogFilter> &list) {
|
void ChatFilters::received(const QVector<MTPDialogFilter> &list) {
|
||||||
|
const auto account = &_owner->session().account();
|
||||||
|
const auto accountId = account->session().userId().bare;
|
||||||
|
const auto isTestAccount = account->mtp().isTestMode();
|
||||||
|
auto localFilters = ::Kotato::JsonSettings::GetJsonArray("folders/local", accountId, isTestAccount);
|
||||||
|
const auto limit = Data::PremiumLimits(&_owner->session()).dialogFiltersCurrent();
|
||||||
auto position = 0;
|
auto position = 0;
|
||||||
|
auto originalPosition = 0;
|
||||||
auto changed = false;
|
auto changed = false;
|
||||||
for (const auto &filter : list) {
|
|
||||||
auto parsed = ChatFilter::FromTL(filter, _owner);
|
auto addToList = [&] (ChatFilter parsed) {
|
||||||
const auto b = begin(_list) + position, e = end(_list);
|
const auto b = begin(_list) + position, e = end(_list);
|
||||||
const auto i = ranges::find(b, e, parsed.id(), &ChatFilter::id);
|
const auto i = ranges::find(b, e, parsed.id(), &ChatFilter::id);
|
||||||
if (i == e) {
|
if (i == e) {
|
||||||
|
|
@ -394,7 +766,62 @@ void ChatFilters::received(const QVector<MTPDialogFilter> &list) {
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
++position;
|
++position;
|
||||||
|
};
|
||||||
|
|
||||||
|
// First we're ensuring that IDs are correct
|
||||||
|
auto leastLocalId = limit;
|
||||||
|
for (auto localFilter : localFilters) {
|
||||||
|
auto local = localFilter.toObject();
|
||||||
|
if (leastLocalId > local.value("id").toInt()) {
|
||||||
|
leastLocalId = local.value("id").toInt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (leastLocalId < limit) {
|
||||||
|
const auto diff = limit - leastLocalId;
|
||||||
|
auto localFolders = QJsonArray();
|
||||||
|
for (auto localFilter : localFilters) {
|
||||||
|
auto local = localFilter.toObject();
|
||||||
|
local.insert("id", local.value("id").toInt() + diff);
|
||||||
|
localFolders << local;
|
||||||
|
}
|
||||||
|
::Kotato::JsonSettings::Set("folders/local", localFolders, accountId, isTestAccount);
|
||||||
|
::Kotato::JsonSettings::Write();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we're adding cloud filters and corresponding local filters.
|
||||||
|
for (const auto &filter : list) {
|
||||||
|
addToList(ChatFilter::FromTL(filter, _owner));
|
||||||
|
for (const auto &localFilter : localFilters) {
|
||||||
|
auto local = MakeLocalFolder(localFilter.toObject());
|
||||||
|
if (local.cloudOrder != originalPosition) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addToList(ChatFilter::local(local, _owner));
|
||||||
|
}
|
||||||
|
++originalPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then we adding local filters, retaining cloud order
|
||||||
|
while (originalPosition < limit) {
|
||||||
|
for (const auto &localFilter : localFilters) {
|
||||||
|
auto local = MakeLocalFolder(localFilter.toObject());
|
||||||
|
if (local.cloudOrder != originalPosition) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addToList(ChatFilter::local(local, _owner));
|
||||||
|
}
|
||||||
|
++originalPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// And finally we adding other filters
|
||||||
|
for (const auto &localFilter : localFilters) {
|
||||||
|
auto local = MakeLocalFolder(localFilter.toObject());
|
||||||
|
if (local.cloudOrder < limit) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addToList(ChatFilter::local(local, _owner));
|
||||||
|
}
|
||||||
|
|
||||||
while (position < _list.size()) {
|
while (position < _list.size()) {
|
||||||
applyRemove(position);
|
applyRemove(position);
|
||||||
changed = true;
|
changed = true;
|
||||||
|
|
@ -555,7 +982,7 @@ void ChatFilters::applyInsert(ChatFilter filter, int position) {
|
||||||
|
|
||||||
_list.insert(
|
_list.insert(
|
||||||
begin(_list) + position,
|
begin(_list) + position,
|
||||||
ChatFilter(filter.id(), {}, {}, {}, {}, {}, {}));
|
ChatFilter(filter.id(), {}, {}, {}, {}, {}, {}, false, filter.isLocal()));
|
||||||
applyChange(*(begin(_list) + position), std::move(filter));
|
applyChange(*(begin(_list) + position), std::move(filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -701,12 +1128,13 @@ const ChatFilter &ChatFilters::applyUpdatedPinned(
|
||||||
if (const auto history = row.history()) {
|
if (const auto history = row.history()) {
|
||||||
if (always.contains(history)) {
|
if (always.contains(history)) {
|
||||||
pinned.push_back(history);
|
pinned.push_back(history);
|
||||||
} else if (always.size() < limit) {
|
} else if (always.size() < limit || i->isLocal()) {
|
||||||
always.insert(history);
|
always.insert(history);
|
||||||
pinned.push_back(history);
|
pinned.push_back(history);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const auto defaultFilterId = _owner->session().account().defaultFilterId();
|
||||||
set(ChatFilter(
|
set(ChatFilter(
|
||||||
id,
|
id,
|
||||||
i->title(),
|
i->title(),
|
||||||
|
|
@ -714,7 +1142,9 @@ const ChatFilter &ChatFilters::applyUpdatedPinned(
|
||||||
i->flags(),
|
i->flags(),
|
||||||
std::move(always),
|
std::move(always),
|
||||||
std::move(pinned),
|
std::move(pinned),
|
||||||
i->never()));
|
i->never(),
|
||||||
|
(id == defaultFilterId),
|
||||||
|
i->isLocal()));
|
||||||
return *i;
|
return *i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -726,18 +1156,34 @@ void ChatFilters::saveOrder(
|
||||||
}
|
}
|
||||||
const auto api = &_owner->session().api();
|
const auto api = &_owner->session().api();
|
||||||
api->request(_saveOrderRequestId).cancel();
|
api->request(_saveOrderRequestId).cancel();
|
||||||
|
const auto limit = Data::PremiumLimits(&_owner->session()).dialogFiltersCurrent();
|
||||||
|
|
||||||
auto ids = QVector<MTPint>();
|
auto ids = QVector<MTPint>();
|
||||||
ids.reserve(order.size());
|
ids.reserve(order.size());
|
||||||
|
auto cloudIds = QVector<MTPint>();
|
||||||
|
cloudIds.reserve(limit);
|
||||||
|
|
||||||
for (const auto id : order) {
|
for (const auto id : order) {
|
||||||
ids.push_back(MTP_int(id));
|
ids.push_back(MTP_int(id));
|
||||||
|
|
||||||
|
auto i = ranges::find(_list, id, &ChatFilter::id);
|
||||||
|
Assert(i != end(_list));
|
||||||
|
|
||||||
|
if ((*i).isLocal()) {
|
||||||
|
i->setLocalCloudOrder(cloudIds.size());
|
||||||
|
} else {
|
||||||
|
cloudIds.push_back(MTP_int(id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const auto wrapped = MTP_vector<MTPint>(ids);
|
const auto wrapped = MTP_vector<MTPint>(ids);
|
||||||
|
|
||||||
apply(MTP_updateDialogFilterOrder(wrapped));
|
apply(MTP_updateDialogFilterOrder(wrapped));
|
||||||
_saveOrderRequestId = api->request(MTPmessages_UpdateDialogFiltersOrder(
|
|
||||||
wrapped
|
if (!cloudIds.isEmpty()) {
|
||||||
)).afterRequest(_saveOrderAfterId).send();
|
const auto cloudWrapped = MTP_vector<MTPint>(cloudIds);
|
||||||
|
_saveOrderRequestId = api->request(MTPmessages_UpdateDialogFiltersOrder(
|
||||||
|
cloudWrapped
|
||||||
|
)).afterRequest(_saveOrderAfterId).send();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChatFilters::archiveNeeded() const {
|
bool ChatFilters::archiveNeeded() const {
|
||||||
|
|
@ -1004,4 +1450,20 @@ void ChatFilters::checkLoadMoreChatsLists() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatFilters::saveLocal() {
|
||||||
|
auto localFolders = QJsonArray();
|
||||||
|
const auto account = &_owner->session().account();
|
||||||
|
const auto accountId = account->session().userId().bare;
|
||||||
|
const auto isTestAccount = account->mtp().isTestMode();
|
||||||
|
|
||||||
|
for (const auto &folder : _list) {
|
||||||
|
if (folder.isLocal()) {
|
||||||
|
localFolders << folder.toLocal().toJson();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::Kotato::JsonSettings::Set("folders/local", localFolders, accountId, isTestAccount);
|
||||||
|
::Kotato::JsonSettings::Write();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,11 @@ struct MoreChatsBarContent;
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
||||||
class Session;
|
class Session;
|
||||||
|
struct LocalFolder;
|
||||||
|
|
||||||
class ChatFilter final {
|
class ChatFilter final {
|
||||||
public:
|
public:
|
||||||
enum class Flag : ushort {
|
enum class Flag : uint32 {
|
||||||
Contacts = (1 << 0),
|
Contacts = (1 << 0),
|
||||||
NonContacts = (1 << 1),
|
NonContacts = (1 << 1),
|
||||||
Groups = (1 << 2),
|
Groups = (1 << 2),
|
||||||
|
|
@ -43,11 +44,20 @@ public:
|
||||||
|
|
||||||
NewChats = (1 << 10), // Telegram Business exceptions.
|
NewChats = (1 << 10), // Telegram Business exceptions.
|
||||||
ExistingChats = (1 << 11),
|
ExistingChats = (1 << 11),
|
||||||
|
|
||||||
|
// Local flags
|
||||||
|
Owned = (1 << 12),
|
||||||
|
Admin = (1 << 13),
|
||||||
|
NotOwned = (1 << 14),
|
||||||
|
NotAdmin = (1 << 15),
|
||||||
|
Recent = (1 << 16),
|
||||||
|
NoFilter = (1 << 17),
|
||||||
};
|
};
|
||||||
friend constexpr inline bool is_flag_type(Flag) { return true; };
|
friend constexpr inline bool is_flag_type(Flag) { return true; };
|
||||||
using Flags = base::flags<Flag>;
|
using Flags = base::flags<Flag>;
|
||||||
|
|
||||||
ChatFilter() = default;
|
ChatFilter() = default;
|
||||||
|
ChatFilter(FilterId id, bool isLocal = false);
|
||||||
ChatFilter(
|
ChatFilter(
|
||||||
FilterId id,
|
FilterId id,
|
||||||
const QString &title,
|
const QString &title,
|
||||||
|
|
@ -55,7 +65,14 @@ public:
|
||||||
Flags flags,
|
Flags flags,
|
||||||
base::flat_set<not_null<History*>> always,
|
base::flat_set<not_null<History*>> always,
|
||||||
std::vector<not_null<History*>> pinned,
|
std::vector<not_null<History*>> pinned,
|
||||||
base::flat_set<not_null<History*>> never);
|
base::flat_set<not_null<History*>> never,
|
||||||
|
bool isDefault = false,
|
||||||
|
bool isLocal = false,
|
||||||
|
int localCloudOrder = 0);
|
||||||
|
|
||||||
|
[[nodiscard]] static ChatFilter local(
|
||||||
|
const LocalFolder &data,
|
||||||
|
not_null<Session*> owner);
|
||||||
|
|
||||||
[[nodiscard]] ChatFilter withId(FilterId id) const;
|
[[nodiscard]] ChatFilter withId(FilterId id) const;
|
||||||
[[nodiscard]] ChatFilter withTitle(const QString &title) const;
|
[[nodiscard]] ChatFilter withTitle(const QString &title) const;
|
||||||
|
|
@ -65,11 +82,14 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] static ChatFilter FromTL(
|
[[nodiscard]] static ChatFilter FromTL(
|
||||||
const MTPDialogFilter &data,
|
const MTPDialogFilter &data,
|
||||||
not_null<Session*> owner);
|
not_null<Session*> owner,
|
||||||
|
bool isLocal = false);
|
||||||
[[nodiscard]] MTPDialogFilter tl(FilterId replaceId = 0) const;
|
[[nodiscard]] MTPDialogFilter tl(FilterId replaceId = 0) const;
|
||||||
|
[[nodiscard]] LocalFolder toLocal(FilterId replaceId = 0) const;
|
||||||
|
|
||||||
[[nodiscard]] FilterId id() const;
|
[[nodiscard]] FilterId id() const;
|
||||||
[[nodiscard]] QString title() const;
|
[[nodiscard]] QString title() const;
|
||||||
|
[[nodiscard]] bool isDefault() const;
|
||||||
[[nodiscard]] QString iconEmoji() const;
|
[[nodiscard]] QString iconEmoji() const;
|
||||||
[[nodiscard]] Flags flags() const;
|
[[nodiscard]] Flags flags() const;
|
||||||
[[nodiscard]] bool chatlist() const;
|
[[nodiscard]] bool chatlist() const;
|
||||||
|
|
@ -80,6 +100,12 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] bool contains(not_null<History*> history) const;
|
[[nodiscard]] bool contains(not_null<History*> history) const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool isLocal() const;
|
||||||
|
|
||||||
|
void setLocalCloudOrder(int order) {
|
||||||
|
_cloudLocalOrder = order;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FilterId _id = 0;
|
FilterId _id = 0;
|
||||||
QString _title;
|
QString _title;
|
||||||
|
|
@ -88,6 +114,9 @@ private:
|
||||||
std::vector<not_null<History*>> _pinned;
|
std::vector<not_null<History*>> _pinned;
|
||||||
base::flat_set<not_null<History*>> _never;
|
base::flat_set<not_null<History*>> _never;
|
||||||
Flags _flags;
|
Flags _flags;
|
||||||
|
bool _isDefault = false;
|
||||||
|
bool _isLocal = false;
|
||||||
|
int _cloudLocalOrder = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -180,6 +209,7 @@ public:
|
||||||
[[nodiscard]] const std::vector<not_null<PeerData*>> &moreChats(
|
[[nodiscard]] const std::vector<not_null<PeerData*>> &moreChats(
|
||||||
FilterId id) const;
|
FilterId id) const;
|
||||||
void moreChatsHide(FilterId id, bool localOnly = false);
|
void moreChatsHide(FilterId id, bool localOnly = false);
|
||||||
|
void saveLocal();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct MoreChatsData {
|
struct MoreChatsData {
|
||||||
|
|
@ -229,4 +259,19 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LocalFolder {
|
||||||
|
QJsonObject toJson();
|
||||||
|
|
||||||
|
int id = 0;
|
||||||
|
int cloudOrder = 0;
|
||||||
|
QString name;
|
||||||
|
QString emoticon;
|
||||||
|
std::vector<uint64> always;
|
||||||
|
std::vector<uint64> never;
|
||||||
|
std::vector<uint64> pinned;
|
||||||
|
ChatFilter::Flags flags = Data::ChatFilter::Flags(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
LocalFolder MakeLocalFolder(const QJsonObject &obj);
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
||||||
|
|
@ -2173,7 +2173,9 @@ bool Session::pinnedCanPin(
|
||||||
const auto i = ranges::find(list, filterId, &Data::ChatFilter::id);
|
const auto i = ranges::find(list, filterId, &Data::ChatFilter::id);
|
||||||
return (i == end(list))
|
return (i == end(list))
|
||||||
|| (i->always().contains(history))
|
|| (i->always().contains(history))
|
||||||
|| (i->always().size() < pinnedChatsLimit(filterId));
|
|| (i->always().size() < (i->isLocal()
|
||||||
|
? std::numeric_limits<int>::max()
|
||||||
|
: pinnedChatsLimit(filterId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Session::pinnedChatsLimit(Data::Folder *folder) const {
|
int Session::pinnedChatsLimit(Data::Folder *folder) const {
|
||||||
|
|
|
||||||
|
|
@ -1806,6 +1806,7 @@ void Widget::slideFinished() {
|
||||||
|
|
||||||
void Widget::escape() {
|
void Widget::escape() {
|
||||||
if (!cancelSearch()) {
|
if (!cancelSearch()) {
|
||||||
|
const auto defaultFilterId = session().account().defaultFilterId();
|
||||||
if (controller()->shownForum().current()) {
|
if (controller()->shownForum().current()) {
|
||||||
controller()->closeForum();
|
controller()->closeForum();
|
||||||
} else if (controller()->openedFolder().current()) {
|
} else if (controller()->openedFolder().current()) {
|
||||||
|
|
@ -1815,7 +1816,11 @@ void Widget::escape() {
|
||||||
} else {
|
} else {
|
||||||
const auto filters = &session().data().chatsFilters();
|
const auto filters = &session().data().chatsFilters();
|
||||||
const auto &list = filters->list();
|
const auto &list = filters->list();
|
||||||
const auto first = list.empty() ? FilterId() : list.front().id();
|
const auto first = list.empty()
|
||||||
|
? FilterId()
|
||||||
|
: defaultFilterId != 0
|
||||||
|
? defaultFilterId
|
||||||
|
: list.front().id();
|
||||||
if (controller()->activeChatsFilterCurrent() != first) {
|
if (controller()->activeChatsFilterCurrent() != first) {
|
||||||
controller()->setActiveChatsFilter(first);
|
controller()->setActiveChatsFilter(first);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/item_text_options.h"
|
#include "ui/item_text_options.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "main/main_session_settings.h"
|
#include "main/main_session_settings.h"
|
||||||
|
#include "main/main_account.h"
|
||||||
#include "main/session/send_as_peers.h"
|
#include "main/session/send_as_peers.h"
|
||||||
#include "window/notifications_manager.h"
|
#include "window/notifications_manager.h"
|
||||||
#include "window/window_adaptive.h"
|
#include "window/window_adaptive.h"
|
||||||
|
|
@ -2547,6 +2548,8 @@ void HistoryWidget::showHistory(
|
||||||
session().sponsoredMessages().request(_history, checkState);
|
session().sponsoredMessages().request(_history, checkState);
|
||||||
checkState();
|
checkState();
|
||||||
}
|
}
|
||||||
|
_history->owner().session().account().addToRecent(_peer->id);
|
||||||
|
_history->owner().chatsFilters().refreshHistory(_history);
|
||||||
} else {
|
} else {
|
||||||
_chooseForReport = nullptr;
|
_chooseForReport = nullptr;
|
||||||
refreshTopBarActiveChat();
|
refreshTopBarActiveChat();
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,11 @@ const std::map<QString, Definition, std::greater<QString>> DefinitionMap {
|
||||||
.type = SettingType::IntSetting,
|
.type = SettingType::IntSetting,
|
||||||
.defaultValue = 0,
|
.defaultValue = 0,
|
||||||
.limitHandler = IntLimit(0, 5), }},
|
.limitHandler = IntLimit(0, 5), }},
|
||||||
|
{ "folders/default", {
|
||||||
|
.scope = SettingScope::Account,
|
||||||
|
.type = SettingType::IntSetting,
|
||||||
|
.defaultValue = 0,
|
||||||
|
.limitHandler = IntLimitMin(0), }},
|
||||||
{ "folders/count_unmuted_only", {
|
{ "folders/count_unmuted_only", {
|
||||||
.type = SettingType::BoolSetting,
|
.type = SettingType::BoolSetting,
|
||||||
.defaultValue = false, }},
|
.defaultValue = false, }},
|
||||||
|
|
@ -318,6 +323,9 @@ const std::map<QString, Definition, std::greater<QString>> DefinitionMap {
|
||||||
{ "folders/hide_all_chats", {
|
{ "folders/hide_all_chats", {
|
||||||
.type = SettingType::BoolSetting,
|
.type = SettingType::BoolSetting,
|
||||||
.defaultValue = false, }},
|
.defaultValue = false, }},
|
||||||
|
{ "folders/local", {
|
||||||
|
.scope = SettingScope::Account,
|
||||||
|
.type = SettingType::QJsonArraySetting, }},
|
||||||
};
|
};
|
||||||
|
|
||||||
using OldOptionKey = QString;
|
using OldOptionKey = QString;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "main/main_account.h"
|
#include "main/main_account.h"
|
||||||
|
|
||||||
|
#include "kotato/kotato_settings.h"
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "storage/storage_account.h"
|
#include "storage/storage_account.h"
|
||||||
|
|
@ -193,6 +194,11 @@ void Account::createSession(
|
||||||
_sessionValue = _session.get();
|
_sessionValue = _session.get();
|
||||||
|
|
||||||
Ensures(_session != nullptr);
|
Ensures(_session != nullptr);
|
||||||
|
|
||||||
|
_defaultFilterId = ::Kotato::JsonSettings::GetInt(
|
||||||
|
"folders/default",
|
||||||
|
session().userId().bare,
|
||||||
|
_mtp->isTestMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Account::destroySession(DestroyReason reason) {
|
void Account::destroySession(DestroyReason reason) {
|
||||||
|
|
@ -602,6 +608,37 @@ void Account::destroyStaleAuthorizationKeys() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Account::setDefaultFilterId(uint64 id) {
|
||||||
|
Expects(_mtp != nullptr);
|
||||||
|
Expects(_session != nullptr);
|
||||||
|
|
||||||
|
_defaultFilterId = id;
|
||||||
|
|
||||||
|
::Kotato::JsonSettings::Set(
|
||||||
|
"folders/default",
|
||||||
|
_defaultFilterId,
|
||||||
|
session().userId().bare,
|
||||||
|
_mtp->isTestMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Account::isCurrent(uint64 id, bool testMode) {
|
||||||
|
Expects(_mtp != nullptr);
|
||||||
|
Expects(_session != nullptr);
|
||||||
|
|
||||||
|
return id == session().userId().bare
|
||||||
|
&& _mtp->isTestMode() == testMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Account::addToRecent(PeerId id) {
|
||||||
|
if (!_recent.contains(id.value)) {
|
||||||
|
_recent << id.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Account::isRecent(PeerId id) {
|
||||||
|
return _recent.contains(id.value);
|
||||||
|
}
|
||||||
|
|
||||||
void Account::setHandleLoginCode(Fn<void(QString)> callback) {
|
void Account::setHandleLoginCode(Fn<void(QString)> callback) {
|
||||||
_handleLoginCode = std::move(callback);
|
_handleLoginCode = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,16 @@ public:
|
||||||
return _lifetime;
|
return _lifetime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int defaultFilterId() {
|
||||||
|
return _defaultFilterId;
|
||||||
|
}
|
||||||
|
void setDefaultFilterId(uint64 id);
|
||||||
|
|
||||||
|
[[nodiscard]] bool isCurrent(uint64 id, bool testMode);
|
||||||
|
|
||||||
|
void addToRecent(PeerId id);
|
||||||
|
[[nodiscard]] bool isRecent(PeerId id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr auto kDefaultSaveDelay = crl::time(1000);
|
static constexpr auto kDefaultSaveDelay = crl::time(1000);
|
||||||
enum class DestroyReason {
|
enum class DestroyReason {
|
||||||
|
|
@ -164,6 +174,9 @@ private:
|
||||||
MTP::Instance::Fields _mtpFields;
|
MTP::Instance::Fields _mtpFields;
|
||||||
MTP::AuthKeysList _mtpKeysToDestroy;
|
MTP::AuthKeysList _mtpKeysToDestroy;
|
||||||
bool _loggingOut = false;
|
bool _loggingOut = false;
|
||||||
|
int _defaultFilterId = 0;
|
||||||
|
|
||||||
|
QSet<uint64> _recent;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
|
|
||||||