Local folders
BIN
Telegram/Resources/icons/filters/filters_local_book.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_book@2x.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_book@3x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram/Resources/icons/filters/filters_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/filters/filters_local_brackets.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_brackets@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_brackets@3x.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_candle.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_candle@2x.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_candle@3x.png
Normal file
|
After Width: | Height: | Size: 3 KiB |
BIN
Telegram/Resources/icons/filters/filters_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/filters/filters_local_city.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_city@2x.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_city@3x.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
Telegram/Resources/icons/filters/filters_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/filters/filters_local_desktop.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_desktop@2x.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Telegram/Resources/icons/filters/filters_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/filters/filters_local_earth.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_earth@2x.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_earth@3x.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_music.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_music@2x.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_music@3x.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
Telegram/Resources/icons/filters/filters_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/filters/filters_local_news.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_news@2x.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_news@3x.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Telegram/Resources/icons/filters/filters_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/filters/filters_local_phone.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_phone@2x.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_phone@3x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram/Resources/icons/filters/filters_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/filters/filters_local_smile.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_smile@2x.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_smile@3x.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
Telegram/Resources/icons/filters/filters_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/filters/filters_local_sun.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_sun@2x.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_sun@3x.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_sun_active.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_sun_active@2x.png
Normal file
|
After Width: | Height: | Size: 3 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_sun_active@3x.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_video.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_video@2x.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
Telegram/Resources/icons/filters/filters_local_video@3x.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Telegram/Resources/icons/filters/filters_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/filters/filters_type_admin.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_admin@2x.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_admin@3x.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_filtered.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_filtered@2x.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_filtered@3x.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_not_admin.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_not_admin@2x.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_not_admin@3x.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_not_owner.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_not_owner@2x.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_not_owner@3x.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_owner.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_owner@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_owner@3x.png
Normal file
|
After Width: | Height: | Size: 3 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_recent.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Telegram/Resources/icons/filters/filters_type_recent@2x.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
Telegram/Resources/icons/filters/filters_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: 2.2 KiB |
BIN
Telegram/Resources/icons/settings_cloud@2x.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
Telegram/Resources/icons/settings_cloud@3x.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
|
|
@ -2636,4 +2636,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
"ktg_profile_mention_user" = "Mention user";
|
"ktg_profile_mention_user" = "Mention user";
|
||||||
|
|
||||||
|
"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.";
|
||||||
|
|
||||||
// Keys finished
|
// Keys finished
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,22 @@
|
||||||
"ktg_forward_subtitle_group_all_media": "as albums",
|
"ktg_forward_subtitle_group_all_media": "as albums",
|
||||||
"ktg_forward_subtitle_separate_messages": "one by one",
|
"ktg_forward_subtitle_separate_messages": "one by one",
|
||||||
"ktg_profile_mention_user": "Mention user",
|
"ktg_profile_mention_user": "Mention user",
|
||||||
|
"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.",
|
||||||
|
|
||||||
// This string should always be last for better work with Git.
|
// This string should always be last for better work with Git.
|
||||||
"dummy_last_string": ""
|
"dummy_last_string": ""
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,22 @@
|
||||||
"ktg_forward_subtitle_group_all_media": "as albums",
|
"ktg_forward_subtitle_group_all_media": "as albums",
|
||||||
"ktg_forward_subtitle_separate_messages": "one by one",
|
"ktg_forward_subtitle_separate_messages": "one by one",
|
||||||
"ktg_profile_mention_user": "Mention user",
|
"ktg_profile_mention_user": "Mention user",
|
||||||
|
"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.",
|
||||||
|
|
||||||
// This string should always be last for better work with Git.
|
// This string should always be last for better work with Git.
|
||||||
"dummy_last_string": ""
|
"dummy_last_string": ""
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,22 @@
|
||||||
"ktg_forward_subtitle_group_all_media": "as albums",
|
"ktg_forward_subtitle_group_all_media": "as albums",
|
||||||
"ktg_forward_subtitle_separate_messages": "one by one",
|
"ktg_forward_subtitle_separate_messages": "one by one",
|
||||||
"ktg_profile_mention_user": "Mention user",
|
"ktg_profile_mention_user": "Mention user",
|
||||||
|
"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.",
|
||||||
|
|
||||||
// This string should always be last for better work with Git.
|
// This string should always be last for better work with Git.
|
||||||
"dummy_last_string": ""
|
"dummy_last_string": ""
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,22 @@
|
||||||
"ktg_forward_subtitle_group_all_media": "as albums",
|
"ktg_forward_subtitle_group_all_media": "as albums",
|
||||||
"ktg_forward_subtitle_separate_messages": "one by one",
|
"ktg_forward_subtitle_separate_messages": "one by one",
|
||||||
"ktg_profile_mention_user": "Mention user",
|
"ktg_profile_mention_user": "Mention user",
|
||||||
|
"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.",
|
||||||
|
|
||||||
// This string should always be last for better work with Git.
|
// This string should always be last for better work with Git.
|
||||||
"dummy_last_string": ""
|
"dummy_last_string": ""
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,22 @@
|
||||||
"ktg_forward_subtitle_group_all_media": "альбомами",
|
"ktg_forward_subtitle_group_all_media": "альбомами",
|
||||||
"ktg_forward_subtitle_separate_messages": "по одному",
|
"ktg_forward_subtitle_separate_messages": "по одному",
|
||||||
"ktg_profile_mention_user": "Упомянуть пользователя",
|
"ktg_profile_mention_user": "Упомянуть пользователя",
|
||||||
|
"ktg_filters_exclude_not_owned": "Без прав владельца",
|
||||||
|
"ktg_filters_exclude_not_admin": "Без прав админа",
|
||||||
|
"ktg_filters_exclude_owned": "С правами владельца",
|
||||||
|
"ktg_filters_exclude_admin": "С правами админа",
|
||||||
|
"ktg_filters_exclude_not_recent": "Не открытые за сессию",
|
||||||
|
"ktg_filters_exclude_filtered": "Из других папок",
|
||||||
|
"ktg_filters_create_cloud": "Создать облачную папку",
|
||||||
|
"ktg_filters_create_local": "Создать локальную папку",
|
||||||
|
"ktg_filters_description": "Облачные папки синхронизируются со остальными вашими приложениями Telegram, но у локальных папок больше функций.",
|
||||||
|
"ktg_filters_new_cloud": "Новая облачная папка",
|
||||||
|
"ktg_filters_new_local": "Новая локальная папка",
|
||||||
|
"ktg_filters_edit_cloud": "Изменить облачную папку",
|
||||||
|
"ktg_filters_edit_local": "Изменить локальную папку",
|
||||||
|
"ktg_filters_local": "локальная папка",
|
||||||
|
"ktg_filters_cloud": "облачная папка",
|
||||||
|
"ktg_filters_cloud_limit": "Вы создали максимальное число облачных папок. Вместо этого можно создать локальную папку.",
|
||||||
|
|
||||||
// This string should always be last for better work with Git.
|
// This string should always be last for better work with Git.
|
||||||
"dummy_last_string": ""
|
"dummy_last_string": ""
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,22 @@
|
||||||
"ktg_forward_subtitle_group_all_media": "as albums",
|
"ktg_forward_subtitle_group_all_media": "as albums",
|
||||||
"ktg_forward_subtitle_separate_messages": "one by one",
|
"ktg_forward_subtitle_separate_messages": "one by one",
|
||||||
"ktg_profile_mention_user": "Mention user",
|
"ktg_profile_mention_user": "Mention user",
|
||||||
|
"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.",
|
||||||
|
|
||||||
// This string should always be last for better work with Git.
|
// This string should always be last for better work with Git.
|
||||||
"dummy_last_string": ""
|
"dummy_last_string": ""
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,22 @@
|
||||||
"ktg_forward_subtitle_group_all_media": "as albums",
|
"ktg_forward_subtitle_group_all_media": "as albums",
|
||||||
"ktg_forward_subtitle_separate_messages": "one by one",
|
"ktg_forward_subtitle_separate_messages": "one by one",
|
||||||
"ktg_profile_mention_user": "Mention user",
|
"ktg_profile_mention_user": "Mention user",
|
||||||
|
"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.",
|
||||||
|
|
||||||
// This string should always be last for better work with Git.
|
// This string should always be last for better work with Git.
|
||||||
"dummy_last_string": ""
|
"dummy_last_string": ""
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_chat_filters.h"
|
#include "data/data_chat_filters.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
#include "kotato/json_settings.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
|
||||||
namespace Api {
|
namespace Api {
|
||||||
|
|
@ -24,11 +25,16 @@ void SaveNewFilterPinned(
|
||||||
const auto &filter = filters.applyUpdatedPinned(
|
const auto &filter = filters.applyUpdatedPinned(
|
||||||
filterId,
|
filterId,
|
||||||
order);
|
order);
|
||||||
session->api().request(MTPmessages_UpdateDialogFilter(
|
if (filter.isLocal()) {
|
||||||
MTP_flags(MTPmessages_UpdateDialogFilter::Flag::f_filter),
|
filters.saveLocal(filterId);
|
||||||
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,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,
|
||||||
};
|
};
|
||||||
|
|
||||||
class FilterChatsPreview final : public Ui::RpWidget {
|
class FilterChatsPreview final : public Ui::RpWidget {
|
||||||
|
|
@ -133,7 +139,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());
|
||||||
|
|
@ -154,7 +162,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());
|
||||||
|
|
@ -403,7 +413,8 @@ void EditExceptions(
|
||||||
: tr::lng_filters_exclude_title()),
|
: tr::lng_filters_exclude_title()),
|
||||||
options,
|
options,
|
||||||
rules.flags() & options,
|
rules.flags() & options,
|
||||||
include ? rules.always() : rules.never());
|
include ? rules.always() : rules.never(),
|
||||||
|
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);
|
||||||
|
|
@ -437,7 +448,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();
|
||||||
|
|
@ -488,7 +501,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();
|
||||||
|
|
@ -506,7 +520,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 = [=] {
|
||||||
|
|
@ -574,7 +590,14 @@ void EditFilterBox(
|
||||||
const Data::ChatFilter &filter,
|
const Data::ChatFilter &filter,
|
||||||
Fn<void(const Data::ChatFilter &)> doneCallback) {
|
Fn<void(const Data::ChatFilter &)> doneCallback) {
|
||||||
const auto creating = filter.title().isEmpty();
|
const auto creating = filter.title().isEmpty();
|
||||||
box->setTitle(creating ? tr::lng_filters_new() : tr::lng_filters_edit());
|
const auto isLocal = filter.isLocal();
|
||||||
|
box->setTitle(creating
|
||||||
|
? (isLocal
|
||||||
|
? tr::ktg_filters_new_local()
|
||||||
|
: tr::ktg_filters_new_cloud())
|
||||||
|
: (isLocal
|
||||||
|
? tr::ktg_filters_edit_local()
|
||||||
|
: tr::ktg_filters_edit_cloud()));
|
||||||
box->setCloseByOutsideClick(false);
|
box->setCloseByOutsideClick(false);
|
||||||
|
|
||||||
using State = rpl::variable<Data::ChatFilter>;
|
using State = rpl::variable<Data::ChatFilter>;
|
||||||
|
|
@ -588,7 +611,9 @@ void EditFilterBox(
|
||||||
tr::lng_filters_new_name(),
|
tr::lng_filters_new_name(),
|
||||||
filter.title()),
|
filter.title()),
|
||||||
st::markdownLinkFieldPadding);
|
st::markdownLinkFieldPadding);
|
||||||
name->setMaxLength(kMaxFilterTitleLength);
|
if (!isLocal) {
|
||||||
|
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());
|
||||||
|
|
@ -609,7 +634,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);
|
||||||
|
|
@ -633,6 +660,13 @@ 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();
|
||||||
|
|
@ -690,7 +724,7 @@ void EditFilterBox(
|
||||||
content,
|
content,
|
||||||
data,
|
data,
|
||||||
updateDefaultTitle,
|
updateDefaultTitle,
|
||||||
kExcludeTypes,
|
(isLocal ? kExcludeTypesLocal : kExcludeTypes),
|
||||||
&Data::ChatFilter::never);
|
&Data::ChatFilter::never);
|
||||||
|
|
||||||
AddSkip(content);
|
AddSkip(content);
|
||||||
|
|
@ -706,7 +740,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([=] {
|
||||||
|
|
@ -722,7 +756,7 @@ void EditFilterBox(
|
||||||
EditExceptions(
|
EditExceptions(
|
||||||
window,
|
window,
|
||||||
box,
|
box,
|
||||||
kExcludeTypes,
|
(isLocal ? kExcludeTypesLocal : kExcludeTypes),
|
||||||
data,
|
data,
|
||||||
updateDefaultTitle,
|
updateDefaultTitle,
|
||||||
refreshPreviews);
|
refreshPreviews);
|
||||||
|
|
@ -740,7 +774,8 @@ void EditFilterBox(
|
||||||
rules.always(),
|
rules.always(),
|
||||||
rules.pinned(),
|
rules.pinned(),
|
||||||
rules.never(),
|
rules.never(),
|
||||||
checked);
|
checked,
|
||||||
|
isLocal);
|
||||||
if (title.isEmpty()) {
|
if (title.isEmpty()) {
|
||||||
name->showError();
|
name->showError();
|
||||||
return;
|
return;
|
||||||
|
|
@ -767,29 +802,40 @@ void EditExistingFilter(
|
||||||
not_null<Window::SessionController*> window,
|
not_null<Window::SessionController*> window,
|
||||||
FilterId id) {
|
FilterId id) {
|
||||||
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(id);
|
||||||
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 defaultFilterId = session->account().defaultFilterId();
|
||||||
const auto isCurrentDefault = result.id() == defaultFilterId;
|
const auto isCurrentDefault = result.id() == defaultFilterId;
|
||||||
if ((isCurrentDefault && !result.isDefault())
|
if ((isCurrentDefault && !result.isDefault())
|
||||||
|| (!isCurrentDefault && result.isDefault())) {
|
|| (!isCurrentDefault && result.isDefault())) {
|
||||||
session->account().setDefaultFilterId(result.isDefault() ? result.id() : 0);
|
session->account().setDefaultFilterId(result.isDefault() ? result.id() : 0);
|
||||||
|
needSave = true;
|
||||||
|
}
|
||||||
|
if (needSave) {
|
||||||
Kotato::JsonSettings::Write();
|
Kotato::JsonSettings::Write();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,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 {
|
||||||
|
|
@ -158,7 +164,7 @@ PaintRoundImageCallback TypeRow::generatePaintUserpicCallback() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Flag TypeRow::flag() const {
|
Flag TypeRow::flag() const {
|
||||||
return static_cast<Flag>(id() & 0xFF);
|
return static_cast<Flag>(id() & 0xFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExceptionRow::ExceptionRow(not_null<History*> history) : Row(history) {
|
ExceptionRow::ExceptionRow(not_null<History*> history) : Row(history) {
|
||||||
|
|
@ -296,6 +302,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 tr::ktg_filters_exclude_not_owned(tr::now);
|
||||||
|
case Flag::Admin: return tr::ktg_filters_exclude_not_admin(tr::now);
|
||||||
|
case Flag::NotOwned: return tr::ktg_filters_exclude_owned(tr::now);
|
||||||
|
case Flag::NotAdmin: return tr::ktg_filters_exclude_admin(tr::now);
|
||||||
|
case Flag::Recent: return tr::ktg_filters_exclude_not_recent(tr::now);
|
||||||
|
case Flag::NoFilter: return tr::ktg_filters_exclude_filtered(tr::now);
|
||||||
}
|
}
|
||||||
Unexpected("Flag in TypeName.");
|
Unexpected("Flag in TypeName.");
|
||||||
}
|
}
|
||||||
|
|
@ -317,6 +329,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.");
|
||||||
}();
|
}();
|
||||||
|
|
@ -330,6 +348,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.");
|
||||||
}();
|
}();
|
||||||
|
|
@ -367,13 +391,15 @@ EditFilterChatsListController::EditFilterChatsListController(
|
||||||
rpl::producer<QString> title,
|
rpl::producer<QString> title,
|
||||||
Flags options,
|
Flags options,
|
||||||
Flags selected,
|
Flags selected,
|
||||||
const base::flat_set<not_null<History*>> &peers)
|
const base::flat_set<not_null<History*>> &peers,
|
||||||
|
bool isLocal)
|
||||||
: ChatsListBoxController(navigation)
|
: ChatsListBoxController(navigation)
|
||||||
, _navigation(navigation)
|
, _navigation(navigation)
|
||||||
, _title(std::move(title))
|
, _title(std::move(title))
|
||||||
, _peers(peers)
|
, _peers(peers)
|
||||||
, _options(options)
|
, _options(options)
|
||||||
, _selected(selected) {
|
, _selected(selected)
|
||||||
|
, _isLocal(isLocal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Main::Session &EditFilterChatsListController::session() const {
|
Main::Session &EditFilterChatsListController::session() const {
|
||||||
|
|
@ -382,7 +408,7 @@ Main::Session &EditFilterChatsListController::session() 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();
|
||||||
if (count < kMaxExceptions || row->checked()) {
|
if (count < kMaxExceptions || row->checked() || _isLocal) {
|
||||||
delegate()->peerListSetRowChecked(row, !row->checked());
|
delegate()->peerListSetRowChecked(row, !row->checked());
|
||||||
updateTitle();
|
updateTitle();
|
||||||
}
|
}
|
||||||
|
|
@ -500,6 +526,8 @@ void EditFilterChatsListController::updateTitle() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto count = delegate()->peerListSelectedRowsCount() - types;
|
const auto count = delegate()->peerListSelectedRowsCount() - types;
|
||||||
const auto additional = qsl("%1 / %2").arg(count).arg(kMaxExceptions);
|
const auto additional = _isLocal
|
||||||
|
? tr::lng_filters_chats_count(tr::now, lt_count_short, count)
|
||||||
|
: qsl("%1 / %2").arg(count).arg(kMaxExceptions);
|
||||||
delegate()->peerListSetAdditionalTitle(rpl::single(additional));
|
delegate()->peerListSetAdditionalTitle(rpl::single(additional));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,8 @@ public:
|
||||||
rpl::producer<QString> title,
|
rpl::producer<QString> title,
|
||||||
Flags options,
|
Flags options,
|
||||||
Flags selected,
|
Flags selected,
|
||||||
const base::flat_set<not_null<History*>> &peers);
|
const base::flat_set<not_null<History*>> &peers,
|
||||||
|
bool isLocal);
|
||||||
|
|
||||||
[[nodiscard]] Main::Session &session() const override;
|
[[nodiscard]] Main::Session &session() const override;
|
||||||
[[nodiscard]] Flags chosenOptions() const {
|
[[nodiscard]] Flags chosenOptions() const {
|
||||||
|
|
@ -69,6 +70,7 @@ private:
|
||||||
base::flat_set<not_null<History*>> _peers;
|
base::flat_set<not_null<History*>> _peers;
|
||||||
Flags _options;
|
Flags _options;
|
||||||
Flags _selected;
|
Flags _selected;
|
||||||
|
bool _isLocal;
|
||||||
|
|
||||||
Fn<void(PeerListRowId)> _deselectOption;
|
Fn<void(PeerListRowId)> _deselectOption;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/ui_utility.h"
|
#include "ui/ui_utility.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "main/main_account.h"
|
#include "main/main_account.h"
|
||||||
|
#include "mainwidget.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
@ -27,10 +28,13 @@ namespace {
|
||||||
constexpr auto kRefreshSuggestedTimeout = 7200 * crl::time(1000);
|
constexpr auto kRefreshSuggestedTimeout = 7200 * crl::time(1000);
|
||||||
constexpr auto kLoadExceptionsAfter = 100;
|
constexpr auto kLoadExceptionsAfter = 100;
|
||||||
constexpr auto kLoadExceptionsPerRequest = 100;
|
constexpr auto kLoadExceptionsPerRequest = 100;
|
||||||
|
constexpr auto kFiltersLimit = 10;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ChatFilter::ChatFilter(FilterId id) : _id(id) {
|
ChatFilter::ChatFilter(FilterId id, bool isLocal)
|
||||||
|
: _id(id)
|
||||||
|
, _isLocal(isLocal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatFilter::ChatFilter(
|
ChatFilter::ChatFilter(
|
||||||
|
|
@ -41,7 +45,8 @@ ChatFilter::ChatFilter(
|
||||||
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 isDefault,
|
||||||
|
bool isLocal)
|
||||||
: _id(id)
|
: _id(id)
|
||||||
, _title(title)
|
, _title(title)
|
||||||
, _iconEmoji(iconEmoji)
|
, _iconEmoji(iconEmoji)
|
||||||
|
|
@ -49,12 +54,67 @@ ChatFilter::ChatFilter(
|
||||||
, _pinned(std::move(pinned))
|
, _pinned(std::move(pinned))
|
||||||
, _never(std::move(never))
|
, _never(std::move(never))
|
||||||
, _flags(flags)
|
, _flags(flags)
|
||||||
, _isDefault(isDefault) {
|
, _isDefault(isDefault)
|
||||||
|
, _isLocal(isLocal) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatFilter ChatFilter::local(
|
||||||
|
const LocalFolder &data,
|
||||||
|
not_null<Session*> owner) {
|
||||||
|
const auto flags = Flag(data.flags);
|
||||||
|
auto &&to_histories = ranges::view::transform([&](
|
||||||
|
const LocalFolder::Peer &filterPeer) {
|
||||||
|
PeerData *peer = nullptr;
|
||||||
|
|
||||||
|
if (filterPeer.type == LocalFolder::Peer::Type::User) {
|
||||||
|
const auto user = owner->user(filterPeer.id);
|
||||||
|
user->setAccessHash(filterPeer.accessHash);
|
||||||
|
peer = (PeerData *)user;
|
||||||
|
} else if (filterPeer.type == LocalFolder::Peer::Type::Chat) {
|
||||||
|
const auto chat = owner->chat(filterPeer.id);
|
||||||
|
peer = (PeerData *)chat;
|
||||||
|
} else if (filterPeer.type == LocalFolder::Peer::Type::Channel) {
|
||||||
|
const auto channel = owner->channel(filterPeer.id);
|
||||||
|
channel->setAccessHash(filterPeer.accessHash);
|
||||||
|
peer = (PeerData *)channel;
|
||||||
|
}
|
||||||
|
return peer ? owner->history(peer).get() : nullptr;
|
||||||
|
}) | ranges::view::filter([](History *history) {
|
||||||
|
return history != nullptr;
|
||||||
|
}) | ranges::view::transform([](History *history) {
|
||||||
|
return not_null<History*>(history);
|
||||||
|
});
|
||||||
|
auto &&always = ranges::view::concat(
|
||||||
|
data.always
|
||||||
|
) | to_histories;
|
||||||
|
auto pinned = ranges::view::all(
|
||||||
|
data.pinned
|
||||||
|
) | to_histories | ranges::to_vector;
|
||||||
|
auto &&never = ranges::view::all(
|
||||||
|
data.never
|
||||||
|
) | to_histories;
|
||||||
|
auto &&all = ranges::view::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,
|
||||||
|
flags,
|
||||||
|
std::move(list),
|
||||||
|
std::move(pinned),
|
||||||
|
{ never.begin(), never.end() },
|
||||||
|
(data.id == defaultFilterId),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
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))
|
||||||
|
|
@ -110,7 +170,8 @@ ChatFilter ChatFilter::FromTL(
|
||||||
std::move(list),
|
std::move(list),
|
||||||
std::move(pinned),
|
std::move(pinned),
|
||||||
{ never.begin(), never.end() },
|
{ never.begin(), never.end() },
|
||||||
(data.vid().v == defaultFilterId));
|
(data.vid().v == defaultFilterId),
|
||||||
|
isLocal);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,6 +215,85 @@ MTPDialogFilter ChatFilter::tl(FilterId replaceId) const {
|
||||||
MTP_vector<MTPInputPeer>(never));
|
MTP_vector<MTPInputPeer>(never));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LocalFolder ChatFilter::toLocal(int cloudOrder, FilterId replaceId) const {
|
||||||
|
auto always = _always;
|
||||||
|
auto pinned = std::vector<LocalFolder::Peer>();
|
||||||
|
pinned.reserve(_pinned.size());
|
||||||
|
for (const auto history : _pinned) {
|
||||||
|
const auto &peer = history->peer;
|
||||||
|
const auto hash = peer->isChannel()
|
||||||
|
? peer->asChannel()->access
|
||||||
|
: peer->isUser()
|
||||||
|
? peer->asUser()->accessHash()
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
pinned.push_back({
|
||||||
|
.type = history->peer->isChannel()
|
||||||
|
? LocalFolder::Peer::Type::Channel
|
||||||
|
: history->peer->isChat()
|
||||||
|
? LocalFolder::Peer::Type::Chat
|
||||||
|
: LocalFolder::Peer::Type::User,
|
||||||
|
.id = peerToBareInt(peer->id),
|
||||||
|
.accessHash = hash
|
||||||
|
});
|
||||||
|
always.remove(history);
|
||||||
|
}
|
||||||
|
auto include = std::vector<LocalFolder::Peer>();
|
||||||
|
include.reserve(always.size());
|
||||||
|
for (const auto history : always) {
|
||||||
|
const auto &peer = history->peer;
|
||||||
|
const auto hash = peer->isChannel()
|
||||||
|
? peer->asChannel()->access
|
||||||
|
: peer->isUser()
|
||||||
|
? peer->asUser()->accessHash()
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
include.push_back({
|
||||||
|
.type = history->peer->isChannel()
|
||||||
|
? LocalFolder::Peer::Type::Channel
|
||||||
|
: history->peer->isChat()
|
||||||
|
? LocalFolder::Peer::Type::Chat
|
||||||
|
: LocalFolder::Peer::Type::User,
|
||||||
|
.id = peerToBareInt(peer->id),
|
||||||
|
.accessHash = hash
|
||||||
|
});
|
||||||
|
}
|
||||||
|
auto never = std::vector<LocalFolder::Peer>();
|
||||||
|
never.reserve(_never.size());
|
||||||
|
for (const auto history : _never) {
|
||||||
|
const auto &peer = history->peer;
|
||||||
|
const auto hash = peer->isChannel()
|
||||||
|
? peer->asChannel()->access
|
||||||
|
: peer->isUser()
|
||||||
|
? peer->asUser()->accessHash()
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
never.push_back({
|
||||||
|
.type = history->peer->isChannel()
|
||||||
|
? LocalFolder::Peer::Type::Channel
|
||||||
|
: history->peer->isChat()
|
||||||
|
? LocalFolder::Peer::Type::Chat
|
||||||
|
: LocalFolder::Peer::Type::User,
|
||||||
|
.id = peerToBareInt(peer->id),
|
||||||
|
.accessHash = hash
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const auto &session = App::main()->session();
|
||||||
|
return {
|
||||||
|
.id = replaceId ? replaceId : _id,
|
||||||
|
.ownerId = session.mtp().isTestMode()
|
||||||
|
? -session.userId()
|
||||||
|
: session.userId(),
|
||||||
|
.cloudOrder = cloudOrder,
|
||||||
|
.name = _title,
|
||||||
|
.emoticon = _iconEmoji,
|
||||||
|
.always = include,
|
||||||
|
.never = never,
|
||||||
|
.pinned = pinned,
|
||||||
|
.flags = _flags.value()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
FilterId ChatFilter::id() const {
|
FilterId ChatFilter::id() const {
|
||||||
return _id;
|
return _id;
|
||||||
}
|
}
|
||||||
|
|
@ -187,6 +327,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()) {
|
||||||
|
|
@ -207,11 +350,56 @@ 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 ((chat->amCreator() && (_flags & Flag::Owned) && !(_flags & Flag::NotOwned))
|
||||||
|
|| (chat->hasAdminRights() && (_flags & Flag::Admin) && !(_flags & Flag::NotAdmin))
|
||||||
|
|| (!chat->amCreator() && !(_flags & Flag::Owned) && (_flags & Flag::NotOwned))
|
||||||
|
|| (!chat->hasAdminRights() && !(_flags & Flag::Admin) && (_flags & Flag::NotAdmin))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (const auto channel = peer->asChannel()) {
|
||||||
|
if ((channel->amCreator() && (_flags & Flag::Owned) && !(_flags & Flag::NotOwned))
|
||||||
|
|| (channel->hasAdminRights() && (_flags & Flag::Admin) && !(_flags & Flag::NotAdmin))
|
||||||
|
|| (!channel->amCreator() && !(_flags & Flag::Owned) && (_flags & Flag::NotOwned))
|
||||||
|
|| (!channel->hasAdminRights() && !(_flags & Flag::Admin) && (_flags & Flag::NotAdmin))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
};
|
||||||
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->mute()
|
|| !history->mute()
|
||||||
|| (history->hasUnreadMentions()
|
|| (history->hasUnreadMentions()
|
||||||
|
|
@ -223,10 +411,15 @@ bool ChatFilter::contains(not_null<History*> history) const {
|
||||||
|| history->hasUnreadMentions()
|
|| history->hasUnreadMentions()
|
||||||
|| 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) : _owner(owner) {
|
ChatFilters::ChatFilters(not_null<Session*> owner) : _owner(owner) {
|
||||||
crl::on_main(&owner->session(), [=] { load(); });
|
crl::on_main(&owner->session(), [=] { load(); });
|
||||||
}
|
}
|
||||||
|
|
@ -274,10 +467,14 @@ 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 defaultFilterId = account->defaultFilterId();
|
||||||
|
const auto localFilters = cRefLocalFolders();
|
||||||
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) {
|
||||||
|
|
@ -293,7 +490,42 @@ void ChatFilters::received(const QVector<MTPDialogFilter> &list) {
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
++position;
|
++position;
|
||||||
|
};
|
||||||
|
|
||||||
|
// First we're adding cloud filters and corresponding local filters.
|
||||||
|
for (const auto &filter : list) {
|
||||||
|
for (const auto &localFilter : localFilters) {
|
||||||
|
if (!account->isCurrent(localFilter.ownerId)
|
||||||
|
|| localFilter.cloudOrder != originalPosition) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addToList(ChatFilter::local(localFilter, _owner));
|
||||||
|
}
|
||||||
|
addToList(ChatFilter::FromTL(filter, _owner));
|
||||||
|
++originalPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Then we adding local filters, retaining cloud order
|
||||||
|
while (originalPosition < kFiltersLimit) {
|
||||||
|
for (const auto &localFilter : localFilters) {
|
||||||
|
if (!account->isCurrent(localFilter.ownerId)
|
||||||
|
|| localFilter.cloudOrder != originalPosition) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addToList(ChatFilter::local(localFilter, _owner));
|
||||||
|
}
|
||||||
|
++originalPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// And finally we adding other filters
|
||||||
|
for (const auto &localFilter : localFilters) {
|
||||||
|
if (!account->isCurrent(localFilter.ownerId)
|
||||||
|
|| localFilter.cloudOrder < kFiltersLimit) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addToList(ChatFilter::local(localFilter, _owner));
|
||||||
|
}
|
||||||
|
|
||||||
while (position < _list.size()) {
|
while (position < _list.size()) {
|
||||||
applyRemove(position);
|
applyRemove(position);
|
||||||
changed = true;
|
changed = true;
|
||||||
|
|
@ -342,7 +574,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -468,7 +700,7 @@ 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() < ChatFilter::kPinnedLimit) {
|
} else if (always.size() < ChatFilter::kPinnedLimit || i->isLocal()) {
|
||||||
always.insert(history);
|
always.insert(history);
|
||||||
pinned.push_back(history);
|
pinned.push_back(history);
|
||||||
}
|
}
|
||||||
|
|
@ -483,7 +715,8 @@ const ChatFilter &ChatFilters::applyUpdatedPinned(
|
||||||
std::move(always),
|
std::move(always),
|
||||||
std::move(pinned),
|
std::move(pinned),
|
||||||
i->never(),
|
i->never(),
|
||||||
(id == defaultFilterId)));
|
(id == defaultFilterId),
|
||||||
|
i->isLocal()));
|
||||||
return *i;
|
return *i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -498,15 +731,37 @@ void ChatFilters::saveOrder(
|
||||||
|
|
||||||
auto ids = QVector<MTPint>();
|
auto ids = QVector<MTPint>();
|
||||||
ids.reserve(order.size());
|
ids.reserve(order.size());
|
||||||
|
auto cloudIds = QVector<MTPint>();
|
||||||
|
cloudIds.reserve(kFiltersLimit);
|
||||||
|
auto &localFolders = cRefLocalFolders();
|
||||||
|
const auto account = &_owner->session().account();
|
||||||
|
|
||||||
for (const auto id : order) {
|
for (const auto id : order) {
|
||||||
ids.push_back(MTP_int(id));
|
ids.push_back(MTP_int(id));
|
||||||
|
|
||||||
|
const auto i = ranges::find(_list, id, &ChatFilter::id);
|
||||||
|
Assert(i != end(_list));
|
||||||
|
|
||||||
|
if ((*i).isLocal()) {
|
||||||
|
auto j = ranges::find_if(localFolders, [id, account](LocalFolder localFolder) {
|
||||||
|
return (id == localFolder.id
|
||||||
|
&& account->isCurrent(localFolder.ownerId));
|
||||||
|
});
|
||||||
|
(*j).cloudOrder = cloudIds.size();
|
||||||
|
std::rotate(j, j+1, localFolders.end());
|
||||||
|
} 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 {
|
||||||
|
|
@ -628,4 +883,20 @@ rpl::producer<> ChatFilters::suggestedUpdated() const {
|
||||||
return _suggestedUpdated.events();
|
return _suggestedUpdated.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatFilters::saveLocal(FilterId filterId) {
|
||||||
|
const auto i = ranges::find(_list, filterId, &ChatFilter::id);
|
||||||
|
auto &localFolders = cRefLocalFolders();
|
||||||
|
const auto account = &_owner->session().account();
|
||||||
|
const auto j = ranges::find_if(localFolders, [filterId, account](LocalFolder localFolder) {
|
||||||
|
return (filterId == localFolder.id
|
||||||
|
&& account->isCurrent(localFolder.ownerId));
|
||||||
|
});
|
||||||
|
Assert(i != end(_list));
|
||||||
|
Assert(j != end(localFolders));
|
||||||
|
|
||||||
|
const auto cloudOrder = (*j).cloudOrder;
|
||||||
|
|
||||||
|
*j = (*i).toLocal(cloudOrder);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
||||||