diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 9d9141559..7f2bbca40 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -29,7 +29,7 @@ jobs:
QT_PREFIX: "/usr/local/desktop-app/Qt-5.12.5"
OPENSSL_VER: "1_1_1"
OPENSSL_PREFIX: "/usr/local/desktop-app/openssl-1.1.1"
- CMAKE_VER: "3.16.3"
+ CMAKE_VER: "3.17.0"
UPLOAD_ARTIFACT: "false"
ONLY_CACHE: "false"
MANUAL_CACHING: "6"
@@ -40,11 +40,6 @@ jobs:
- name: Get repository name.
run: echo ::set-env name=REPO_NAME::${GITHUB_REPOSITORY##*/}
- - name: Clone.
- uses: actions/checkout@v1
- with:
- submodules: recursive
-
- name: Disable man for further package installs.
run: |
cfgFile="/etc/dpkg/dpkg.cfg.d/no_man"
@@ -57,17 +52,13 @@ jobs:
p locale
p doc
- - name: First set up.
+ - name: Apt install.
shell: bash
run: |
- cd ..
- mv $REPO_NAME temp
- mkdir $REPO_NAME
- mv temp $REPO_NAME/$REPO_NAME
- cd $REPO_NAME
-
sudo apt-get update
sudo apt-get install software-properties-common -y && \
+ sudo add-apt-repository ppa:git-core/ppa -y && \
+ sudo apt-get update && \
sudo apt-get install git libexif-dev liblzma-dev libz-dev libssl-dev \
libgtk2.0-dev libice-dev libsm-dev libicu-dev libdrm-dev dh-autoreconf \
autoconf automake build-essential libxml2-dev libass-dev libfreetype6-dev \
@@ -86,6 +77,15 @@ jobs:
sudo update-alternatives --config gcc && \
sudo add-apt-repository --remove ppa:ubuntu-toolchain-r/test -y
+ - name: Clone.
+ uses: actions/checkout@v2
+ with:
+ submodules: recursive
+ path: ${{ env.REPO_NAME }}
+
+ - name: First set up.
+ shell: bash
+ run: |
gcc --version
gcc --version > CACHE_KEY.txt
@@ -127,6 +127,7 @@ jobs:
sudo mkdir /opt/cmake
sudo sh $file --prefix=/opt/cmake --skip-license
sudo ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake
+ rm $file
cmake --version
@@ -500,11 +501,14 @@ jobs:
echo Define from matrix: $DEFINE
fi
- ./configure.sh -D TDESKTOP_API_TEST=ON -D DESKTOP_APP_USE_PACKAGED=OFF $DEFINE
+ ./configure.sh \
+ -D CMAKE_CXX_FLAGS="-s" \
+ -D TDESKTOP_API_TEST=ON \
+ -D DESKTOP_APP_USE_PACKAGED=OFF \
+ $DEFINE
cd ../out/Debug
make -j$(nproc)
- strip -s bin/Kotatogram
- name: Check.
if: env.ONLY_CACHE == 'false'
diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml
index 896e1061c..88a17b074 100644
--- a/.github/workflows/mac.yml
+++ b/.github/workflows/mac.yml
@@ -42,18 +42,13 @@ jobs:
run: echo ::set-env name=REPO_NAME::${GITHUB_REPOSITORY##*/}
- name: Clone.
- uses: actions/checkout@v1
+ uses: actions/checkout@v2
with:
submodules: recursive
+ path: ${{ env.REPO_NAME }}
- name: First set up.
run: |
- cd ..
- mv $REPO_NAME temp
- mkdir $REPO_NAME
- mv temp $REPO_NAME/$REPO_NAME
- cd $REPO_NAME
-
brew install automake fdk-aac lame libass libtool libvorbis libvpx \
ninja opus sdl shtool texi2html theora x264 xvid yasm pkg-config
diff --git a/.github/workflows/snap.yml b/.github/workflows/snap.yml
index 1dbe5b461..9769045bf 100644
--- a/.github/workflows/snap.yml
+++ b/.github/workflows/snap.yml
@@ -23,33 +23,40 @@ jobs:
steps:
- name: Clone.
- uses: actions/checkout@v1
+ uses: actions/checkout@v2
with:
submodules: recursive
- name: First set up.
run: |
+ # Workaround for Heroku
+ curl https://cli-assets.heroku.com/apt/release.key | sudo apt-key add -
+
sudo apt-get update
sudo apt-get install gcc-8 g++-8 -y
sudo snap install --classic snapcraft
# Workaround for snapcraft
- # See https://forum.snapcraft.io/t/permissions-problem-using-snapcraft-in-azure-pipelines/13258
+ # See https://forum.snapcraft.io/t/13258
sudo chown root:root /
+ md5() {
+ md5cache=$(md5sum $1.txt | cut -c -32)
+ echo ::set-env name=$1::$md5cache
+ }
+ keyFor() {
+ keyName="${1^^}_CACHE_KEY"
+ awk -v RS="" -v ORS="\n\n" '/^ '"$1"':/' snap/snapcraft.yaml > $keyName.txt
+ md5 $keyName
+ }
+
snapcraft --version > CACHE_KEY.txt
gcc-8 --version >> CACHE_KEY.txt
echo $MANUAL_CACHING >> CACHE_KEY.txt
- md5cache=$(md5sum CACHE_KEY.txt | cut -c -32)
- echo ::set-env name=CACHE_KEY::$md5cache
+ md5 CACHE_KEY
- awk -v RS="" -v ORS="\n\n" '/^ cmake:/' snap/snapcraft.yaml > CMAKE_CACHE_KEY.txt
- md5cache=$(md5sum CMAKE_CACHE_KEY.txt | cut -c -32)
- echo ::set-env name=CMAKE_CACHE_KEY::$md5cache
-
- awk -v RS="" -v ORS="\n\n" '/^ ffmpeg:/' snap/snapcraft.yaml > FFMPEG_CACHE_KEY.txt
- md5cache=$(md5sum FFMPEG_CACHE_KEY.txt | cut -c -32)
- echo ::set-env name=FFMPEG_CACHE_KEY::$md5cache
+ keyFor cmake
+ keyFor ffmpeg
- name: CMake cache.
id: cache-cmake
diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml
index 1cb303ffa..6ca279d72 100644
--- a/.github/workflows/win.yml
+++ b/.github/workflows/win.yml
@@ -38,19 +38,14 @@ jobs:
run: echo ::set-env name=REPO_NAME::${GITHUB_REPOSITORY##*/}
- name: Clone.
- uses: actions/checkout@v1
+ uses: actions/checkout@v2
with:
submodules: recursive
+ path: ${{ env.REPO_NAME }}
- name: Set up environment variables.
shell: cmd
run: |
- cd ..
- move %REPO_NAME% temp
- mkdir %REPO_NAME%
- move temp %REPO_NAME%/%REPO_NAME%
- cd %REPO_NAME%
-
echo ::add-path::C:\Strawberry\perl\bin\
echo ::add-path::"%programfiles%\NASM"
@@ -350,6 +345,7 @@ jobs:
- name: Move artifact.
if: env.UPLOAD_ARTIFACT == 'true'
+ shell: cmd
run: |
cd %REPO_NAME%\out\Debug
mkdir artifact
diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 0923dbb19..fec2f5e4a 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -38,6 +38,7 @@ include(cmake/lib_tgvoip.cmake)
set(style_files
boxes/boxes.style
calls/calls.style
+ chat_helpers/chat_helpers.style
dialogs/dialogs.style
export/view/export.style
history/history.style
@@ -49,7 +50,7 @@ set(style_files
passport/passport.style
profile/profile.style
settings/settings.style
- chat_helpers/chat_helpers.style
+ ui/filter_icons.style
window/window.style
)
@@ -134,6 +135,8 @@ nice_target_sources(Telegram ${src_loc}
PRIVATE
${style_files}
+ api/api_chat_filters.cpp
+ api/api_chat_filters.h
api/api_common.h
api/api_hash.h
api/api_self_destruct.cpp
@@ -146,6 +149,10 @@ PRIVATE
api/api_single_message_search.h
api/api_text_entities.cpp
api/api_text_entities.h
+ boxes/filters/edit_filter_box.cpp
+ boxes/filters/edit_filter_box.h
+ boxes/filters/edit_filter_chats_list.cpp
+ boxes/filters/edit_filter_chats_list.h
boxes/peers/add_participants_box.cpp
boxes/peers/add_participants_box.h
boxes/peers/edit_contact_box.cpp
@@ -274,6 +281,8 @@ PRIVATE
chat_helpers/stickers.h
chat_helpers/stickers_emoji_pack.cpp
chat_helpers/stickers_emoji_pack.h
+ chat_helpers/stickers_dice_pack.cpp
+ chat_helpers/stickers_dice_pack.h
chat_helpers/stickers_list_widget.cpp
chat_helpers/stickers_list_widget.h
chat_helpers/tabbed_panel.cpp
@@ -326,6 +335,8 @@ PRIVATE
data/data_auto_download.h
data/data_chat.cpp
data/data_chat.h
+ data/data_chat_filters.cpp
+ data/data_chat_filters.h
data/data_channel.cpp
data/data_channel.h
data/data_channel_admins.cpp
@@ -440,6 +451,8 @@ PRIVATE
history/view/media/history_view_call.cpp
history/view/media/history_view_contact.h
history/view/media/history_view_contact.cpp
+ history/view/media/history_view_dice.h
+ history/view/media/history_view_dice.cpp
history/view/media/history_view_document.h
history/view/media/history_view_document.cpp
history/view/media/history_view_file.h
@@ -833,6 +846,8 @@ PRIVATE
settings/settings_codes.h
settings/settings_common.cpp
settings/settings_common.h
+ settings/settings_folders.cpp
+ settings/settings_folders.h
settings/settings_information.cpp
settings/settings_information.h
settings/settings_intro.cpp
@@ -915,6 +930,10 @@ PRIVATE
ui/countryinput.h
ui/empty_userpic.cpp
ui/empty_userpic.h
+ ui/filter_icons.cpp
+ ui/filter_icons.h
+ ui/filter_icon_panel.cpp
+ ui/filter_icon_panel.h
ui/grouped_layout.cpp
ui/grouped_layout.h
ui/resize_area.h
@@ -943,6 +962,8 @@ PRIVATE
window/window_connecting_widget.h
window/window_controller.cpp
window/window_controller.h
+ window/window_filters_menu.cpp
+ window/window_filters_menu.h
window/window_history_hider.cpp
window/window_history_hider.h
window/window_lock_widgets.cpp
diff --git a/Telegram/Resources/art/dice_idle.tgs b/Telegram/Resources/art/dice_idle.tgs
new file mode 100644
index 000000000..aad33bffe
Binary files /dev/null and b/Telegram/Resources/art/dice_idle.tgs differ
diff --git a/Telegram/Resources/icons/filters/filters_all.png b/Telegram/Resources/icons/filters/filters_all.png
new file mode 100644
index 000000000..9cf16f5e0
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_all.png differ
diff --git a/Telegram/Resources/icons/filters/filters_all@2x.png b/Telegram/Resources/icons/filters/filters_all@2x.png
new file mode 100644
index 000000000..fc14b63b6
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_all@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_all@3x.png b/Telegram/Resources/icons/filters/filters_all@3x.png
new file mode 100644
index 000000000..e1cc8aa77
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_all@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_all_active.png b/Telegram/Resources/icons/filters/filters_all_active.png
new file mode 100644
index 000000000..c2a0fb691
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_all_active.png differ
diff --git a/Telegram/Resources/icons/filters/filters_all_active@2x.png b/Telegram/Resources/icons/filters/filters_all_active@2x.png
new file mode 100644
index 000000000..fa027ea7f
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_all_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_all_active@3x.png b/Telegram/Resources/icons/filters/filters_all_active@3x.png
new file mode 100644
index 000000000..99d77e8c1
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_all_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_bots.png b/Telegram/Resources/icons/filters/filters_bots.png
new file mode 100644
index 000000000..590b15279
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_bots.png differ
diff --git a/Telegram/Resources/icons/filters/filters_bots@2x.png b/Telegram/Resources/icons/filters/filters_bots@2x.png
new file mode 100644
index 000000000..c152af138
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_bots@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_bots@3x.png b/Telegram/Resources/icons/filters/filters_bots@3x.png
new file mode 100644
index 000000000..867e63a7d
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_bots@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_bots_active.png b/Telegram/Resources/icons/filters/filters_bots_active.png
new file mode 100644
index 000000000..3342d8671
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_bots_active.png differ
diff --git a/Telegram/Resources/icons/filters/filters_bots_active@2x.png b/Telegram/Resources/icons/filters/filters_bots_active@2x.png
new file mode 100644
index 000000000..6788eb795
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_bots_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_bots_active@3x.png b/Telegram/Resources/icons/filters/filters_bots_active@3x.png
new file mode 100644
index 000000000..7bf3a689d
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_bots_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_channels.png b/Telegram/Resources/icons/filters/filters_channels.png
new file mode 100644
index 000000000..be77932b8
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_channels.png differ
diff --git a/Telegram/Resources/icons/filters/filters_channels@2x.png b/Telegram/Resources/icons/filters/filters_channels@2x.png
new file mode 100644
index 000000000..697e07daf
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_channels@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_channels@3x.png b/Telegram/Resources/icons/filters/filters_channels@3x.png
new file mode 100644
index 000000000..3dc154a45
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_channels@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_channels_active.png b/Telegram/Resources/icons/filters/filters_channels_active.png
new file mode 100644
index 000000000..202ce0368
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_channels_active.png differ
diff --git a/Telegram/Resources/icons/filters/filters_channels_active@2x.png b/Telegram/Resources/icons/filters/filters_channels_active@2x.png
new file mode 100644
index 000000000..64306644f
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_channels_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_channels_active@3x.png b/Telegram/Resources/icons/filters/filters_channels_active@3x.png
new file mode 100644
index 000000000..c5428670e
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_channels_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_custom.png b/Telegram/Resources/icons/filters/filters_custom.png
new file mode 100644
index 000000000..499de7caa
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_custom.png differ
diff --git a/Telegram/Resources/icons/filters/filters_custom@2x.png b/Telegram/Resources/icons/filters/filters_custom@2x.png
new file mode 100644
index 000000000..2d8ef3270
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_custom@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_custom@3x.png b/Telegram/Resources/icons/filters/filters_custom@3x.png
new file mode 100644
index 000000000..4e0b2a126
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_custom@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_custom_active.png b/Telegram/Resources/icons/filters/filters_custom_active.png
new file mode 100644
index 000000000..740235201
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_custom_active.png differ
diff --git a/Telegram/Resources/icons/filters/filters_custom_active@2x.png b/Telegram/Resources/icons/filters/filters_custom_active@2x.png
new file mode 100644
index 000000000..4a25cbaef
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_custom_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_custom_active@3x.png b/Telegram/Resources/icons/filters/filters_custom_active@3x.png
new file mode 100644
index 000000000..5e696988c
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_custom_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_groups.png b/Telegram/Resources/icons/filters/filters_groups.png
new file mode 100644
index 000000000..24d62f890
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_groups.png differ
diff --git a/Telegram/Resources/icons/filters/filters_groups@2x.png b/Telegram/Resources/icons/filters/filters_groups@2x.png
new file mode 100644
index 000000000..00e515474
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_groups@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_groups@3x.png b/Telegram/Resources/icons/filters/filters_groups@3x.png
new file mode 100644
index 000000000..9f3ff16e5
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_groups@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_groups_active.png b/Telegram/Resources/icons/filters/filters_groups_active.png
new file mode 100644
index 000000000..a5a143946
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_groups_active.png differ
diff --git a/Telegram/Resources/icons/filters/filters_groups_active@2x.png b/Telegram/Resources/icons/filters/filters_groups_active@2x.png
new file mode 100644
index 000000000..6f87b49e4
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_groups_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_groups_active@3x.png b/Telegram/Resources/icons/filters/filters_groups_active@3x.png
new file mode 100644
index 000000000..342825ff2
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_groups_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_private.png b/Telegram/Resources/icons/filters/filters_private.png
new file mode 100644
index 000000000..68924026e
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_private.png differ
diff --git a/Telegram/Resources/icons/filters/filters_private@2x.png b/Telegram/Resources/icons/filters/filters_private@2x.png
new file mode 100644
index 000000000..c39dc8d74
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_private@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_private@3x.png b/Telegram/Resources/icons/filters/filters_private@3x.png
new file mode 100644
index 000000000..42ce50320
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_private@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_private_active.png b/Telegram/Resources/icons/filters/filters_private_active.png
new file mode 100644
index 000000000..912ea878d
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_private_active.png differ
diff --git a/Telegram/Resources/icons/filters/filters_private_active@2x.png b/Telegram/Resources/icons/filters/filters_private_active@2x.png
new file mode 100644
index 000000000..c83948ad4
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_private_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_private_active@3x.png b/Telegram/Resources/icons/filters/filters_private_active@3x.png
new file mode 100644
index 000000000..2bf50ef6e
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_private_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_setup.png b/Telegram/Resources/icons/filters/filters_setup.png
new file mode 100644
index 000000000..49602582f
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_setup.png differ
diff --git a/Telegram/Resources/icons/filters/filters_setup@2x.png b/Telegram/Resources/icons/filters/filters_setup@2x.png
new file mode 100644
index 000000000..519f77fef
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_setup@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_setup@3x.png b/Telegram/Resources/icons/filters/filters_setup@3x.png
new file mode 100644
index 000000000..87ab4420c
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_setup@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_archived.png b/Telegram/Resources/icons/filters/filters_type_archived.png
new file mode 100644
index 000000000..790d50335
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_archived.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_archived@2x.png b/Telegram/Resources/icons/filters/filters_type_archived@2x.png
new file mode 100644
index 000000000..342c6cb55
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_archived@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_archived@3x.png b/Telegram/Resources/icons/filters/filters_type_archived@3x.png
new file mode 100644
index 000000000..f20b8b610
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_archived@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_bots.png b/Telegram/Resources/icons/filters/filters_type_bots.png
new file mode 100644
index 000000000..34d2ed00a
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_bots.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_bots@2x.png b/Telegram/Resources/icons/filters/filters_type_bots@2x.png
new file mode 100644
index 000000000..1f37ff794
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_bots@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_bots@3x.png b/Telegram/Resources/icons/filters/filters_type_bots@3x.png
new file mode 100644
index 000000000..afc773a3c
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_bots@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_channels.png b/Telegram/Resources/icons/filters/filters_type_channels.png
new file mode 100644
index 000000000..e43f40012
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_channels.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_channels@2x.png b/Telegram/Resources/icons/filters/filters_type_channels@2x.png
new file mode 100644
index 000000000..cf3804750
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_channels@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_channels@3x.png b/Telegram/Resources/icons/filters/filters_type_channels@3x.png
new file mode 100644
index 000000000..3e8d01368
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_channels@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_contacts.png b/Telegram/Resources/icons/filters/filters_type_contacts.png
new file mode 100644
index 000000000..1cfb2a575
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_contacts.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_contacts@2x.png b/Telegram/Resources/icons/filters/filters_type_contacts@2x.png
new file mode 100644
index 000000000..6c81c14ea
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_contacts@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_contacts@3x.png b/Telegram/Resources/icons/filters/filters_type_contacts@3x.png
new file mode 100644
index 000000000..f3ac2f12a
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_contacts@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_groups.png b/Telegram/Resources/icons/filters/filters_type_groups.png
new file mode 100644
index 000000000..c66addebf
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_groups.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_groups@2x.png b/Telegram/Resources/icons/filters/filters_type_groups@2x.png
new file mode 100644
index 000000000..114156e54
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_groups@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_groups@3x.png b/Telegram/Resources/icons/filters/filters_type_groups@3x.png
new file mode 100644
index 000000000..fcaffe853
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_groups@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_muted.png b/Telegram/Resources/icons/filters/filters_type_muted.png
new file mode 100644
index 000000000..92e3b1adc
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_muted.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_muted@2x.png b/Telegram/Resources/icons/filters/filters_type_muted@2x.png
new file mode 100644
index 000000000..1dd51e26c
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_muted@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_muted@3x.png b/Telegram/Resources/icons/filters/filters_type_muted@3x.png
new file mode 100644
index 000000000..0f0f81dbb
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_muted@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_noncontacts.png b/Telegram/Resources/icons/filters/filters_type_noncontacts.png
new file mode 100644
index 000000000..213633ac0
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_noncontacts.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_noncontacts@2x.png b/Telegram/Resources/icons/filters/filters_type_noncontacts@2x.png
new file mode 100644
index 000000000..f1f7695b6
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_noncontacts@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_noncontacts@3x.png b/Telegram/Resources/icons/filters/filters_type_noncontacts@3x.png
new file mode 100644
index 000000000..f5a58c269
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_noncontacts@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_read.png b/Telegram/Resources/icons/filters/filters_type_read.png
new file mode 100644
index 000000000..689da4ca7
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_read.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_read@2x.png b/Telegram/Resources/icons/filters/filters_type_read@2x.png
new file mode 100644
index 000000000..f874a3c28
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_read@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_type_read@3x.png b/Telegram/Resources/icons/filters/filters_type_read@3x.png
new file mode 100644
index 000000000..b2a8feea1
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_type_read@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_unmuted.png b/Telegram/Resources/icons/filters/filters_unmuted.png
new file mode 100644
index 000000000..819d3f7ec
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_unmuted.png differ
diff --git a/Telegram/Resources/icons/filters/filters_unmuted@2x.png b/Telegram/Resources/icons/filters/filters_unmuted@2x.png
new file mode 100644
index 000000000..3244112e8
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_unmuted@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_unmuted@3x.png b/Telegram/Resources/icons/filters/filters_unmuted@3x.png
new file mode 100644
index 000000000..113455c3e
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_unmuted@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_unmuted_active.png b/Telegram/Resources/icons/filters/filters_unmuted_active.png
new file mode 100644
index 000000000..39da6995d
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_unmuted_active.png differ
diff --git a/Telegram/Resources/icons/filters/filters_unmuted_active@2x.png b/Telegram/Resources/icons/filters/filters_unmuted_active@2x.png
new file mode 100644
index 000000000..1613a7ef3
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_unmuted_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_unmuted_active@3x.png b/Telegram/Resources/icons/filters/filters_unmuted_active@3x.png
new file mode 100644
index 000000000..1cb402100
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_unmuted_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_unread.png b/Telegram/Resources/icons/filters/filters_unread.png
new file mode 100644
index 000000000..1ed90a352
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_unread.png differ
diff --git a/Telegram/Resources/icons/filters/filters_unread@2x.png b/Telegram/Resources/icons/filters/filters_unread@2x.png
new file mode 100644
index 000000000..907daf84a
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_unread@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_unread@3x.png b/Telegram/Resources/icons/filters/filters_unread@3x.png
new file mode 100644
index 000000000..c6577a20c
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_unread@3x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_unread_active.png b/Telegram/Resources/icons/filters/filters_unread_active.png
new file mode 100644
index 000000000..b7169765c
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_unread_active.png differ
diff --git a/Telegram/Resources/icons/filters/filters_unread_active@2x.png b/Telegram/Resources/icons/filters/filters_unread_active@2x.png
new file mode 100644
index 000000000..38bcba696
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_unread_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/filters_unread_active@3x.png b/Telegram/Resources/icons/filters/filters_unread_active@3x.png
new file mode 100644
index 000000000..99bd33737
Binary files /dev/null and b/Telegram/Resources/icons/filters/filters_unread_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_cat.png b/Telegram/Resources/icons/filters/folders_cat.png
new file mode 100644
index 000000000..67748a1f6
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_cat.png differ
diff --git a/Telegram/Resources/icons/filters/folders_cat@2x.png b/Telegram/Resources/icons/filters/folders_cat@2x.png
new file mode 100644
index 000000000..aa08a4b4c
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_cat@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_cat@3x.png b/Telegram/Resources/icons/filters/folders_cat@3x.png
new file mode 100644
index 000000000..ef978ea92
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_cat@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_cat_active.png b/Telegram/Resources/icons/filters/folders_cat_active.png
new file mode 100644
index 000000000..6f9e33981
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_cat_active.png differ
diff --git a/Telegram/Resources/icons/filters/folders_cat_active@2x.png b/Telegram/Resources/icons/filters/folders_cat_active@2x.png
new file mode 100644
index 000000000..012424ade
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_cat_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_cat_active@3x.png b/Telegram/Resources/icons/filters/folders_cat_active@3x.png
new file mode 100644
index 000000000..13ed57f1d
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_cat_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_crown.png b/Telegram/Resources/icons/filters/folders_crown.png
new file mode 100644
index 000000000..ab4a48567
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_crown.png differ
diff --git a/Telegram/Resources/icons/filters/folders_crown@2x.png b/Telegram/Resources/icons/filters/folders_crown@2x.png
new file mode 100644
index 000000000..a091297e7
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_crown@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_crown@3x.png b/Telegram/Resources/icons/filters/folders_crown@3x.png
new file mode 100644
index 000000000..b7d609191
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_crown@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_crown_active.png b/Telegram/Resources/icons/filters/folders_crown_active.png
new file mode 100644
index 000000000..f14e27d9a
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_crown_active.png differ
diff --git a/Telegram/Resources/icons/filters/folders_crown_active@2x.png b/Telegram/Resources/icons/filters/folders_crown_active@2x.png
new file mode 100644
index 000000000..be26b69d7
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_crown_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_crown_active@3x.png b/Telegram/Resources/icons/filters/folders_crown_active@3x.png
new file mode 100644
index 000000000..0399765e7
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_crown_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_favorite.png b/Telegram/Resources/icons/filters/folders_favorite.png
new file mode 100644
index 000000000..400fb9612
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_favorite.png differ
diff --git a/Telegram/Resources/icons/filters/folders_favorite@2x.png b/Telegram/Resources/icons/filters/folders_favorite@2x.png
new file mode 100644
index 000000000..36102a2b5
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_favorite@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_favorite@3x.png b/Telegram/Resources/icons/filters/folders_favorite@3x.png
new file mode 100644
index 000000000..117bd86c5
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_favorite@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_favorite_active.png b/Telegram/Resources/icons/filters/folders_favorite_active.png
new file mode 100644
index 000000000..e45aa53a4
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_favorite_active.png differ
diff --git a/Telegram/Resources/icons/filters/folders_favorite_active@2x.png b/Telegram/Resources/icons/filters/folders_favorite_active@2x.png
new file mode 100644
index 000000000..faebedbba
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_favorite_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_favorite_active@3x.png b/Telegram/Resources/icons/filters/folders_favorite_active@3x.png
new file mode 100644
index 000000000..88d4504ac
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_favorite_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_flower.png b/Telegram/Resources/icons/filters/folders_flower.png
new file mode 100644
index 000000000..3c452c82a
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_flower.png differ
diff --git a/Telegram/Resources/icons/filters/folders_flower@2x.png b/Telegram/Resources/icons/filters/folders_flower@2x.png
new file mode 100644
index 000000000..e6ad0af47
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_flower@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_flower@3x.png b/Telegram/Resources/icons/filters/folders_flower@3x.png
new file mode 100644
index 000000000..a143804cc
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_flower@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_flower_active.png b/Telegram/Resources/icons/filters/folders_flower_active.png
new file mode 100644
index 000000000..598554c47
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_flower_active.png differ
diff --git a/Telegram/Resources/icons/filters/folders_flower_active@2x.png b/Telegram/Resources/icons/filters/folders_flower_active@2x.png
new file mode 100644
index 000000000..d4d897d0b
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_flower_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_flower_active@3x.png b/Telegram/Resources/icons/filters/folders_flower_active@3x.png
new file mode 100644
index 000000000..ecb2169eb
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_flower_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_game.png b/Telegram/Resources/icons/filters/folders_game.png
new file mode 100644
index 000000000..69119b7d9
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_game.png differ
diff --git a/Telegram/Resources/icons/filters/folders_game@2x.png b/Telegram/Resources/icons/filters/folders_game@2x.png
new file mode 100644
index 000000000..09fd05fe4
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_game@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_game@3x.png b/Telegram/Resources/icons/filters/folders_game@3x.png
new file mode 100644
index 000000000..84e31f533
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_game@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_game_active.png b/Telegram/Resources/icons/filters/folders_game_active.png
new file mode 100644
index 000000000..37ed6f20a
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_game_active.png differ
diff --git a/Telegram/Resources/icons/filters/folders_game_active@2x.png b/Telegram/Resources/icons/filters/folders_game_active@2x.png
new file mode 100644
index 000000000..5e6d6edc1
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_game_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_game_active@3x.png b/Telegram/Resources/icons/filters/folders_game_active@3x.png
new file mode 100644
index 000000000..594930468
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_game_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_home.png b/Telegram/Resources/icons/filters/folders_home.png
new file mode 100644
index 000000000..59ef27c92
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_home.png differ
diff --git a/Telegram/Resources/icons/filters/folders_home@2x.png b/Telegram/Resources/icons/filters/folders_home@2x.png
new file mode 100644
index 000000000..4ec5954c7
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_home@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_home@3x.png b/Telegram/Resources/icons/filters/folders_home@3x.png
new file mode 100644
index 000000000..52e147732
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_home@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_home_active.png b/Telegram/Resources/icons/filters/folders_home_active.png
new file mode 100644
index 000000000..cfd99be56
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_home_active.png differ
diff --git a/Telegram/Resources/icons/filters/folders_home_active@2x.png b/Telegram/Resources/icons/filters/folders_home_active@2x.png
new file mode 100644
index 000000000..55aee99ed
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_home_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_home_active@3x.png b/Telegram/Resources/icons/filters/folders_home_active@3x.png
new file mode 100644
index 000000000..6d148e115
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_home_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_love.png b/Telegram/Resources/icons/filters/folders_love.png
new file mode 100644
index 000000000..701cd807f
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_love.png differ
diff --git a/Telegram/Resources/icons/filters/folders_love@2x.png b/Telegram/Resources/icons/filters/folders_love@2x.png
new file mode 100644
index 000000000..27e5a1dc9
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_love@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_love@3x.png b/Telegram/Resources/icons/filters/folders_love@3x.png
new file mode 100644
index 000000000..41b44363e
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_love@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_love_active.png b/Telegram/Resources/icons/filters/folders_love_active.png
new file mode 100644
index 000000000..cea3b50dc
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_love_active.png differ
diff --git a/Telegram/Resources/icons/filters/folders_love_active@2x.png b/Telegram/Resources/icons/filters/folders_love_active@2x.png
new file mode 100644
index 000000000..55997b62d
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_love_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_love_active@3x.png b/Telegram/Resources/icons/filters/folders_love_active@3x.png
new file mode 100644
index 000000000..b1477710a
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_love_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_mask.png b/Telegram/Resources/icons/filters/folders_mask.png
new file mode 100644
index 000000000..86dc6c336
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_mask.png differ
diff --git a/Telegram/Resources/icons/filters/folders_mask@2x.png b/Telegram/Resources/icons/filters/folders_mask@2x.png
new file mode 100644
index 000000000..42fa98a5d
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_mask@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_mask@3x.png b/Telegram/Resources/icons/filters/folders_mask@3x.png
new file mode 100644
index 000000000..14eb2ded4
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_mask@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_mask_active.png b/Telegram/Resources/icons/filters/folders_mask_active.png
new file mode 100644
index 000000000..837922a11
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_mask_active.png differ
diff --git a/Telegram/Resources/icons/filters/folders_mask_active@2x.png b/Telegram/Resources/icons/filters/folders_mask_active@2x.png
new file mode 100644
index 000000000..9d7918a6a
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_mask_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_mask_active@3x.png b/Telegram/Resources/icons/filters/folders_mask_active@3x.png
new file mode 100644
index 000000000..9c36ab932
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_mask_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_party.png b/Telegram/Resources/icons/filters/folders_party.png
new file mode 100644
index 000000000..144a8600d
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_party.png differ
diff --git a/Telegram/Resources/icons/filters/folders_party@2x.png b/Telegram/Resources/icons/filters/folders_party@2x.png
new file mode 100644
index 000000000..4c4d99218
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_party@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_party@3x.png b/Telegram/Resources/icons/filters/folders_party@3x.png
new file mode 100644
index 000000000..652338b07
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_party@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_party_active.png b/Telegram/Resources/icons/filters/folders_party_active.png
new file mode 100644
index 000000000..16ab0fcfb
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_party_active.png differ
diff --git a/Telegram/Resources/icons/filters/folders_party_active@2x.png b/Telegram/Resources/icons/filters/folders_party_active@2x.png
new file mode 100644
index 000000000..e12213328
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_party_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_party_active@3x.png b/Telegram/Resources/icons/filters/folders_party_active@3x.png
new file mode 100644
index 000000000..123d39f2a
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_party_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_sport.png b/Telegram/Resources/icons/filters/folders_sport.png
new file mode 100644
index 000000000..b59aa62a3
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_sport.png differ
diff --git a/Telegram/Resources/icons/filters/folders_sport@2x.png b/Telegram/Resources/icons/filters/folders_sport@2x.png
new file mode 100644
index 000000000..16d7bdf57
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_sport@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_sport@3x.png b/Telegram/Resources/icons/filters/folders_sport@3x.png
new file mode 100644
index 000000000..422af43e6
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_sport@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_sport_active.png b/Telegram/Resources/icons/filters/folders_sport_active.png
new file mode 100644
index 000000000..c3581bc34
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_sport_active.png differ
diff --git a/Telegram/Resources/icons/filters/folders_sport_active@2x.png b/Telegram/Resources/icons/filters/folders_sport_active@2x.png
new file mode 100644
index 000000000..50138d104
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_sport_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_sport_active@3x.png b/Telegram/Resources/icons/filters/folders_sport_active@3x.png
new file mode 100644
index 000000000..f4bdb1f09
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_sport_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_study.png b/Telegram/Resources/icons/filters/folders_study.png
new file mode 100644
index 000000000..dbbb4cc2f
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_study.png differ
diff --git a/Telegram/Resources/icons/filters/folders_study@2x.png b/Telegram/Resources/icons/filters/folders_study@2x.png
new file mode 100644
index 000000000..96b281491
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_study@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_study@3x.png b/Telegram/Resources/icons/filters/folders_study@3x.png
new file mode 100644
index 000000000..c76dc5cf2
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_study@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_study_active.png b/Telegram/Resources/icons/filters/folders_study_active.png
new file mode 100644
index 000000000..379d32854
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_study_active.png differ
diff --git a/Telegram/Resources/icons/filters/folders_study_active@2x.png b/Telegram/Resources/icons/filters/folders_study_active@2x.png
new file mode 100644
index 000000000..4ba6d66b9
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_study_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_study_active@3x.png b/Telegram/Resources/icons/filters/folders_study_active@3x.png
new file mode 100644
index 000000000..dff4822fe
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_study_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_trade.png b/Telegram/Resources/icons/filters/folders_trade.png
new file mode 100644
index 000000000..62cbae9a8
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_trade.png differ
diff --git a/Telegram/Resources/icons/filters/folders_trade@2x.png b/Telegram/Resources/icons/filters/folders_trade@2x.png
new file mode 100644
index 000000000..db435b6eb
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_trade@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_trade@3x.png b/Telegram/Resources/icons/filters/folders_trade@3x.png
new file mode 100644
index 000000000..c854a9497
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_trade@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_travel.png b/Telegram/Resources/icons/filters/folders_travel.png
new file mode 100644
index 000000000..886fcaf53
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_travel.png differ
diff --git a/Telegram/Resources/icons/filters/folders_travel@2x.png b/Telegram/Resources/icons/filters/folders_travel@2x.png
new file mode 100644
index 000000000..e5df48b95
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_travel@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_travel@3x.png b/Telegram/Resources/icons/filters/folders_travel@3x.png
new file mode 100644
index 000000000..0897aeee3
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_travel@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_travel_active.png b/Telegram/Resources/icons/filters/folders_travel_active.png
new file mode 100644
index 000000000..7774ca025
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_travel_active.png differ
diff --git a/Telegram/Resources/icons/filters/folders_travel_active@2x.png b/Telegram/Resources/icons/filters/folders_travel_active@2x.png
new file mode 100644
index 000000000..059b6b0ff
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_travel_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_travel_active@3x.png b/Telegram/Resources/icons/filters/folders_travel_active@3x.png
new file mode 100644
index 000000000..2a5b35492
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_travel_active@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_work.png b/Telegram/Resources/icons/filters/folders_work.png
new file mode 100644
index 000000000..9be9132ee
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_work.png differ
diff --git a/Telegram/Resources/icons/filters/folders_work@2x.png b/Telegram/Resources/icons/filters/folders_work@2x.png
new file mode 100644
index 000000000..a70a41ac1
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_work@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_work@3x.png b/Telegram/Resources/icons/filters/folders_work@3x.png
new file mode 100644
index 000000000..91d9b99cb
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_work@3x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_work_active.png b/Telegram/Resources/icons/filters/folders_work_active.png
new file mode 100644
index 000000000..26fceb1a0
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_work_active.png differ
diff --git a/Telegram/Resources/icons/filters/folders_work_active@2x.png b/Telegram/Resources/icons/filters/folders_work_active@2x.png
new file mode 100644
index 000000000..2dbd8b3f0
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_work_active@2x.png differ
diff --git a/Telegram/Resources/icons/filters/folders_work_active@3x.png b/Telegram/Resources/icons/filters/folders_work_active@3x.png
new file mode 100644
index 000000000..c4391725c
Binary files /dev/null and b/Telegram/Resources/icons/filters/folders_work_active@3x.png differ
diff --git a/Telegram/Resources/icons/info_media_delete.png b/Telegram/Resources/icons/info_media_delete.png
index 0898e3791..5702d982e 100644
Binary files a/Telegram/Resources/icons/info_media_delete.png and b/Telegram/Resources/icons/info_media_delete.png differ
diff --git a/Telegram/Resources/icons/info_media_delete@2x.png b/Telegram/Resources/icons/info_media_delete@2x.png
index 3b3b3fc33..c33801c8f 100644
Binary files a/Telegram/Resources/icons/info_media_delete@2x.png and b/Telegram/Resources/icons/info_media_delete@2x.png differ
diff --git a/Telegram/Resources/icons/info_media_delete@3x.png b/Telegram/Resources/icons/info_media_delete@3x.png
index 425d12bdb..d4a97c6ae 100644
Binary files a/Telegram/Resources/icons/info_media_delete@3x.png and b/Telegram/Resources/icons/info_media_delete@3x.png differ
diff --git a/Telegram/Resources/icons/info_media_forward.png b/Telegram/Resources/icons/info_media_forward.png
index c1cff1fc5..8726f1802 100644
Binary files a/Telegram/Resources/icons/info_media_forward.png and b/Telegram/Resources/icons/info_media_forward.png differ
diff --git a/Telegram/Resources/icons/info_media_forward@2x.png b/Telegram/Resources/icons/info_media_forward@2x.png
index 8f879ebd0..a141387a5 100644
Binary files a/Telegram/Resources/icons/info_media_forward@2x.png and b/Telegram/Resources/icons/info_media_forward@2x.png differ
diff --git a/Telegram/Resources/icons/info_media_forward@3x.png b/Telegram/Resources/icons/info_media_forward@3x.png
index b623b6a86..89dbab915 100644
Binary files a/Telegram/Resources/icons/info_media_forward@3x.png and b/Telegram/Resources/icons/info_media_forward@3x.png differ
diff --git a/Telegram/Resources/icons/settings_folders.png b/Telegram/Resources/icons/settings_folders.png
new file mode 100644
index 000000000..bb941b3e2
Binary files /dev/null and b/Telegram/Resources/icons/settings_folders.png differ
diff --git a/Telegram/Resources/icons/settings_folders@2x.png b/Telegram/Resources/icons/settings_folders@2x.png
new file mode 100644
index 000000000..468d20aee
Binary files /dev/null and b/Telegram/Resources/icons/settings_folders@2x.png differ
diff --git a/Telegram/Resources/icons/settings_folders@3x.png b/Telegram/Resources/icons/settings_folders@3x.png
new file mode 100644
index 000000000..9300c2dc6
Binary files /dev/null and b/Telegram/Resources/icons/settings_folders@3x.png differ
diff --git a/Telegram/Resources/icons/stickers_remove.png b/Telegram/Resources/icons/stickers_remove.png
deleted file mode 100644
index 8a98e3293..000000000
Binary files a/Telegram/Resources/icons/stickers_remove.png and /dev/null differ
diff --git a/Telegram/Resources/icons/stickers_remove@2x.png b/Telegram/Resources/icons/stickers_remove@2x.png
deleted file mode 100644
index d9f22acd2..000000000
Binary files a/Telegram/Resources/icons/stickers_remove@2x.png and /dev/null differ
diff --git a/Telegram/Resources/icons/stickers_remove@3x.png b/Telegram/Resources/icons/stickers_remove@3x.png
deleted file mode 100644
index ce5bd3a25..000000000
Binary files a/Telegram/Resources/icons/stickers_remove@3x.png and /dev/null differ
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 67046818f..0bfba6cd6 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -250,6 +250,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_dlg_new_channel_name" = "Channel name";
"lng_no_contacts" = "You have no contacts";
"lng_no_chats" = "Your chats will be here";
+"lng_no_chats_filter" = "No chats currently belong to this folder.";
"lng_contacts_loading" = "Loading...";
"lng_contacts_not_found" = "No contacts found";
"lng_dlg_search_for_messages" = "Search for messages";
@@ -344,6 +345,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_send_ctrlenter" = "Send by Ctrl+Enter";
"lng_settings_send_cmdenter" = "Send by Cmd+Enter";
+"lng_settings_section_filters" = "Folders";
+
"lng_settings_section_background" = "Chat background";
"lng_settings_bg_use_default" = "Use default color theme";
"lng_settings_bg_from_gallery" = "Choose from gallery";
@@ -1839,6 +1842,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_admin_log_filter_all_admins" = "All users and admins";
"lng_admin_log_about" = "What is this?";
"lng_admin_log_about_text" = "This is a list of all service actions taken by the group's members and admins in the last 48 hours.";
+"lng_admin_log_about_text_channel" = "This is a list of all service actions taken by the channel's admins in the last 48 hours.";
"lng_admin_log_no_results_title" = "No actions found";
"lng_admin_log_no_results_text" = "No recent actions that match your query have been found.";
"lng_admin_log_no_results_search_text" = "No recent actions that contain '{query}' have been found.";
@@ -2228,6 +2232,62 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_outdated_soon" = "Otherwise, Telegram Desktop will stop updating on {date}.";
"lng_outdated_now" = "So that Telegram Desktop can update to newer versions.";
+"lng_filters_all" = "All chats";
+"lng_filters_setup" = "Edit";
+"lng_filters_title" = "Folders";
+"lng_filters_subtitle" = "My folders";
+"lng_filters_no_chats" = "No chats";
+"lng_filters_chats_count#one" = "{count} chat";
+"lng_filters_chats_count#other" = "{count} chats";
+"lng_filters_create" = "Create new folder";
+"lng_filters_about" = "Create folders for different groups of chats and quickly switch between them.";
+"lng_filters_recommended" = "Recommended folders";
+"lng_filters_recommended_add" = "Add";
+"lng_filters_restore" = "Undo";
+"lng_filters_new" = "New Folder";
+"lng_filters_edit" = "Edit Folder";
+"lng_filters_new_name" = "Folder name";
+"lng_filters_add_chats" = "Add chats";
+"lng_filters_remove_chats" = "Remove chats";
+"lng_filters_include" = "Included Chats";
+"lng_filters_include_about" = "Choose chats and types of chats that will appear in this folder.";
+"lng_filters_exclude" = "Excluded Chats";
+"lng_filters_exclude_about" = "Choose chats and types of chats that will never appear in this folder.";
+"lng_filters_create_button" = "Create";
+"lng_filters_include_title" = "Include Chats";
+"lng_filters_exclude_title" = "Exclude Chats";
+"lng_filters_edit_types" = "Chat types";
+"lng_filters_edit_chats" = "Chats";
+"lng_filters_include_contacts" = "Contacts";
+"lng_filters_include_non_contacts" = "Non-Contacts";
+"lng_filters_include_groups" = "Groups";
+"lng_filters_include_channels" = "Channels";
+"lng_filters_include_bots" = "Bots";
+"lng_filters_exclude_muted" = "Muted";
+"lng_filters_exclude_read" = "Read";
+"lng_filters_exclude_archived" = "Archived";
+"lng_filters_name_people" = "People";
+"lng_filters_name_unread" = "Unread";
+"lng_filters_name_unmuted" = "Unmuted";
+"lng_filters_add" = "Done";
+"lng_filters_limit" = "Sorry, you have reached folders limit.";
+"lng_filters_empty" = "Please choose at least one chat for this folder.";
+"lng_filters_default" = "Please change at least one rule for this folder.";
+"lng_filters_type_contacts" = "Contacts";
+"lng_filters_type_non_contacts" = "Non-Contacts";
+"lng_filters_type_groups" = "Groups";
+"lng_filters_type_channels" = "Channels";
+"lng_filters_type_bots" = "Bots";
+"lng_filters_type_no_archived" = "Archived";
+"lng_filters_type_no_muted" = "Muted";
+"lng_filters_type_no_read" = "Read";
+"lng_filters_icon_header" = "Choose an icon";
+"lng_filters_error_pinned_max" = "Sorry, you can't pin any more chats to the top.";
+"lng_filters_context_edit" = "Edit Folder";
+"lng_filters_context_remove" = "Remove";
+"lng_filters_remove_sure" = "This will remove the folder, your chats will not be deleted.";
+"lng_filters_remove_yes" = "Remove";
+
// Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program...";
@@ -2400,4 +2460,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"ktg_settings_recent_stickers_limit#other" = "Recent stickers: show {count} stickers";
"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_make_default" = "Make folder default";
+
// Keys finished
diff --git a/Telegram/Resources/langs/rewrites/ru.json b/Telegram/Resources/langs/rewrites/ru.json
index 09ab40c1a..ef61e45ee 100644
--- a/Telegram/Resources/langs/rewrites/ru.json
+++ b/Telegram/Resources/langs/rewrites/ru.json
@@ -97,5 +97,8 @@
"many": "Недавние стикеры: показывать {count} стикеров",
"other": "Недавние стикеры: показывать {count} стикеров"
},
- "ktg_settings_recent_stickers_limit_none": "Недавние стикеры: скрыть все"
+ "ktg_settings_recent_stickers_limit_none": "Недавние стикеры: скрыть все",
+ "ktg_filters_default": "Папка по умолчанию",
+ "ktg_filters_context_edit_all": "Изменить папки",
+ "ktg_filters_context_make_default": "Сделать папкой по умолчанию"
}
diff --git a/Telegram/Resources/night-green.tdesktop-theme b/Telegram/Resources/night-green.tdesktop-theme
index 2a47a1c14..6158491ac 100644
Binary files a/Telegram/Resources/night-green.tdesktop-theme and b/Telegram/Resources/night-green.tdesktop-theme differ
diff --git a/Telegram/Resources/night.tdesktop-theme b/Telegram/Resources/night.tdesktop-theme
index c9d188ec1..6a557e631 100644
Binary files a/Telegram/Resources/night.tdesktop-theme and b/Telegram/Resources/night.tdesktop-theme differ
diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc
index daacdb128..32cc459e5 100644
--- a/Telegram/Resources/qrc/telegram/telegram.qrc
+++ b/Telegram/Resources/qrc/telegram/telegram.qrc
@@ -57,6 +57,7 @@
../../art/logo_256_no_margin_red.png
../../art/logo_256_no_margin_old.png
../../art/sunrise.jpg
+ ../../art/dice_idle.tgs
../../day-blue.tdesktop-theme
../../night.tdesktop-theme
../../night-green.tdesktop-theme
diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl
index a7cddc5c3..8a87a4424 100644
--- a/Telegram/Resources/tl/api.tl
+++ b/Telegram/Resources/tl/api.tl
@@ -72,6 +72,7 @@ inputMediaGame#d33f43f3 id:InputGame = InputMedia;
inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia;
inputMediaGeoLive#ce4e82fd flags:# stopped:flags.0?true geo_point:InputGeoPoint period:flags.1?int = InputMedia;
inputMediaPoll#abe9ca25 flags:# poll:Poll correct_answers:flags.0?Vector = InputMedia;
+inputMediaDice#aeffa807 = InputMedia;
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto;
@@ -128,7 +129,7 @@ channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#1b7c9db3 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
-channelFull#2d895c74 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true has_scheduled:flags.19?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int pts:int = ChatFull;
+channelFull#f0e6672a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true has_scheduled:flags.19?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int = ChatFull;
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
@@ -156,6 +157,7 @@ messageMediaGame#fdb19008 game:Game = MessageMedia;
messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia;
messageMediaGeoLive#7c3c2609 geo:GeoPoint period:int = MessageMedia;
messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
+messageMediaDice#638fe46b value:int = MessageMedia;
messageActionEmpty#b6aef7b0 = MessageAction;
messageActionChatCreate#a6638b9a title:string users:Vector = MessageAction;
@@ -352,6 +354,9 @@ updateTheme#8216fba3 theme:Theme = Update;
updateGeoLiveViewed#871fb939 peer:Peer msg_id:int = Update;
updateLoginToken#564fe691 = Update;
updateMessagePollVote#42f88f2c poll_id:long user_id:int options:Vector = Update;
+updateDialogFilter#26ffde7d flags:# id:int filter:flags.0?DialogFilter = Update;
+updateDialogFilterOrder#a5d72105 order:Vector = Update;
+updateDialogFilters#3504914f = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@@ -502,7 +507,7 @@ messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMess
webPageEmpty#eb1477e8 id:long = WebPage;
webPagePending#c586da1c id:long date:int = WebPage;
webPage#e89c45b2 flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page attributes:flags.12?Vector = WebPage;
-webPageNotModified#85849473 = WebPage;
+webPageNotModified#7311ca11 flags:# cached_page_views:flags.0?int = WebPage;
authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization;
@@ -528,6 +533,7 @@ inputStickerSetEmpty#ffb62b95 = InputStickerSet;
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
+inputStickerSetDice#79e21a53 = InputStickerSet;
stickerSet#eeb46f27 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize thumb_dc_id:flags.4?int count:int hash:int = StickerSet;
@@ -574,6 +580,7 @@ messageEntityCashtag#4c4e743f offset:int length:int = MessageEntity;
messageEntityUnderline#9c4e7e8b offset:int length:int = MessageEntity;
messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity;
messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity;
+messageEntityBankCard#761e6af4 offset:int length:int = MessageEntity;
inputChannelEmpty#ee8c1e86 = InputChannel;
inputChannel#afeb712e channel_id:int access_hash:long = InputChannel;
@@ -822,7 +829,7 @@ phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?
phoneConnection#9d4c17c0 id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection;
-phoneCallProtocol#a2bb35cb flags:# udp_p2p:flags.0?true udp_reflector:flags.1?true min_layer:int max_layer:int = PhoneCallProtocol;
+phoneCallProtocol#fc878fc8 flags:# udp_p2p:flags.0?true udp_reflector:flags.1?true min_layer:int max_layer:int library_versions:Vector = PhoneCallProtocol;
phone.phoneCall#ec82e140 phone_call:PhoneCall users:Vector = phone.PhoneCall;
@@ -1008,7 +1015,7 @@ pageListOrderedItemBlocks#98dd8936 num:string blocks:Vector = PageLis
pageRelatedArticle#b390dc08 flags:# url:string webpage_id:long title:flags.0?string description:flags.1?string photo_id:flags.2?long author:flags.3?string published_date:flags.4?int = PageRelatedArticle;
-page#ae891bec flags:# part:flags.0?true rtl:flags.1?true v2:flags.2?true url:string blocks:Vector photos:Vector documents:Vector = Page;
+page#98657f0d flags:# part:flags.0?true rtl:flags.1?true v2:flags.2?true url:string blocks:Vector photos:Vector documents:Vector views:flags.3?int = Page;
help.supportName#8c05f1c9 name:string = help.SupportName;
@@ -1073,6 +1080,7 @@ channelLocationEmpty#bfb5ad8b = ChannelLocation;
channelLocation#209b82db geo_point:GeoPoint address:string = ChannelLocation;
peerLocated#ca461b5d peer:Peer expires:int distance:int = PeerLocated;
+peerSelfLocated#f8ec284b expires:int = PeerLocated;
restrictionReason#d072acb4 platform:string reason:string text:string = RestrictionReason;
@@ -1110,6 +1118,28 @@ messageUserVoteMultiple#e8fe0de user_id:int options:Vector date:int = Mes
messages.votesList#823f649 flags:# count:int votes:Vector users:Vector next_offset:flags.0?string = messages.VotesList;
+bankCardOpenUrl#f568028a url:string name:string = BankCardOpenUrl;
+
+payments.bankCardData#3e24e573 title:string open_urls:Vector = payments.BankCardData;
+
+dialogFilter#7438f7e8 flags:# contacts:flags.0?true non_contacts:flags.1?true groups:flags.2?true broadcasts:flags.3?true bots:flags.4?true exclude_muted:flags.11?true exclude_read:flags.12?true exclude_archived:flags.13?true id:int title:string emoticon:flags.25?string pinned_peers:Vector include_peers:Vector exclude_peers:Vector = DialogFilter;
+
+dialogFilterSuggested#77744d4a filter:DialogFilter description:string = DialogFilterSuggested;
+
+statsDateRangeDays#b637edaf min_date:int max_date:int = StatsDateRangeDays;
+
+statsAbsValueAndPrev#cb43acde current:double previous:double = StatsAbsValueAndPrev;
+
+statsPercentValue#cbce2fe0 part:double total:double = StatsPercentValue;
+
+statsGraphAsync#4a27eb2d token:string = StatsGraph;
+statsGraphError#bedc9822 error:string = StatsGraph;
+statsGraph#8ea464b6 flags:# json:DataJSON zoom_token:flags.0?string = StatsGraph;
+
+messageInteractionCounters#ad4fc9bd msg_id:int views:int forwards:int = MessageInteractionCounters;
+
+stats.broadcastStats#bdf78394 period:StatsDateRangeDays followers:StatsAbsValueAndPrev views_per_post:StatsAbsValueAndPrev shares_per_post:StatsAbsValueAndPrev enabled_notifications:StatsPercentValue growth_graph:StatsGraph followers_graph:StatsGraph mute_graph:StatsGraph top_hours_graph:StatsGraph interactions_graph:StatsGraph iv_interactions_graph:StatsGraph views_by_source_graph:StatsGraph new_followers_by_source_graph:StatsGraph languages_graph:StatsGraph recent_message_interactions:Vector = stats.BroadcastStats;
+
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -1227,7 +1257,7 @@ contacts.getSaved#82f1e39f = Vector;
contacts.toggleTopPeers#8514bdda enabled:Bool = Bool;
contacts.addContact#e8f463d0 flags:# add_phone_privacy_exception:flags.0?true id:InputUser first_name:string last_name:string phone:string = Updates;
contacts.acceptContact#f831a20f id:InputUser = Updates;
-contacts.getLocated#a356056 geo_point:InputGeoPoint = Updates;
+contacts.getLocated#d348bc44 flags:# background:flags.1?true geo_point:InputGeoPoint self_expires:flags.0?int = Updates;
messages.getMessages#63c66506 id:Vector = messages.Messages;
messages.getDialogs#a0ee3b73 flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:int = messages.Dialogs;
@@ -1347,6 +1377,11 @@ messages.getScheduledMessages#bdbb0464 peer:InputPeer id:Vector = messages.
messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector = Updates;
messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector = Updates;
messages.getPollVotes#b86e380e flags:# peer:InputPeer id:int option:flags.0?bytes offset:flags.1?string limit:int = messages.VotesList;
+messages.toggleStickerSets#b5052fea flags:# uninstall:flags.0?true archive:flags.1?true unarchive:flags.2?true stickersets:Vector = Bool;
+messages.getDialogFilters#f19ed96d = Vector;
+messages.getSuggestedDialogFilters#a29cd42c = Vector;
+messages.updateDialogFilter#1ad4a04a flags:# id:int filter:flags.0?DialogFilter = Bool;
+messages.updateDialogFiltersOrder#c563c1e4 order:Vector = Bool;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@@ -1431,6 +1466,7 @@ payments.validateRequestedInfo#770a8e74 flags:# save:flags.0?true msg_id:int inf
payments.sendPaymentForm#2b8879b3 flags:# msg_id:int requested_info_id:flags.0?string shipping_option_id:flags.1?string credentials:InputPaymentCredentials = payments.PaymentResult;
payments.getSavedInfo#227d824b = payments.SavedInfo;
payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool;
+payments.getBankCardData#2e79d779 number:string = payments.BankCardData;
stickers.createStickerSet#9bd86e6a flags:# masks:flags.0?true user_id:InputUser title:string short_name:string stickers:Vector = messages.StickerSet;
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;
@@ -1455,4 +1491,7 @@ langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLangua
folders.editPeerFolders#6847d0ab folder_peers:Vector = Updates;
folders.deleteFolder#1c295881 folder_id:int = Updates;
-// LAYER 109
+stats.getBroadcastStats#ab42441a flags:# dark:flags.0?true channel:InputChannel = stats.BroadcastStats;
+stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
+
+// LAYER 111
diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml
index ab2fefff9..5db8923b4 100644
--- a/Telegram/Resources/uwp/AppX/AppxManifest.xml
+++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml
@@ -9,7 +9,7 @@
+ Version="1.9.22.0" />
Telegram Desktop
Telegram FZ-LLC
diff --git a/Telegram/SourceFiles/api/api_chat_filters.cpp b/Telegram/SourceFiles/api/api_chat_filters.cpp
new file mode 100644
index 000000000..68b0dbebe
--- /dev/null
+++ b/Telegram/SourceFiles/api/api_chat_filters.cpp
@@ -0,0 +1,35 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "api/api_chat_filters.h"
+
+#include "data/data_session.h"
+#include "data/data_chat_filters.h"
+#include "main/main_session.h"
+#include "apiwrap.h"
+
+namespace Api {
+
+void SaveNewFilterPinned(
+ not_null session,
+ FilterId filterId) {
+ const auto &order = session->data().pinnedChatsOrder(
+ nullptr,
+ filterId);
+ auto &filters = session->data().chatsFilters();
+ const auto &filter = filters.applyUpdatedPinned(
+ filterId,
+ order);
+ session->api().request(MTPmessages_UpdateDialogFilter(
+ MTP_flags(MTPmessages_UpdateDialogFilter::Flag::f_filter),
+ MTP_int(filterId),
+ filter.tl()
+ )).send();
+
+}
+
+} // namespace Api
diff --git a/Telegram/SourceFiles/api/api_chat_filters.h b/Telegram/SourceFiles/api/api_chat_filters.h
new file mode 100644
index 000000000..59c29f72b
--- /dev/null
+++ b/Telegram/SourceFiles/api/api_chat_filters.h
@@ -0,0 +1,20 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+namespace Main {
+class Session;
+} // namespace Main
+
+namespace Api {
+
+void SaveNewFilterPinned(
+ not_null session,
+ FilterId filterId);
+
+} // namespace Api
diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp
index 082826664..d0c5315ac 100644
--- a/Telegram/SourceFiles/api/api_sending.cpp
+++ b/Telegram/SourceFiles/api/api_sending.cpp
@@ -198,4 +198,109 @@ void SendExistingPhoto(
Data::FileOrigin());
}
+bool SendDice(Api::MessageToSend &message) {
+ static const auto kDiceString = QString::fromUtf8("\xF0\x9F\x8E\xB2");
+ if (message.textWithTags.text != kDiceString) {
+ return false;
+ }
+ const auto history = message.action.history;
+ const auto peer = history->peer;
+ const auto session = &history->session();
+ const auto api = &session->api();
+
+ message.textWithTags = TextWithTags();
+ message.action.clearDraft = false;
+ message.action.generateLocal = true;
+ api->sendAction(message.action);
+
+ const auto newId = FullMsgId(
+ peerToChannel(peer->id),
+ session->data().nextLocalMessageId());
+ const auto randomId = rand_value();
+
+ auto &histories = history->owner().histories();
+ auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
+ auto clientFlags = NewMessageClientFlags();
+ auto sendFlags = MTPmessages_SendMedia::Flags(0);
+ if (message.action.replyTo) {
+ flags |= MTPDmessage::Flag::f_reply_to_msg_id;
+ sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
+ }
+ const auto channelPost = peer->isChannel() && !peer->isMegagroup();
+ const auto silentPost = message.action.options.silent
+ || (channelPost && session->data().notifySilentPosts(peer));
+ if (channelPost) {
+ flags |= MTPDmessage::Flag::f_views;
+ flags |= MTPDmessage::Flag::f_post;
+ }
+ if (!channelPost) {
+ flags |= MTPDmessage::Flag::f_from_id;
+ } else if (peer->asChannel()->addsSignature()) {
+ flags |= MTPDmessage::Flag::f_post_author;
+ }
+ if (silentPost) {
+ sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
+ }
+ auto messageFromId = channelPost ? 0 : session->userId();
+ auto messagePostAuthor = channelPost ? session->user()->name : QString();
+ const auto replyTo = message.action.replyTo;
+
+ if (message.action.options.scheduled) {
+ flags |= MTPDmessage::Flag::f_from_scheduled;
+ sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
+ } else {
+ clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
+ }
+
+ session->data().registerMessageRandomId(randomId, newId);
+
+ history->addNewMessage(
+ MTP_message(
+ MTP_flags(flags),
+ MTP_int(newId.msg),
+ MTP_int(messageFromId),
+ peerToMTP(history->peer->id),
+ MTPMessageFwdHeader(),
+ MTP_int(0),
+ MTP_int(replyTo),
+ MTP_int(HistoryItem::NewMessageDate(
+ message.action.options.scheduled)),
+ MTP_string(),
+ MTP_messageMediaDice(MTP_int(0)),
+ MTPReplyMarkup(),
+ MTP_vector(),
+ MTP_int(1),
+ MTPint(),
+ MTP_string(messagePostAuthor),
+ MTPlong(),
+ //MTPMessageReactions(),
+ MTPVector()),
+ clientFlags,
+ NewMessageType::Unread);
+
+ const auto requestType = Data::Histories::RequestType::Send;
+ histories.sendRequest(history, requestType, [=](Fn finish) {
+ history->sendRequestId = api->request(MTPmessages_SendMedia(
+ MTP_flags(sendFlags),
+ peer->input,
+ MTP_int(replyTo),
+ MTP_inputMediaDice(),
+ MTP_string(),
+ MTP_long(randomId),
+ MTPReplyMarkup(),
+ MTP_vector(),
+ MTP_int(message.action.options.scheduled)
+ )).done([=](const MTPUpdates &result) {
+ api->applyUpdates(result, randomId);
+ finish();
+ }).fail([=](const RPCError &error) {
+ api->sendMessageFail(error, peer, randomId, newId);
+ finish();
+ }).afterRequest(history->sendRequestId
+ ).send();
+ return history->sendRequestId;
+ });
+ return true;
+}
+
} // namespace Api
diff --git a/Telegram/SourceFiles/api/api_sending.h b/Telegram/SourceFiles/api/api_sending.h
index ce4c33bb7..c6232eeba 100644
--- a/Telegram/SourceFiles/api/api_sending.h
+++ b/Telegram/SourceFiles/api/api_sending.h
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
class History;
+class PhotoData;
class DocumentData;
namespace Api {
@@ -22,4 +23,6 @@ void SendExistingPhoto(
Api::MessageToSend &&message,
not_null photo);
+[[nodiscard]] bool SendDice(Api::MessageToSend &message);
+
} // namespace Api
diff --git a/Telegram/SourceFiles/api/api_text_entities.cpp b/Telegram/SourceFiles/api/api_text_entities.cpp
index e67b86dcc..eb3f0ebf1 100644
--- a/Telegram/SourceFiles/api/api_text_entities.cpp
+++ b/Telegram/SourceFiles/api/api_text_entities.cpp
@@ -65,6 +65,7 @@ EntitiesInText EntitiesFromMTP(const QVector &entities) {
case mtpc_messageEntityStrike: { auto &d = entity.c_messageEntityStrike(); result.push_back({ EntityType::StrikeOut, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityCode: { auto &d = entity.c_messageEntityCode(); result.push_back({ EntityType::Code, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back({ EntityType::Pre, d.voffset().v, d.vlength().v, Clean(qs(d.vlanguage())) }); } break;
+ case mtpc_messageEntityBankCard: break; // Skipping cards.
// #TODO entities
}
}
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index 5dbea9760..5da6934f7 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "apiwrap.h"
+#include "api/api_sending.h"
#include "api/api_text_entities.h"
#include "api/api_self_destruct.h"
#include "api/api_sensitive_content.h"
@@ -25,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat.h"
#include "data/data_user.h"
#include "data/data_cloud_themes.h"
+#include "data/data_chat_filters.h"
#include "data/data_histories.h"
#include "dialogs/dialogs_key.h"
#include "core/core_cloud_password.h"
@@ -247,6 +249,13 @@ ApiWrap::ApiWrap(not_null session)
photoUploadReady(data.fullId, data.file);
}, _session->lifetime());
+ _session->data().chatsFilters().changed(
+ ) | rpl::filter([=] {
+ return _session->data().chatsFilters().archiveNeeded();
+ }) | rpl::start_with_next([=] {
+ requestMoreDialogsIfNeeded();
+ }, _session->lifetime());
+
setupSupportMode();
});
}
@@ -483,7 +492,9 @@ void ApiWrap::applyUpdates(
}
void ApiWrap::savePinnedOrder(Data::Folder *folder) {
- const auto &order = _session->data().pinnedChatsOrder(folder);
+ const auto &order = _session->data().pinnedChatsOrder(
+ folder,
+ FilterId());
const auto input = [](const Dialogs::Key &key) {
if (const auto history = key.history()) {
return MTP_inputDialogPeer(history->peer->input);
@@ -512,7 +523,7 @@ void ApiWrap::toggleHistoryArchived(
if (const auto already = _historyArchivedRequests.take(history)) {
request(already->first).cancel();
}
- const auto isPinned = history->isPinnedDialog();
+ const auto isPinned = history->isPinnedDialog(0);
const auto archiveId = Data::Folder::kId;
const auto requestId = request(MTPfolders_EditPeerFolders(
MTP_vector(
@@ -853,13 +864,11 @@ void ApiWrap::requestMoreDialogs(Data::Folder *folder) {
count);
});
- if (!folder) {
- if (!_dialogsLoadState || !_dialogsLoadState->listReceived) {
- refreshDialogsLoadBlocked();
- }
- requestDialogs(folder);
- requestContacts();
+ if (!folder
+ && (!_dialogsLoadState || !_dialogsLoadState->listReceived)) {
+ refreshDialogsLoadBlocked();
}
+ requestMoreDialogsIfNeeded();
_session->data().chatsListChanged(folder);
}).fail([=](const RPCError &error) {
dialogsLoadState(folder)->requestId = 0;
@@ -885,6 +894,25 @@ void ApiWrap::refreshDialogsLoadBlocked() {
&& (_dialogsLoadState->offsetDate <= _dialogsLoadTill);
}
+void ApiWrap::requestMoreDialogsIfNeeded() {
+ const auto dialogsReady = !_dialogsLoadState
+ || _dialogsLoadState->listReceived;
+ if (_session->data().chatsFilters().loadNextExceptions(dialogsReady)) {
+ return;
+ } else if (_dialogsLoadState && !_dialogsLoadState->listReceived) {
+ if (_dialogsLoadState->requestId) {
+ return;
+ }
+ requestDialogs(nullptr);
+ } else if (const auto folder = _session->data().folderLoaded(
+ Data::Folder::kId)) {
+ if (_session->data().chatsFilters().archiveNeeded()) {
+ requestMoreDialogs(folder);
+ }
+ }
+ requestContacts();
+}
+
void ApiWrap::updateDialogsOffset(
Data::Folder *folder,
const QVector &dialogs,
@@ -4793,7 +4821,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
action.generateLocal = true;
sendAction(action);
- if (!peer->canWrite()) {
+ if (!peer->canWrite() || Api::SendDice(message)) {
return;
}
Local::saveRecentSentHashtags(textWithTags.text);
diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h
index c48ef2b01..30d364dbd 100644
--- a/Telegram/SourceFiles/apiwrap.h
+++ b/Telegram/SourceFiles/apiwrap.h
@@ -170,6 +170,7 @@ public:
void requestDialogs(Data::Folder *folder = nullptr);
void requestPinnedDialogs(Data::Folder *folder = nullptr);
void requestMoreBlockedByDateDialogs();
+ void requestMoreDialogsIfNeeded();
rpl::producer dialogsLoadMayBlockByDate() const;
rpl::producer dialogsLoadBlockedByDate() const;
diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp
index dfc4bcedc..fb7bd3f78 100644
--- a/Telegram/SourceFiles/app.cpp
+++ b/Telegram/SourceFiles/app.cpp
@@ -56,23 +56,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#endif // OS_MAC_OLD
namespace {
- App::LaunchState _launchState = App::Launched;
- HistoryView::Element *hoveredItem = nullptr,
- *pressedItem = nullptr,
- *hoveredLinkItem = nullptr,
- *pressedLinkItem = nullptr,
- *mousedItem = nullptr;
+constexpr auto kImageAreaLimit = 6'016 * 3'384;
- struct CornersPixmaps {
- QPixmap p[4];
- };
- QVector corners;
- using CornersMap = QMap;
- CornersMap cornersMap;
- QImage cornersMaskLarge[4], cornersMaskSmall[4];
+App::LaunchState _launchState = App::Launched;
- int32 serviceImageCacheSize = 0;
+HistoryView::Element *hoveredItem = nullptr,
+ *pressedItem = nullptr,
+ *hoveredLinkItem = nullptr,
+ *pressedLinkItem = nullptr,
+ *mousedItem = nullptr;
+
+struct CornersPixmaps {
+ QPixmap p[4];
+};
+QVector corners;
+using CornersMap = QMap;
+CornersMap cornersMap;
+QImage cornersMaskLarge[4], cornersMaskSmall[4];
+
+int32 serviceImageCacheSize = 0;
} // namespace
@@ -331,6 +334,13 @@ namespace App {
reader.setAutoTransform(true);
#endif // OS_MAC_OLD
if (animated) *animated = reader.supportsAnimation() && reader.imageCount() > 1;
+ if (!reader.canRead()) {
+ return QImage();
+ }
+ const auto imageSize = reader.size();
+ if (imageSize.width() * imageSize.height() > kImageAreaLimit) {
+ return QImage();
+ }
QByteArray fmt = reader.format();
if (!fmt.isEmpty()) *format = fmt;
if (!reader.read(&result)) {
diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp
index f69aac5db..6882c74b2 100644
--- a/Telegram/SourceFiles/boxes/background_box.cpp
+++ b/Telegram/SourceFiles/boxes/background_box.cpp
@@ -185,7 +185,7 @@ BackgroundBox::Inner::Inner(
, _session(session)
, _api(_session->api().instance())
, _check(std::make_unique(st::overviewCheck, [=] { update(); })) {
- _check->setChecked(true, Ui::RoundCheckbox::SetStyle::Fast);
+ _check->setChecked(true, anim::type::instant);
if (_session->data().wallpapers().empty()) {
resize(st::boxWideWidth, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
} else {
diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style
index f1fc45e13..00a664ef6 100644
--- a/Telegram/SourceFiles/boxes/boxes.style
+++ b/Telegram/SourceFiles/boxes/boxes.style
@@ -216,26 +216,9 @@ contactsMultiSelect: MultiSelect {
fieldCancel: contactsSearchCancel;
fieldCancelSkip: 40px;
}
-contactsPhotoCheckIcon: icon {{
- "default_checkbox_check",
- overviewCheckFgActive,
- point(3px, 6px)
-}};
-contactsPhotoCheck: RoundCheckbox(defaultRoundCheckbox) {
- size: 20px;
- sizeSmall: 0.3;
- bgInactive: overviewCheckBg;
- bgActive: overviewCheckBgActive;
- check: contactsPhotoCheckIcon;
-}
-contactsPhotoCheckbox: RoundImageCheckbox {
- imageRadius: 21px;
- imageSmallRadius: 18px;
- selectWidth: 2px;
- selectFg: windowBgActive;
- selectDuration: 150;
- check: contactsPhotoCheck;
-}
+contactsPhotoCheckIcon: defaultPeerListCheckIcon;
+contactsPhotoCheck: defaultPeerListCheck;
+contactsPhotoCheckbox: defaultPeerListCheckbox;
contactsPhotoDisabledCheckFg: menuIconFg;
contactsNameCheckedFg: windowActiveTextFg;
contactsRipple: defaultRippleAnimation;
diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp
index c580d3716..4839c5269 100644
--- a/Telegram/SourceFiles/boxes/confirm_box.cpp
+++ b/Telegram/SourceFiles/boxes/confirm_box.cpp
@@ -43,13 +43,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include
#include
-TextParseOptions _confirmBoxTextOptions = {
- TextParseLinks | TextParseMultiline | TextParseMarkdown | TextParseRichText, // flags
+namespace {
+
+TextParseOptions kInformBoxTextOptions = {
+ (TextParseLinks
+ | TextParseMultiline
+ | TextParseMarkdown
+ | TextParseRichText), // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
+TextParseOptions kMarkedTextBoxOptions = {
+ (TextParseLinks
+ | TextParseMultiline
+ | TextParseMarkdown
+ | TextParseRichText
+ | TextParseMentions
+ | TextParseHashtags), // flags
+ 0, // maxw
+ 0, // maxh
+ Qt::LayoutDirectionAuto, // dir
+};
+
+} // namespace
+
ConfirmBox::ConfirmBox(
QWidget*,
const QString &text,
@@ -185,11 +204,11 @@ void ConfirmBox::init(const QString &text) {
_text.setText(
st::boxLabelStyle,
text,
- _informative ? _confirmBoxTextOptions : _textPlainOptions);
+ _informative ? kInformBoxTextOptions : _textPlainOptions);
}
void ConfirmBox::init(const TextWithEntities &text) {
- _text.setMarkedText(st::boxLabelStyle, text, _confirmBoxTextOptions);
+ _text.setMarkedText(st::boxLabelStyle, text, kMarkedTextBoxOptions);
}
void ConfirmBox::prepare() {
@@ -326,7 +345,16 @@ InformBox::InformBox(QWidget*, const TextWithEntities &text, const QString &done
MaxInviteBox::MaxInviteBox(QWidget*, not_null channel) : BoxContent()
, _channel(channel)
-, _text(st::boxLabelStyle, tr::lng_participant_invite_sorry(tr::now, lt_count, Global::ChatSizeMax()), _confirmBoxTextOptions, st::boxWidth - st::boxPadding.left() - st::defaultBox.buttonPadding.right()) {
+, _text(
+ st::boxLabelStyle,
+ tr::lng_participant_invite_sorry(
+ tr::now,
+ lt_count,
+ Global::ChatSizeMax()),
+ kInformBoxTextOptions,
+ (st::boxWidth
+ - st::boxPadding.left()
+ - st::defaultBox.buttonPadding.right())) {
}
void MaxInviteBox::prepare() {
diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
index 03667da4e..8b634248f 100644
--- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
+++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
@@ -69,7 +69,7 @@ Main::Session &PrivacyExceptionsBoxController::session() const {
void PrivacyExceptionsBoxController::prepareViewHook() {
delegate()->peerListSetTitle(std::move(_title));
- delegate()->peerListAddSelectedRows(_selected);
+ delegate()->peerListAddSelectedPeers(_selected);
}
std::vector> PrivacyExceptionsBoxController::getResult() const {
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp
new file mode 100644
index 000000000..0955fd11d
--- /dev/null
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp
@@ -0,0 +1,702 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "boxes/filters/edit_filter_box.h"
+
+#include "boxes/filters/edit_filter_chats_list.h"
+#include "chat_helpers/emoji_suggestions_widget.h"
+#include "ui/layers/generic_box.h"
+#include "ui/text/text_utilities.h"
+#include "ui/widgets/checkbox.h"
+#include "ui/widgets/buttons.h"
+#include "ui/widgets/input_fields.h"
+#include "ui/effects/panel_animation.h"
+#include "ui/filter_icons.h"
+#include "ui/filter_icon_panel.h"
+#include "data/data_chat_filters.h"
+#include "data/data_peer.h"
+#include "data/data_session.h"
+#include "core/kotato_settings.h"
+#include "settings/settings_common.h"
+#include "base/event_filter.h"
+#include "lang/lang_keys.h"
+#include "history/history.h"
+#include "main/main_session.h"
+#include "window/window_session_controller.h"
+#include "window/window_controller.h"
+#include "apiwrap.h"
+#include "styles/style_settings.h"
+#include "styles/style_boxes.h"
+#include "styles/style_layers.h"
+#include "styles/style_window.h"
+
+namespace {
+
+using namespace Settings;
+
+constexpr auto kMaxFilterTitleLength = 12;
+
+using Flag = Data::ChatFilter::Flag;
+using Flags = Data::ChatFilter::Flags;
+using ExceptionPeersRef = const base::flat_set> &;
+using ExceptionPeersGetter = ExceptionPeersRef(Data::ChatFilter::*)() const;
+
+constexpr auto kAllTypes = {
+ Flag::Contacts,
+ Flag::NonContacts,
+ Flag::Groups,
+ Flag::Channels,
+ Flag::Bots,
+ Flag::NoMuted,
+ Flag::NoRead,
+ Flag::NoArchived,
+};
+
+class FilterChatsPreview final : public Ui::RpWidget {
+public:
+ FilterChatsPreview(
+ not_null parent,
+ Flags flags,
+ const base::flat_set> &peers);
+
+ [[nodiscard]] rpl::producer flagRemoved() const;
+ [[nodiscard]] rpl::producer> peerRemoved() const;
+
+ void updateData(
+ Flags flags,
+ const base::flat_set> &peers);
+
+ int resizeGetHeight(int newWidth) override;
+
+private:
+ using Button = base::unique_qptr;
+ struct FlagButton {
+ Flag flag = Flag();
+ Button button;
+ };
+ struct PeerButton {
+ not_null history;
+ Button button;
+ };
+
+ void paintEvent(QPaintEvent *e) override;
+
+ void refresh();
+ void removeFlag(Flag flag);
+ void removePeer(not_null history);
+
+ std::vector _removeFlag;
+ std::vector _removePeer;
+
+ rpl::event_stream _flagRemoved;
+ rpl::event_stream> _peerRemoved;
+
+};
+
+struct NameEditing {
+ not_null field;
+ bool custom = false;
+ bool settingDefault = false;
+};
+
+not_null SetupChatsPreview(
+ not_null content,
+ not_null*> data,
+ Fn updateDefaultTitle,
+ Flags flags,
+ ExceptionPeersGetter peers) {
+ const auto rules = data->current();
+ const auto preview = content->add(object_ptr(
+ content,
+ rules.flags() & flags,
+ (rules.*peers)()));
+
+ preview->flagRemoved(
+ ) | rpl::start_with_next([=](Flag flag) {
+ const auto rules = data->current();
+ auto computed = Data::ChatFilter(
+ rules.id(),
+ rules.title(),
+ rules.iconEmoji(),
+ (rules.flags() & ~flag),
+ rules.always(),
+ rules.pinned(),
+ rules.never());
+ updateDefaultTitle(computed);
+ *data = std::move(computed);
+ }, preview->lifetime());
+
+ preview->peerRemoved(
+ ) | rpl::start_with_next([=](not_null history) {
+ const auto rules = data->current();
+ auto always = rules.always();
+ auto pinned = rules.pinned();
+ auto never = rules.never();
+ always.remove(history);
+ pinned.erase(ranges::remove(pinned, history), end(pinned));
+ never.remove(history);
+ auto computed = Data::ChatFilter(
+ rules.id(),
+ rules.title(),
+ rules.iconEmoji(),
+ rules.flags(),
+ std::move(always),
+ std::move(pinned),
+ std::move(never));
+ updateDefaultTitle(computed);
+ *data = std::move(computed);
+ }, preview->lifetime());
+
+ return preview;
+}
+
+FilterChatsPreview::FilterChatsPreview(
+ not_null parent,
+ Flags flags,
+ const base::flat_set> &peers)
+: RpWidget(parent) {
+ updateData(flags, peers);
+}
+
+void FilterChatsPreview::refresh() {
+ resizeToWidth(width());
+}
+
+void FilterChatsPreview::updateData(
+ Flags flags,
+ const base::flat_set> &peers) {
+ _removeFlag.clear();
+ _removePeer.clear();
+ const auto makeButton = [&](Fn handler) {
+ auto result = base::make_unique_q(
+ this,
+ st::windowFilterSmallRemove);
+ result->setClickedCallback(std::move(handler));
+ return result;
+ };
+ for (const auto flag : kAllTypes) {
+ if (flags & flag) {
+ _removeFlag.push_back({
+ flag,
+ makeButton([=] { removeFlag(flag); }) });
+ }
+ }
+ for (const auto history : peers) {
+ _removePeer.push_back({
+ history,
+ makeButton([=] { removePeer(history); }) });
+ }
+ refresh();
+}
+
+int FilterChatsPreview::resizeGetHeight(int newWidth) {
+ const auto right = st::windowFilterSmallRemoveRight;
+ const auto add = (st::windowFilterSmallItem.height
+ - st::windowFilterSmallRemove.height) / 2;
+ auto top = 0;
+ const auto moveNextButton = [&](not_null button) {
+ button->moveToRight(right, top + add, newWidth);
+ top += st::windowFilterSmallItem.height;
+ };
+ for (const auto &[flag, button] : _removeFlag) {
+ moveNextButton(button.get());
+ }
+ for (const auto &[history, button] : _removePeer) {
+ moveNextButton(button.get());
+ }
+ return top;
+}
+
+void FilterChatsPreview::paintEvent(QPaintEvent *e) {
+ auto p = Painter(this);
+ auto top = 0;
+ const auto &st = st::windowFilterSmallItem;
+ const auto iconLeft = st.photoPosition.x();
+ const auto iconTop = st.photoPosition.y();
+ const auto nameLeft = st.namePosition.x();
+ p.setFont(st::windowFilterSmallItem.nameStyle.font);
+ const auto nameTop = st.namePosition.y();
+ for (const auto &[flag, button] : _removeFlag) {
+ PaintFilterChatsTypeIcon(
+ p,
+ flag,
+ iconLeft,
+ top + iconTop,
+ width(),
+ st.photoSize);
+
+ p.setPen(st::contactsNameFg);
+ p.drawTextLeft(
+ nameLeft,
+ top + nameTop,
+ width(),
+ FilterChatsTypeName(flag));
+ top += st.height;
+ }
+ for (const auto &[history, button] : _removePeer) {
+ const auto savedMessages = history->peer->isSelf();
+ if (savedMessages) {
+ Ui::EmptyUserpic::PaintSavedMessages(
+ p,
+ iconLeft,
+ top + iconTop,
+ width(),
+ st.photoSize);
+ p.setPen(st::contactsNameFg);
+ p.drawTextLeft(
+ nameLeft,
+ top + nameTop,
+ width(),
+ tr::lng_saved_messages(tr::now));
+ } else {
+ history->peer->paintUserpicLeft(
+ p,
+ iconLeft,
+ top + iconTop,
+ width(),
+ st.photoSize);
+ p.setPen(st::contactsNameFg);
+ history->peer->nameText().drawLeftElided(
+ p,
+ nameLeft,
+ top + nameTop,
+ button->x() - nameLeft,
+ width());
+ }
+ top += st.height;
+ }
+}
+
+void FilterChatsPreview::removeFlag(Flag flag) {
+ const auto i = ranges::find(_removeFlag, flag, &FlagButton::flag);
+ Assert(i != end(_removeFlag));
+ _removeFlag.erase(i);
+ refresh();
+ _flagRemoved.fire_copy(flag);
+}
+
+void FilterChatsPreview::removePeer(not_null history) {
+ const auto i = ranges::find(_removePeer, history, &PeerButton::history);
+ Assert(i != end(_removePeer));
+ _removePeer.erase(i);
+ refresh();
+ _peerRemoved.fire_copy(history);
+}
+
+rpl::producer FilterChatsPreview::flagRemoved() const {
+ return _flagRemoved.events();
+}
+
+rpl::producer> FilterChatsPreview::peerRemoved() const {
+ return _peerRemoved.events();
+}
+
+void EditExceptions(
+ not_null window,
+ not_null context,
+ Flags options,
+ not_null*> data,
+ Fn updateDefaultTitle,
+ Fn refresh) {
+ const auto include = (options & Flag::Contacts) != Flags(0);
+ const auto rules = data->current();
+ auto controller = std::make_unique(
+ window,
+ (include
+ ? tr::lng_filters_include_title()
+ : tr::lng_filters_exclude_title()),
+ options,
+ rules.flags() & options,
+ include ? rules.always() : rules.never());
+ const auto rawController = controller.get();
+ auto initBox = [=](not_null box) {
+ box->addButton(tr::lng_settings_save(), crl::guard(context, [=] {
+ const auto peers = box->peerListCollectSelectedRows();
+ const auto rules = data->current();
+ auto &&histories = ranges::view::all(
+ peers
+ ) | ranges::view::transform([=](not_null peer) {
+ return window->session().data().history(peer);
+ });
+ auto changed = base::flat_set>{
+ histories.begin(),
+ histories.end()
+ };
+ auto removeFrom = include ? rules.never() : rules.always();
+ for (const auto &history : changed) {
+ removeFrom.remove(history);
+ }
+ auto pinned = rules.pinned();
+ pinned.erase(ranges::remove_if(pinned, [&](
+ not_null history) {
+ const auto contains = changed.contains(history);
+ return include ? !contains : contains;
+ }), end(pinned));
+ auto computed = Data::ChatFilter(
+ rules.id(),
+ rules.title(),
+ rules.iconEmoji(),
+ ((rules.flags() & ~options)
+ | rawController->chosenOptions()),
+ include ? std::move(changed) : std::move(removeFrom),
+ std::move(pinned),
+ include ? std::move(removeFrom) : std::move(changed));
+ updateDefaultTitle(computed);
+ *data = computed;
+ refresh();
+ box->closeBox();
+ }));
+ box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
+ };
+ window->window().show(
+ Box(
+ std::move(controller),
+ std::move(initBox)),
+ Ui::LayerOption::KeepOther);
+}
+
+void CreateIconSelector(
+ not_null outer,
+ not_null box,
+ not_null parent,
+ not_null input,
+ not_null*> data) {
+ const auto rules = data->current();
+ const auto toggle = Ui::CreateChild(parent.get());
+ toggle->resize(st::windowFilterIconToggleSize);
+
+ const auto type = toggle->lifetime().make_state();
+ data->value(
+ ) | rpl::map([=](const Data::ChatFilter &filter) {
+ return Ui::ComputeFilterIcon(filter);
+ }) | rpl::start_with_next([=](Ui::FilterIcon icon) {
+ *type = icon;
+ toggle->update();
+ }, toggle->lifetime());
+
+ input->geometryValue(
+ ) | rpl::start_with_next([=](QRect geometry) {
+ const auto left = geometry.x() + geometry.width() - toggle->width();
+ const auto position = st::windowFilterIconTogglePosition;
+ toggle->move(
+ left - position.x(),
+ geometry.y() + position.y());
+ }, toggle->lifetime());
+
+ toggle->paintRequest(
+ ) | rpl::start_with_next([=] {
+ auto p = QPainter(toggle);
+ const auto icons = Ui::LookupFilterIcon(*type);
+ icons.normal->paintInCenter(p, toggle->rect(), st::emojiIconFg->c);
+ }, toggle->lifetime());
+
+ const auto panel = toggle->lifetime().make_state(
+ outer);
+ toggle->installEventFilter(panel);
+ toggle->addClickHandler([=] {
+ panel->toggleAnimated();
+ });
+ panel->chosen(
+ ) | rpl::filter([=](Ui::FilterIcon icon) {
+ return icon != Ui::ComputeFilterIcon(data->current());
+ }) | rpl::start_with_next([=](Ui::FilterIcon icon) {
+ panel->hideAnimated();
+ const auto rules = data->current();
+ *data = Data::ChatFilter(
+ rules.id(),
+ rules.title(),
+ Ui::LookupFilterIcon(icon).emoji,
+ rules.flags(),
+ rules.always(),
+ rules.pinned(),
+ rules.never());
+ }, panel->lifetime());
+
+ const auto updatePanelGeometry = [=] {
+ const auto global = toggle->mapToGlobal({
+ toggle->width(),
+ toggle->height()
+ });
+ const auto local = outer->mapFromGlobal(global);
+ const auto position = st::windwoFilterIconPanelPosition;
+ const auto padding = panel->innerPadding();
+ panel->move(
+ local.x() - panel->width() + position.x() + padding.right(),
+ local.y() + position.y() - padding.top());
+ };
+
+ const auto filterForGeometry = [=](not_null event) {
+ const auto type = event->type();
+ if (type == QEvent::Move || type == QEvent::Resize) {
+ // updatePanelGeometry uses not only container geometry, but
+ // also container children geometries that will be updated later.
+ crl::on_main(panel, [=] { updatePanelGeometry(); });
+ }
+ return base::EventFilterResult::Continue;
+ };
+
+ const auto installFilterForGeometry = [&](not_null target) {
+ panel->lifetime().make_state>(
+ base::install_event_filter(target, filterForGeometry));
+ };
+ installFilterForGeometry(outer);
+ installFilterForGeometry(box);
+}
+
+[[nodiscard]] QString DefaultTitle(const Data::ChatFilter &filter) {
+ using Icon = Ui::FilterIcon;
+ const auto icon = Ui::ComputeDefaultFilterIcon(filter);
+ switch (icon) {
+ case Icon::Private:
+ return (filter.flags() & Data::ChatFilter::Flag::NonContacts)
+ ? tr::lng_filters_name_people(tr::now)
+ : tr::lng_filters_include_contacts(tr::now);
+ case Icon::Groups:
+ return tr::lng_filters_include_groups(tr::now);
+ case Icon::Channels:
+ return tr::lng_filters_include_channels(tr::now);
+ case Icon::Bots:
+ return tr::lng_filters_include_bots(tr::now);
+ case Icon::Unread:
+ return tr::lng_filters_name_unread(tr::now);
+ case Icon::Unmuted:
+ return tr::lng_filters_name_unmuted(tr::now);
+ }
+ return QString();
+}
+
+} // namespace
+
+void EditFilterBox(
+ not_null box,
+ not_null window,
+ const Data::ChatFilter &filter,
+ Fn doneCallback) {
+ const auto creating = filter.title().isEmpty();
+ box->setTitle(creating ? tr::lng_filters_new() : tr::lng_filters_edit());
+ box->setCloseByOutsideClick(false);
+
+ using State = rpl::variable;
+ const auto data = box->lifetime().make_state(filter);
+
+ const auto content = box->verticalLayout();
+ const auto name = content->add(
+ object_ptr(
+ box,
+ st::windowFilterNameInput,
+ tr::lng_filters_new_name(),
+ filter.title()),
+ st::markdownLinkFieldPadding);
+ name->setMaxLength(kMaxFilterTitleLength);
+ name->setInstantReplaces(Ui::InstantReplaces::Default());
+ name->setInstantReplacesEnabled(
+ window->session().settings().replaceEmojiValue());
+ Ui::Emoji::SuggestionsController::Init(
+ box->getDelegate()->outerContainer(),
+ name,
+ &window->session());
+
+ const auto nameEditing = box->lifetime().make_state(
+ NameEditing{ name });
+ nameEditing->custom = !creating;
+ QObject::connect(name, &Ui::InputField::changed, [=] {
+ if (!nameEditing->settingDefault) {
+ nameEditing->custom = true;
+ }
+ });
+ const auto updateDefaultTitle = [=](const Data::ChatFilter &filter) {
+ if (nameEditing->custom) {
+ return;
+ }
+ const auto title = DefaultTitle(filter);
+ if (nameEditing->field->getLastText() != title) {
+ nameEditing->settingDefault = true;
+ nameEditing->field->setText(title);
+ nameEditing->settingDefault = false;
+ }
+ };
+
+ const auto outer = box->getDelegate()->outerContainer();
+ CreateIconSelector(
+ outer,
+ box,
+ content,
+ name,
+ data);
+
+ constexpr auto kTypes = Flag::Contacts
+ | Flag::NonContacts
+ | Flag::Groups
+ | Flag::Channels
+ | Flag::Bots;
+ constexpr auto kExcludeTypes = Flag::NoMuted
+ | Flag::NoArchived
+ | Flag::NoRead;
+
+ box->setFocusCallback([=] {
+ name->setFocusFast();
+ });
+
+ const auto isCurrent = filter.id() == cDefaultFilterId();
+ const auto checkboxDefault = content->add(
+ object_ptr(
+ box,
+ tr::ktg_filters_default(tr::now),
+ (creating ? false : isCurrent),
+ st::defaultBoxCheckbox),
+ style::margins(
+ st::boxPadding.left(),
+ st::boxPadding.bottom(),
+ st::boxPadding.right(),
+ st::boxPadding.bottom()));
+
+ AddSkip(content);
+ AddDivider(content);
+ AddSkip(content);
+ AddSubsectionTitle(content, tr::lng_filters_include());
+
+ const auto include = SetupChatsPreview(
+ content,
+ data,
+ updateDefaultTitle,
+ kTypes,
+ &Data::ChatFilter::always);
+
+ const auto includeAdd = AddButton(
+ content,
+ tr::lng_filters_add_chats() | Ui::Text::ToUpper(),
+ st::settingsUpdate);
+
+ AddSkip(content);
+ content->add(
+ object_ptr(
+ content,
+ tr::lng_filters_include_about(),
+ st::boxDividerLabel),
+ st::windowFilterAboutPadding);
+ AddDivider(content);
+ AddSkip(content);
+
+ AddSubsectionTitle(content, tr::lng_filters_exclude());
+
+ const auto exclude = SetupChatsPreview(
+ content,
+ data,
+ updateDefaultTitle,
+ kExcludeTypes,
+ &Data::ChatFilter::never);
+
+ const auto excludeAdd = AddButton(
+ content,
+ tr::lng_filters_remove_chats() | Ui::Text::ToUpper(),
+ st::settingsUpdate);
+
+ AddSkip(content);
+ content->add(
+ object_ptr(
+ content,
+ tr::lng_filters_exclude_about(),
+ st::boxDividerLabel),
+ st::windowFilterAboutPadding);
+
+ const auto refreshPreviews = [=] {
+ include->updateData(
+ data->current().flags() & kTypes,
+ data->current().always());
+ exclude->updateData(
+ data->current().flags() & kExcludeTypes,
+ data->current().never());
+ };
+ includeAdd->setClickedCallback([=] {
+ EditExceptions(
+ window,
+ box,
+ kTypes,
+ data,
+ updateDefaultTitle,
+ refreshPreviews);
+ });
+ excludeAdd->setClickedCallback([=] {
+ EditExceptions(
+ window,
+ box,
+ kExcludeTypes,
+ data,
+ updateDefaultTitle,
+ refreshPreviews);
+ });
+
+ const auto save = [=] {
+ const auto title = name->getLastText().trimmed();
+ const auto rules = data->current();
+ const auto checked = checkboxDefault && checkboxDefault->checked();
+ const auto result = Data::ChatFilter(
+ rules.id(),
+ title,
+ rules.iconEmoji(),
+ rules.flags(),
+ rules.always(),
+ rules.pinned(),
+ rules.never(),
+ checked);
+ if (title.isEmpty()) {
+ name->showError();
+ return;
+ } else if (!(rules.flags() & kTypes) && rules.always().empty()) {
+ window->window().showToast(tr::lng_filters_empty(tr::now));
+ return;
+ } else if ((rules.flags() == (kTypes | Flag::NoArchived))
+ && rules.always().empty()
+ && rules.never().empty()) {
+ window->window().showToast(tr::lng_filters_default(tr::now));
+ return;
+ }
+ box->closeBox();
+
+ doneCallback(result);
+ };
+ box->addButton(
+ creating ? tr::lng_filters_create_button() : tr::lng_settings_save(),
+ save);
+ box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
+}
+
+void EditExistingFilter(
+ not_null window,
+ FilterId id) {
+ const auto session = &window->session();
+ const auto &list = session->data().chatsFilters().list();
+ const auto i = ranges::find(list, id, &Data::ChatFilter::id);
+ if (i == end(list)) {
+ return;
+ }
+ const auto doneCallback = [=](const Data::ChatFilter &result) {
+ Expects(id == result.id());
+
+ const auto tl = result.tl();
+ session->data().chatsFilters().apply(MTP_updateDialogFilter(
+ MTP_flags(MTPDupdateDialogFilter::Flag::f_filter),
+ MTP_int(id),
+ tl));
+ session->api().request(MTPmessages_UpdateDialogFilter(
+ MTP_flags(MTPmessages_UpdateDialogFilter::Flag::f_filter),
+ MTP_int(id),
+ tl
+ )).send();
+ const auto isCurrentDefault = result.id() == cDefaultFilterId();
+ if ((isCurrentDefault && !result.isDefault())
+ || (!isCurrentDefault && result.isDefault())) {
+ cSetDefaultFilterId(result.isDefault() ? result.id() : 0);
+ KotatoSettings::Write();
+ }
+ };
+ window->window().show(Box(
+ EditFilterBox,
+ window,
+ *i,
+ crl::guard(session, doneCallback)));
+}
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_box.h b/Telegram/SourceFiles/boxes/filters/edit_filter_box.h
new file mode 100644
index 000000000..43b97adb1
--- /dev/null
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_box.h
@@ -0,0 +1,28 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "ui/layers/generic_box.h"
+
+namespace Window {
+class SessionController;
+} // namespace Window
+
+namespace Data {
+class ChatFilter;
+} // namespace Data
+
+void EditFilterBox(
+ not_null box,
+ not_null window,
+ const Data::ChatFilter &filter,
+ Fn doneCallback);
+
+void EditExistingFilter(
+ not_null window,
+ FilterId id);
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp
new file mode 100644
index 000000000..bc783f194
--- /dev/null
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp
@@ -0,0 +1,504 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "boxes/filters/edit_filter_chats_list.h"
+
+#include "history/history.h"
+#include "window/window_session_controller.h"
+#include "lang/lang_keys.h"
+#include "ui/widgets/labels.h"
+#include "ui/wrap/vertical_layout.h"
+#include "base/object_ptr.h"
+#include "styles/style_window.h"
+#include "styles/style_boxes.h"
+
+namespace {
+
+constexpr auto kMaxExceptions = 100;
+
+using Flag = Data::ChatFilter::Flag;
+using Flags = Data::ChatFilter::Flags;
+
+constexpr auto kAllTypes = {
+ Flag::Contacts,
+ Flag::NonContacts,
+ Flag::Groups,
+ Flag::Channels,
+ Flag::Bots,
+ Flag::NoMuted,
+ Flag::NoRead,
+ Flag::NoArchived,
+};
+
+struct RowSelectionChange {
+ not_null row;
+ bool checked = false;
+};
+
+class TypeRow final : public PeerListRow {
+public:
+ explicit TypeRow(Flag flag);
+
+ QString generateName() override;
+ QString generateShortName() override;
+ PaintRoundImageCallback generatePaintUserpicCallback() override;
+
+private:
+ [[nodiscard]] Flag flag() const;
+
+};
+
+class ExceptionRow final : public ChatsListBoxController::Row {
+public:
+ explicit ExceptionRow(not_null history);
+
+ QString generateName() override;
+ QString generateShortName() override;
+ PaintRoundImageCallback generatePaintUserpicCallback() override;
+
+};
+
+class TypeDelegate final : public PeerListContentDelegate {
+public:
+ void peerListSetTitle(rpl::producer title) override;
+ void peerListSetAdditionalTitle(rpl::producer title) override;
+ bool peerListIsRowChecked(not_null row) override;
+ int peerListSelectedRowsCount() override;
+ std::vector> peerListCollectSelectedRows() override;
+ void peerListScrollToTop() override;
+ void peerListAddSelectedPeerInBunch(
+ not_null peer) override;
+ void peerListAddSelectedRowInBunch(not_null row) override;
+ void peerListFinishSelectedRowsBunch() override;
+ void peerListSetDescription(
+ object_ptr description) override;
+
+};
+
+class TypeController final : public PeerListController {
+public:
+ TypeController(
+ not_null session,
+ Flags options,
+ Flags selected);
+
+ Main::Session &session() const override;
+ void prepare() override;
+ void rowClicked(not_null row) override;
+
+ [[nodiscard]] rpl::producer selectedChanges() const;
+ [[nodiscard]] auto rowSelectionChanges() const
+ -> rpl::producer;
+
+private:
+ [[nodiscard]] std::unique_ptr createRow(Flag flag) const;
+ [[nodiscard]] Flags collectSelectedOptions() const;
+
+ const not_null _session;
+ Flags _options;
+
+ rpl::event_stream<> _selectionChanged;
+ rpl::event_stream _rowSelectionChanges;
+
+};
+
+[[nodiscard]] object_ptr CreateSectionSubtitle(
+ not_null parent,
+ rpl::producer text) {
+ auto result = object_ptr(
+ parent,
+ st::searchedBarHeight);
+
+ const auto raw = result.data();
+ raw->paintRequest(
+ ) | rpl::start_with_next([=](QRect clip) {
+ auto p = QPainter(raw);
+ p.fillRect(clip, st::searchedBarBg);
+ }, raw->lifetime());
+
+ const auto label = Ui::CreateChild(
+ raw,
+ std::move(text),
+ st::windowFilterChatsSectionSubtitle);
+ raw->widthValue(
+ ) | rpl::start_with_next([=](int width) {
+ const auto padding = st::windowFilterChatsSectionSubtitlePadding;
+ const auto available = width - padding.left() - padding.right();
+ label->resizeToNaturalWidth(available);
+ label->moveToLeft(padding.left(), padding.top(), width);
+ }, label->lifetime());
+
+ return result;
+}
+
+[[nodiscard]] uint64 TypeId(Flag flag) {
+ return PeerIdFakeShift | static_cast(flag);
+}
+
+TypeRow::TypeRow(Flag flag) : PeerListRow(TypeId(flag)) {
+}
+
+QString TypeRow::generateName() {
+ return FilterChatsTypeName(flag());
+}
+
+QString TypeRow::generateShortName() {
+ return generateName();
+}
+
+PaintRoundImageCallback TypeRow::generatePaintUserpicCallback() {
+ const auto flag = this->flag();
+ return [=](Painter &p, int x, int y, int outerWidth, int size) {
+ PaintFilterChatsTypeIcon(p, flag, x, y, outerWidth, size);
+ };
+}
+
+Flag TypeRow::flag() const {
+ return static_cast(id() & 0xFF);
+}
+
+ExceptionRow::ExceptionRow(not_null history) : Row(history) {
+ if (peer()->isSelf()) {
+ setCustomStatus(tr::lng_saved_forward_here(tr::now));
+ }
+}
+
+QString ExceptionRow::generateName() {
+ return peer()->isSelf()
+ ? tr::lng_saved_messages(tr::now)
+ : Row::generateName();
+}
+
+QString ExceptionRow::generateShortName() {
+ return generateName();
+}
+
+PaintRoundImageCallback ExceptionRow::generatePaintUserpicCallback() {
+ const auto peer = this->peer();
+ const auto saved = peer->isSelf();
+ return [=](Painter &p, int x, int y, int outerWidth, int size) {
+ if (saved) {
+ Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
+ } else {
+ peer->paintUserpicLeft(p, x, y, outerWidth, size);
+ }
+ };
+}
+
+void TypeDelegate::peerListSetTitle(rpl::producer title) {
+}
+
+void TypeDelegate::peerListSetAdditionalTitle(rpl::producer title) {
+}
+
+bool TypeDelegate::peerListIsRowChecked(not_null row) {
+ return false;
+}
+
+int TypeDelegate::peerListSelectedRowsCount() {
+ return 0;
+}
+
+auto TypeDelegate::peerListCollectSelectedRows()
+-> std::vector> {
+ return {};
+}
+
+void TypeDelegate::peerListScrollToTop() {
+}
+
+void TypeDelegate::peerListAddSelectedPeerInBunch(not_null peer) {
+ Unexpected("Item selection in Info::Profile::Members.");
+}
+
+void TypeDelegate::peerListAddSelectedRowInBunch(not_null row) {
+ Unexpected("Item selection in Info::Profile::Members.");
+}
+
+void TypeDelegate::peerListFinishSelectedRowsBunch() {
+}
+
+void TypeDelegate::peerListSetDescription(
+ object_ptr description) {
+ description.destroy();
+}
+
+TypeController::TypeController(
+ not_null session,
+ Flags options,
+ Flags selected)
+: _session(session)
+, _options(options) {
+}
+
+Main::Session &TypeController::session() const {
+ return *_session;
+}
+
+void TypeController::prepare() {
+ for (const auto flag : kAllTypes) {
+ if (_options & flag) {
+ delegate()->peerListAppendRow(createRow(flag));
+ }
+ }
+ delegate()->peerListRefreshRows();
+}
+
+Flags TypeController::collectSelectedOptions() const {
+ auto result = Flags();
+ for (const auto flag : kAllTypes) {
+ if (const auto row = delegate()->peerListFindRow(TypeId(flag))) {
+ if (row->checked()) {
+ result |= flag;
+ }
+ }
+ }
+ return result;
+}
+
+void TypeController::rowClicked(not_null row) {
+ const auto checked = !row->checked();
+ delegate()->peerListSetRowChecked(row, checked);
+ _rowSelectionChanges.fire({ row, checked });
+}
+
+std::unique_ptr TypeController::createRow(Flag flag) const {
+ return std::make_unique(flag);
+}
+
+rpl::producer TypeController::selectedChanges() const {
+ return _rowSelectionChanges.events(
+ ) | rpl::map([=] {
+ return collectSelectedOptions();
+ });
+}
+
+auto TypeController::rowSelectionChanges() const
+-> rpl::producer {
+ return _rowSelectionChanges.events();
+}
+
+} // namespace
+
+[[nodiscard]] QString FilterChatsTypeName(Flag flag) {
+ switch (flag) {
+ case Flag::Contacts: return tr::lng_filters_type_contacts(tr::now);
+ case Flag::NonContacts:
+ return tr::lng_filters_type_non_contacts(tr::now);
+ case Flag::Groups: return tr::lng_filters_type_groups(tr::now);
+ case Flag::Channels: return tr::lng_filters_type_channels(tr::now);
+ case Flag::Bots: return tr::lng_filters_type_bots(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::NoRead: return tr::lng_filters_type_no_read(tr::now);
+ }
+ Unexpected("Flag in TypeName.");
+}
+
+void PaintFilterChatsTypeIcon(
+ Painter &p,
+ Data::ChatFilter::Flag flag,
+ int x,
+ int y,
+ int outerWidth,
+ int size) {
+ const auto &color = [&]() -> const style::color& {
+ switch (flag) {
+ case Flag::Contacts: return st::historyPeer4UserpicBg;
+ case Flag::NonContacts: return st::historyPeer7UserpicBg;
+ case Flag::Groups: return st::historyPeer2UserpicBg;
+ case Flag::Channels: return st::historyPeer1UserpicBg;
+ case Flag::Bots: return st::historyPeer6UserpicBg;
+ case Flag::NoMuted: return st::historyPeer6UserpicBg;
+ case Flag::NoArchived: return st::historyPeer4UserpicBg;
+ case Flag::NoRead: return st::historyPeer7UserpicBg;
+ }
+ Unexpected("Flag in color paintFlagIcon.");
+ }();
+ const auto &icon = [&]() -> const style::icon& {
+ switch (flag) {
+ case Flag::Contacts: return st::windowFilterTypeContacts;
+ case Flag::NonContacts: return st::windowFilterTypeNonContacts;
+ case Flag::Groups: return st::windowFilterTypeGroups;
+ case Flag::Channels: return st::windowFilterTypeChannels;
+ case Flag::Bots: return st::windowFilterTypeBots;
+ case Flag::NoMuted: return st::windowFilterTypeNoMuted;
+ case Flag::NoArchived: return st::windowFilterTypeNoArchived;
+ case Flag::NoRead: return st::windowFilterTypeNoRead;
+ }
+ Unexpected("Flag in icon paintFlagIcon.");
+ }();
+ const auto rect = style::rtlrect(x, y, size, size, outerWidth);
+ auto hq = PainterHighQualityEnabler(p);
+ p.setBrush(color->b);
+ p.setPen(Qt::NoPen);
+ switch (cUserpicCornersType()) {
+ case 0:
+ p.drawRoundedRect(
+ rect,
+ 0, 0);
+ break;
+
+ case 1:
+ p.drawRoundedRect(
+ rect,
+ st::buttonRadius, st::buttonRadius);
+ break;
+
+ case 2:
+ p.drawRoundedRect(
+ rect,
+ st::dateRadius, st::dateRadius);
+ break;
+
+ default:
+ p.drawEllipse(rect);
+ }
+ icon.paintInCenter(p, rect);
+}
+
+EditFilterChatsListController::EditFilterChatsListController(
+ not_null navigation,
+ rpl::producer title,
+ Flags options,
+ Flags selected,
+ const base::flat_set> &peers)
+: ChatsListBoxController(navigation)
+, _navigation(navigation)
+, _title(std::move(title))
+, _peers(peers)
+, _options(options)
+, _selected(selected) {
+}
+
+Main::Session &EditFilterChatsListController::session() const {
+ return _navigation->session();
+}
+
+void EditFilterChatsListController::rowClicked(not_null row) {
+ const auto count = delegate()->peerListSelectedRowsCount();
+ if (count < kMaxExceptions || row->checked()) {
+ delegate()->peerListSetRowChecked(row, !row->checked());
+ updateTitle();
+ }
+}
+
+void EditFilterChatsListController::itemDeselectedHook(
+ not_null peer) {
+ updateTitle();
+}
+
+bool EditFilterChatsListController::isForeignRow(PeerListRowId itemId) {
+ return ranges::contains(kAllTypes, itemId, TypeId);
+}
+
+bool EditFilterChatsListController::handleDeselectForeignRow(
+ PeerListRowId itemId) {
+ if (isForeignRow(itemId)) {
+ _deselectOption(itemId);
+ return true;
+ }
+ return false;
+}
+
+void EditFilterChatsListController::prepareViewHook() {
+ delegate()->peerListSetTitle(std::move(_title));
+ delegate()->peerListSetAboveWidget(prepareTypesList());
+
+ const auto count = int(_peers.size());
+ const auto rows = std::make_unique[]>(count);
+ auto i = 0;
+ for (const auto history : _peers) {
+ rows[i++].emplace(history);
+ }
+ auto pointers = std::vector();
+ pointers.reserve(count);
+ for (auto i = 0; i != count; ++i) {
+ pointers.push_back(&*rows[i]);
+ }
+ delegate()->peerListAddSelectedRows(pointers);
+ updateTitle();
+}
+
+object_ptr EditFilterChatsListController::prepareTypesList() {
+ auto result = object_ptr((QWidget*)nullptr);
+ const auto container = result.data();
+ container->add(CreateSectionSubtitle(
+ container,
+ tr::lng_filters_edit_types()));
+ container->add(object_ptr(
+ container,
+ st::membersMarginTop));
+ const auto delegate = container->lifetime().make_state();
+ const auto controller = container->lifetime().make_state(
+ &session(),
+ _options,
+ _selected);
+ const auto content = result->add(object_ptr(
+ container,
+ controller,
+ st::windowFilterSmallList));
+ delegate->setContent(content);
+ controller->setDelegate(delegate);
+ for (const auto flag : kAllTypes) {
+ if (_selected & flag) {
+ if (const auto row = delegate->peerListFindRow(TypeId(flag))) {
+ content->changeCheckState(row, true, anim::type::instant);
+ this->delegate()->peerListSetForeignRowChecked(
+ row,
+ true,
+ anim::type::instant);
+ }
+ }
+ }
+ container->add(object_ptr(
+ container,
+ st::membersMarginBottom));
+ container->add(CreateSectionSubtitle(
+ container,
+ tr::lng_filters_edit_chats()));
+
+ controller->selectedChanges(
+ ) | rpl::start_with_next([=](Flags selected) {
+ _selected = selected;
+ }, _lifetime);
+
+ controller->rowSelectionChanges(
+ ) | rpl::start_with_next([=](RowSelectionChange update) {
+ this->delegate()->peerListSetForeignRowChecked(
+ update.row,
+ update.checked,
+ anim::type::normal);
+ }, _lifetime);
+
+ _deselectOption = [=](PeerListRowId itemId) {
+ if (const auto row = delegate->peerListFindRow(itemId)) {
+ delegate->peerListSetRowChecked(row, false);
+ }
+ };
+
+ return result;
+}
+
+auto EditFilterChatsListController::createRow(not_null history)
+-> std::unique_ptr {
+ return history->inChatList()
+ ? std::make_unique(history)
+ : nullptr;
+}
+
+void EditFilterChatsListController::updateTitle() {
+ auto types = 0;
+ for (const auto flag : kAllTypes) {
+ if (_selected & flag) {
+ ++types;
+ }
+ }
+ const auto count = delegate()->peerListSelectedRowsCount() - types;
+ const auto additional = qsl("%1 / %2").arg(count).arg(kMaxExceptions);
+ delegate()->peerListSetAdditionalTitle(rpl::single(additional));
+}
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.h b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.h
new file mode 100644
index 000000000..e993cafb7
--- /dev/null
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.h
@@ -0,0 +1,77 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "boxes/peer_list_controllers.h"
+#include "data/data_chat_filters.h"
+
+class History;
+
+namespace Ui {
+class GenericBox;
+} // namespace Ui
+
+namespace Window {
+class SessionController;
+} // namespace Window
+
+namespace Main {
+class Session;
+} // namespace Main
+
+class Painter;
+
+[[nodiscard]] QString FilterChatsTypeName(Data::ChatFilter::Flag flag);
+void PaintFilterChatsTypeIcon(
+ Painter &p,
+ Data::ChatFilter::Flag flag,
+ int x,
+ int y,
+ int outerWidth,
+ int size);
+
+class EditFilterChatsListController final : public ChatsListBoxController {
+public:
+ using Flag = Data::ChatFilter::Flag;
+ using Flags = Data::ChatFilter::Flags;
+
+ EditFilterChatsListController(
+ not_null navigation,
+ rpl::producer title,
+ Flags options,
+ Flags selected,
+ const base::flat_set> &peers);
+
+ [[nodiscard]] Main::Session &session() const override;
+ [[nodiscard]] Flags chosenOptions() const {
+ return _selected;
+ }
+
+ void rowClicked(not_null row) override;
+ void itemDeselectedHook(not_null peer) override;
+ bool isForeignRow(PeerListRowId itemId) override;
+ bool handleDeselectForeignRow(PeerListRowId itemId) override;
+
+private:
+ void prepareViewHook() override;
+ std::unique_ptr createRow(not_null history) override;
+ [[nodiscard]] object_ptr prepareTypesList();
+
+ void updateTitle();
+
+ const not_null _navigation;
+ rpl::producer _title;
+ base::flat_set> _peers;
+ Flags _options;
+ Flags _selected;
+
+ Fn _deselectOption;
+
+ rpl::lifetime _lifetime;
+
+};
diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp
index 6434c8c8e..1e1f12c9e 100644
--- a/Telegram/SourceFiles/boxes/peer_list_box.cpp
+++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp
@@ -34,10 +34,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include
-auto PaintUserpicCallback(
- not_null peer,
- bool respectSavedMessagesChat)
--> Fn {
+PaintRoundImageCallback PaintUserpicCallback(
+ not_null peer,
+ bool respectSavedMessagesChat) {
if (respectSavedMessagesChat && peer->isSelf()) {
return [](Painter &p, int x, int y, int outerWidth, int size) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
@@ -76,9 +75,12 @@ void PeerListBox::createMultiSelect() {
searchQueryChanged(query);
});
_select->entity()->setItemRemovedCallback([=](uint64 itemId) {
+ if (_controller->handleDeselectForeignRow(itemId)) {
+ return;
+ }
if (const auto peer = _controller->session().data().peerLoaded(itemId)) {
if (const auto row = peerListFindRow(peer->id)) {
- content()->changeCheckState(row, false, PeerListRow::SetStyle::Animated);
+ content()->changeCheckState(row, false, anim::type::normal);
update();
}
_controller->itemDeselectedHook(peer);
@@ -186,21 +188,35 @@ void PeerListBox::setInnerFocus() {
void PeerListBox::peerListSetRowChecked(
not_null row,
bool checked) {
- auto peer = row->peer();
if (checked) {
- addSelectItem(peer, PeerListRow::SetStyle::Animated);
+ addSelectItem(row, anim::type::normal);
PeerListContentDelegate::peerListSetRowChecked(row, checked);
peerListUpdateRow(row);
// This call deletes row from _searchRows.
- _select->entity()->clearQuery();
+ //_select->entity()->clearQuery();
} else {
// The itemRemovedCallback will call changeCheckState() here.
- _select->entity()->removeItem(peer->id);
+ _select->entity()->removeItem(row->id());
peerListUpdateRow(row);
}
}
+void PeerListBox::peerListSetForeignRowChecked(
+ not_null row,
+ bool checked,
+ anim::type animated) {
+ if (checked) {
+ addSelectItem(row, animated);
+
+ // This call deletes row from _searchRows.
+ //_select->entity()->clearQuery();
+ } else {
+ // The itemRemovedCallback will call changeCheckState() here.
+ _select->entity()->removeItem(row->id());
+ }
+}
+
void PeerListBox::peerListScrollToTop() {
onScrollToY(0);
}
@@ -296,38 +312,62 @@ int PeerListController::contentWidth() const {
return st::boxWideWidth;
}
-void PeerListBox::addSelectItem(not_null peer, PeerListRow::SetStyle style) {
- if (!_select) {
- createMultiSelect();
- _select->hide(anim::type::instant);
- }
+void PeerListBox::addSelectItem(
+ not_null peer,
+ anim::type animated) {
const auto respect = _controller->respectSavedMessagesChat();
const auto text = (respect && peer->isSelf())
? tr::lng_saved_short(tr::now)
: peer->shortName();
- const auto callback = PaintUserpicCallback(peer, respect);
- if (style == PeerListRow::SetStyle::Fast) {
+ addSelectItem(
+ peer->id,
+ text,
+ PaintUserpicCallback(peer, respect),
+ animated);
+}
+
+void PeerListBox::addSelectItem(
+ not_null row,
+ anim::type animated) {
+ addSelectItem(
+ row->id(),
+ row->generateShortName(),
+ row->generatePaintUserpicCallback(),
+ animated);
+}
+
+void PeerListBox::addSelectItem(
+ uint64 itemId,
+ const QString &text,
+ Ui::MultiSelect::PaintRoundImage paintUserpic,
+ anim::type animated) {
+ if (!_select) {
+ createMultiSelect();
+ _select->hide(anim::type::instant);
+ }
+ if (animated == anim::type::instant) {
_select->entity()->addItemInBunch(
- peer->id,
+ itemId,
text,
st::activeButtonBg,
- std::move(callback));
+ std::move(paintUserpic));
} else {
_select->entity()->addItem(
- peer->id,
+ itemId,
text,
st::activeButtonBg,
- std::move(callback));
+ std::move(paintUserpic));
}
}
void PeerListBox::peerListFinishSelectedRowsBunch() {
Expects(_select != nullptr);
+
_select->entity()->finishItemsBunch();
}
-bool PeerListBox::peerListIsRowSelected(not_null peer) {
- return _select ? _select->entity()->hasItem(peer->id) : false;
+bool PeerListBox::peerListIsRowChecked(not_null row) {
+ return _select ? _select->entity()->hasItem(row->id()) : false;
}
int PeerListBox::peerListSelectedRowsCount() {
@@ -343,13 +383,16 @@ auto PeerListBox::peerListCollectSelectedRows()
if (!items.empty()) {
result.reserve(items.size());
for (const auto itemId : items) {
- result.push_back(_controller->session().data().peer(itemId));
+ if (!_controller->isForeignRow(itemId)) {
+ result.push_back(_controller->session().data().peer(itemId));
+ }
}
}
return result;
}
-PeerListRow::PeerListRow(not_null peer) : PeerListRow(peer, peer->id) {
+PeerListRow::PeerListRow(not_null peer)
+: PeerListRow(peer, peer->id) {
}
PeerListRow::PeerListRow(not_null peer, PeerListRowId id)
@@ -360,6 +403,15 @@ PeerListRow::PeerListRow(not_null peer, PeerListRowId id)
, _isSavedMessagesChat(false) {
}
+PeerListRow::PeerListRow(PeerListRowId id)
+: _id(id)
+, _initialized(false)
+, _isSearchResult(false)
+, _isSavedMessagesChat(false) {
+}
+
+PeerListRow::~PeerListRow() = default;
+
bool PeerListRow::checked() const {
return _checkbox && _checkbox->checked();
}
@@ -376,7 +428,7 @@ void PeerListRow::clearCustomStatus() {
}
void PeerListRow::refreshStatus() {
- if (!_initialized || _statusType == StatusType::Custom) {
+ if (!_initialized || special() || _statusType == StatusType::Custom) {
return;
}
_statusType = StatusType::LastSeen;
@@ -420,11 +472,31 @@ void PeerListRow::refreshName(const style::PeerListItem &st) {
}
const auto text = _isSavedMessagesChat
? tr::lng_saved_messages(tr::now)
- : peer()->name;
+ : generateName();
_name.setText(st.nameStyle, text, Ui::NameTextOptions());
}
-PeerListRow::~PeerListRow() = default;
+QString PeerListRow::generateName() {
+ return peer()->name;
+}
+
+QString PeerListRow::generateShortName() {
+ return _isSavedMessagesChat
+ ? tr::lng_saved_short(tr::now)
+ : peer()->shortName();
+}
+
+PaintRoundImageCallback PeerListRow::generatePaintUserpicCallback() {
+ const auto saved = _isSavedMessagesChat;
+ const auto peer = this->peer();
+ return [=](Painter &p, int x, int y, int outerWidth, int size) {
+ if (saved) {
+ Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
+ } else {
+ peer->paintUserpicLeft(p, x, y, outerWidth, size);
+ }
+ };
+}
void PeerListRow::invalidatePixmapsCache() {
if (_checkbox) {
@@ -433,7 +505,9 @@ void PeerListRow::invalidatePixmapsCache() {
}
int PeerListRow::nameIconWidth() const {
- return _peer->isVerified() ? st::dialogsVerifiedIcon.width() : 0;
+ return (special() || !_peer->isVerified())
+ ? 0
+ : st::dialogsVerifiedIcon.width();
}
void PeerListRow::paintNameIcon(
@@ -505,10 +579,8 @@ void PeerListRow::paintUserpic(
paintDisabledCheckUserpic(p, st, x, y, outerWidth);
} else if (_checkbox) {
_checkbox->paint(p, x, y, outerWidth);
- } else if (_isSavedMessagesChat) {
- Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, st.photoSize);
- } else {
- peer()->paintUserpicLeft(p, x, y, outerWidth, st.photoSize);
+ } else if (const auto callback = generatePaintUserpicCallback()) {
+ callback(p, x, y, outerWidth, st.photoSize);
}
}
@@ -594,18 +666,19 @@ void PeerListRow::lazyInitialize(const style::PeerListItem &st) {
refreshStatus();
}
-void PeerListRow::createCheckbox(Fn updateCallback) {
+void PeerListRow::createCheckbox(
+ const style::RoundImageCheckbox &st,
+ Fn updateCallback) {
_checkbox = std::make_unique(
- st::contactsPhotoCheckbox,
+ st,
std::move(updateCallback),
- PaintUserpicCallback(_peer, _isSavedMessagesChat));
+ generatePaintUserpicCallback());
}
-void PeerListRow::setCheckedInternal(bool checked, SetStyle style) {
+void PeerListRow::setCheckedInternal(bool checked, anim::type animated) {
Expects(_checkbox != nullptr);
- using CheckboxStyle = Ui::RoundCheckbox::SetStyle;
- auto speed = (style == SetStyle::Animated) ? CheckboxStyle::Animated : CheckboxStyle::Fast;
- _checkbox->setChecked(checked, speed);
+
+ _checkbox->setChecked(checked, animated);
}
PeerListContent::PeerListContent(
@@ -672,25 +745,30 @@ void PeerListContent::appendFoundRow(not_null row) {
void PeerListContent::changeCheckState(
not_null row,
bool checked,
- PeerListRow::SetStyle style) {
+ anim::type animated) {
row->setChecked(
checked,
- style,
- [this, row] { updateRow(row); });
+ _st.item.checkbox,
+ animated,
+ [=] { updateRow(row); });
}
void PeerListContent::addRowEntry(not_null row) {
- if (_controller->respectSavedMessagesChat() && row->peer()->isSelf()) {
+ if (_controller->respectSavedMessagesChat()
+ && !row->special()
+ && row->peer()->isSelf()) {
row->setIsSavedMessagesChat(true);
}
_rowsById.emplace(row->id(), row);
- _rowsByPeer[row->peer()].push_back(row);
+ if (!row->special()) {
+ _rowsByPeer[row->peer()].push_back(row);
+ }
if (addingToSearchIndex()) {
addToSearchIndex(row);
}
- if (_controller->isRowSelected(row->peer())) {
- Assert(row->id() == row->peer()->id);
- changeCheckState(row, true, PeerListRow::SetStyle::Fast);
+ if (_controller->isRowSelected(row)) {
+ Assert(row->special() || row->id() == row->peer()->id);
+ changeCheckState(row, true, anim::type::instant);
}
}
@@ -706,7 +784,7 @@ bool PeerListContent::addingToSearchIndex() const {
}
void PeerListContent::addToSearchIndex(not_null row) {
- if (row->isSearchResult()) {
+ if (row->isSearchResult() || row->special()) {
return;
}
@@ -799,8 +877,10 @@ void PeerListContent::removeRow(not_null row) {
setContexted(Selected());
_rowsById.erase(row->id());
- auto &byPeer = _rowsByPeer[row->peer()];
- byPeer.erase(ranges::remove(byPeer, row), end(byPeer));
+ if (!row->special()) {
+ auto &byPeer = _rowsByPeer[row->peer()];
+ byPeer.erase(ranges::remove(byPeer, row), end(byPeer));
+ }
removeFromSearchIndex(row);
_filterResults.erase(
ranges::remove(_filterResults, row),
@@ -1143,8 +1223,11 @@ void PeerListContent::setPressed(Selected pressed) {
_pressed = pressed;
}
-crl::time PeerListContent::paintRow(Painter &p, crl::time ms, RowIndex index) {
- auto row = getRow(index);
+crl::time PeerListContent::paintRow(
+ Painter &p,
+ crl::time ms,
+ RowIndex index) {
+ const auto row = getRow(index);
Assert(row != nullptr);
row->lazyInitialize(_st.item);
@@ -1155,17 +1238,17 @@ crl::time PeerListContent::paintRow(Painter &p, crl::time ms, RowIndex index) {
refreshStatusAt = row->refreshStatusTime();
}
- auto peer = row->peer();
- auto user = peer->asUser();
- auto active = (_contexted.index.value >= 0)
+ const auto peer = row->special() ? nullptr : row->peer().get();
+ const auto user = peer ? peer->asUser() : nullptr;
+ const auto active = (_contexted.index.value >= 0)
? _contexted
: (_pressed.index.value >= 0)
? _pressed
: _selected;
- auto selected = (active.index == index);
- auto actionSelected = (selected && active.action);
+ const auto selected = (active.index == index);
+ const auto actionSelected = (selected && active.action);
- auto &bg = selected
+ const auto &bg = selected
? _st.item.button.textBgOver
: _st.item.button.textBg;
p.fillRect(0, 0, width(), _rowHeight, bg);
@@ -1231,12 +1314,17 @@ crl::time PeerListContent::paintRow(Painter &p, crl::time ms, RowIndex index) {
}
p.setFont(st::contactsStatusFont);
- if (row->isSearchResult() && !_mentionHighlight.isEmpty() && peer->userName().startsWith(_mentionHighlight, Qt::CaseInsensitive)) {
- auto username = peer->userName();
- auto availableWidth = statusw;
+ if (row->isSearchResult()
+ && !_mentionHighlight.isEmpty()
+ && peer
+ && peer->userName().startsWith(
+ _mentionHighlight,
+ Qt::CaseInsensitive)) {
+ const auto username = peer->userName();
+ const auto availableWidth = statusw;
auto highlightedPart = '@' + username.mid(0, _mentionHighlight.size());
auto grayedPart = username.mid(_mentionHighlight.size());
- auto highlightedWidth = st::contactsStatusFont->width(highlightedPart);
+ const auto highlightedWidth = st::contactsStatusFont->width(highlightedPart);
if (highlightedWidth >= availableWidth || grayedPart.isEmpty()) {
if (highlightedWidth > availableWidth) {
highlightedPart = st::contactsStatusFont->elided(highlightedPart, availableWidth);
@@ -1347,7 +1435,10 @@ void PeerListContent::loadProfilePhotos() {
if (to > rowsCount) to = rowsCount;
for (auto index = from; index != to; ++index) {
- getRow(RowIndex(index))->peer()->loadUserpic();
+ const auto row = getRow(RowIndex(index));
+ if (!row->special()) {
+ row->peer()->loadUserpic();
+ }
}
}
}
@@ -1360,13 +1451,13 @@ void PeerListContent::checkScrollForPreload() {
}
void PeerListContent::searchQueryChanged(QString query) {
- auto searchWordsList = TextUtilities::PrepareSearchWords(query);
- auto normalizedQuery = searchWordsList.join(' ');
+ const auto searchWordsList = TextUtilities::PrepareSearchWords(query);
+ const auto normalizedQuery = searchWordsList.join(' ');
if (_normalizedSearchQuery != normalizedQuery) {
setSearchQuery(query, normalizedQuery);
if (_controller->searchInLocal() && !searchWordsList.isEmpty()) {
auto minimalList = (const std::vector>*)nullptr;
- for_const (auto &searchWord, searchWordsList) {
+ for (const auto &searchWord : searchWordsList) {
auto searchWordStart = searchWord[0].toLower();
auto it = _searchIndex.find(searchWordStart);
if (it == _searchIndex.cend()) {
@@ -1390,7 +1481,7 @@ void PeerListContent::searchQueryChanged(QString query) {
};
auto allSearchWordsInNames = [&](
not_null peer) {
- for_const (auto &searchWord, searchWordsList) {
+ for (const auto &searchWord : searchWordsList) {
if (!searchWordInNames(peer, searchWord)) {
return false;
}
@@ -1399,8 +1490,8 @@ void PeerListContent::searchQueryChanged(QString query) {
};
_filterResults.reserve(minimalList->size());
- for_const (auto row, *minimalList) {
- if (allSearchWordsInNames(row->peer())) {
+ for (const auto row : *minimalList) {
+ if (!row->special() && allSearchWordsInNames(row->peer())) {
_filterResults.push_back(row);
}
}
diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h
index 8903aefd0..0490bd320 100644
--- a/Telegram/SourceFiles/boxes/peer_list_box.h
+++ b/Telegram/SourceFiles/boxes/peer_list_box.h
@@ -38,22 +38,31 @@ namespace Notify {
struct PeerUpdate;
} // namespace Notify
-auto PaintUserpicCallback(
+using PaintRoundImageCallback = Fn;
+
+[[nodiscard]] PaintRoundImageCallback PaintUserpicCallback(
not_null peer,
- bool respectSavedMessagesChat)
--> Fn;
+ bool respectSavedMessagesChat);
using PeerListRowId = uint64;
class PeerListRow {
public:
- PeerListRow(not_null peer);
- PeerListRow(not_null peer, PeerListRowId id);
-
enum class State {
Active,
Disabled,
DisabledChecked,
};
+
+ explicit PeerListRow(not_null peer);
+ PeerListRow(not_null peer, PeerListRowId id);
+
+ virtual ~PeerListRow();
+
void setDisabledState(State state) {
_disabledState = state;
}
@@ -62,20 +71,28 @@ public:
// not by the row itself, so there is no setChecked() method.
// We can query the checked state from row, but before it is
// added to the box it is always false.
- bool checked() const;
+ [[nodiscard]] bool checked() const;
+
+ [[nodiscard]] bool special() const {
+ return !_peer;
+ }
+ [[nodiscard]] not_null peer() const {
+ Expects(!special());
- not_null peer() const {
return _peer;
}
- PeerListRowId id() const {
+ [[nodiscard]] PeerListRowId id() const {
return _id;
}
+ [[nodiscard]] virtual QString generateName();
+ [[nodiscard]] virtual QString generateShortName();
+ [[nodiscard]] virtual auto generatePaintUserpicCallback()
+ -> PaintRoundImageCallback;
+
void setCustomStatus(const QString &status);
void clearCustomStatus();
- virtual ~PeerListRow();
-
// Box interface.
virtual int nameIconWidth() const;
virtual void paintNameIcon(
@@ -146,19 +163,16 @@ public:
_isSavedMessagesChat = isSavedMessagesChat;
}
- enum class SetStyle {
- Animated,
- Fast,
- };
template
void setChecked(
bool checked,
- SetStyle style,
+ const style::RoundImageCheckbox &st,
+ anim::type animated,
UpdateCallback callback) {
if (checked && !_checkbox) {
- createCheckbox(std::move(callback));
+ createCheckbox(st, std::move(callback));
}
- setCheckedInternal(checked, style);
+ setCheckedInternal(checked, animated);
}
void invalidatePixmapsCache();
@@ -200,9 +214,13 @@ protected:
return _initialized;
}
+ explicit PeerListRow(PeerListRowId id);
+
private:
- void createCheckbox(Fn updateCallback);
- void setCheckedInternal(bool checked, SetStyle style);
+ void createCheckbox(
+ const style::RoundImageCheckbox &st,
+ Fn updateCallback);
+ void setCheckedInternal(bool checked, anim::type animated);
void paintDisabledCheckUserpic(
Painter &p,
const style::PeerListItem &st,
@@ -212,7 +230,7 @@ private:
void setStatusText(const QString &text);
PeerListRowId _id = 0;
- not_null _peer;
+ PeerData *_peer = nullptr;
std::unique_ptr _ripple;
std::unique_ptr _checkbox;
Ui::Text::String _name;
@@ -253,8 +271,12 @@ public:
virtual void peerListUpdateRow(not_null row) = 0;
virtual void peerListRemoveRow(not_null row) = 0;
virtual void peerListConvertRowToSearchResult(not_null row) = 0;
- virtual bool peerListIsRowSelected(not_null peer) = 0;
+ virtual bool peerListIsRowChecked(not_null row) = 0;
virtual void peerListSetRowChecked(not_null row, bool checked) = 0;
+ virtual void peerListSetForeignRowChecked(
+ not_null row,
+ bool checked,
+ anim::type animated) = 0;
virtual not_null peerListRowAt(int index) = 0;
virtual void peerListRefreshRows() = 0;
virtual void peerListScrollToTop() = 0;
@@ -264,9 +286,17 @@ public:
virtual int peerListPartitionRows(Fn border) = 0;
template
- void peerListAddSelectedRows(PeerDataRange &&range) {
- for (auto peer : range) {
- peerListAddSelectedRowInBunch(peer);
+ void peerListAddSelectedPeers(PeerDataRange &&range) {
+ for (const auto peer : range) {
+ peerListAddSelectedPeerInBunch(peer);
+ }
+ peerListFinishSelectedRowsBunch();
+ }
+
+ template
+ void peerListAddSelectedRows(PeerListRowRange &&range) {
+ for (const auto row : range) {
+ peerListAddSelectedRowInBunch(row);
}
peerListFinishSelectedRowsBunch();
}
@@ -279,7 +309,8 @@ public:
virtual ~PeerListDelegate() = default;
private:
- virtual void peerListAddSelectedRowInBunch(not_null peer) = 0;
+ virtual void peerListAddSelectedPeerInBunch(not_null peer) = 0;
+ virtual void peerListAddSelectedRowInBunch(not_null row) = 0;
virtual void peerListFinishSelectedRowsBunch() = 0;
};
@@ -353,6 +384,12 @@ public:
}
virtual void itemDeselectedHook(not_null peer) {
}
+ virtual bool isForeignRow(PeerListRowId itemId) {
+ return false;
+ }
+ virtual bool handleDeselectForeignRow(PeerListRowId itemId) {
+ return false;
+ }
virtual base::unique_qptr rowContextMenu(
QWidget *parent,
not_null row);
@@ -374,8 +411,8 @@ public:
virtual int contentWidth() const;
- bool isRowSelected(not_null peer) {
- return delegate()->peerListIsRowSelected(peer);
+ bool isRowSelected(not_null row) {
+ return delegate()->peerListIsRowChecked(row);
}
virtual bool searchInLocal() {
@@ -478,7 +515,10 @@ public:
void refreshRows();
void setSearchMode(PeerListSearchMode mode);
- void changeCheckState(not_null row, bool checked, PeerListRow::SetStyle style);
+ void changeCheckState(
+ not_null row,
+ bool checked,
+ anim::type animated);
template
void reorderRows(ReorderCallback &&callback) {
@@ -685,10 +725,12 @@ public:
void peerListSetRowChecked(
not_null