Compare commits
216 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcf91e2f2c | ||
|
|
1e9087db4a | ||
|
|
c5cb928724 | ||
|
|
e1350d6813 | ||
|
|
898581badd | ||
|
|
71570f5be1 | ||
|
|
8c38d31950 | ||
|
|
c8407e5bde | ||
|
|
2ce9e610fa | ||
|
|
6aef6d7f4e | ||
|
|
54841de991 | ||
|
|
fc66a0eea8 | ||
|
|
e9787170d5 | ||
|
|
d57aa2a1f6 | ||
|
|
2fb7bdc803 | ||
|
|
f671897a4d | ||
|
|
7877463468 | ||
|
|
8a99de16f6 | ||
|
|
f46f655a0e | ||
|
|
04a3a50e74 | ||
|
|
d349763460 | ||
|
|
c4b95b40e5 | ||
|
|
0fa2f83cf8 | ||
|
|
abc32c63b0 | ||
|
|
8ae373b654 | ||
|
|
b6395d08d8 | ||
|
|
ad573ecc84 | ||
|
|
1d2a50c407 | ||
|
|
082ffb6cd1 | ||
|
|
9514b0d1f1 | ||
|
|
2ea50f5c85 | ||
|
|
3eca5d206b | ||
|
|
862f4822f2 | ||
|
|
6dff1f11ba | ||
|
|
90b5c6a582 | ||
|
|
e05b813b6e | ||
|
|
e8a4a7b754 | ||
|
|
d909248e25 | ||
|
|
65afa2c402 | ||
|
|
665467b02d | ||
|
|
787cab7417 | ||
|
|
b98b44e638 | ||
|
|
d1455f5117 | ||
|
|
0ffaff2d8b | ||
|
|
2675b5df3b | ||
|
|
627c870dd5 | ||
|
|
db60bee7dc | ||
|
|
2ff341b7d3 | ||
|
|
bb008911b0 | ||
|
|
0ca9bbafad | ||
|
|
0ca983ed71 | ||
|
|
0b4ebcbae4 | ||
|
|
0a011db483 | ||
|
|
37f5576c38 | ||
|
|
b881d24a5a | ||
|
|
4628d4fece | ||
|
|
68e229640b | ||
|
|
1ac0c4142d | ||
|
|
61a61669b6 | ||
|
|
9b576a13bc | ||
|
|
60fe961c21 | ||
|
|
c890281258 | ||
|
|
9615347634 | ||
|
|
864959aee0 | ||
|
|
11906297d8 | ||
|
|
3825586715 | ||
|
|
cc6f63edf4 | ||
|
|
3da787791f | ||
|
|
ad238108bd | ||
|
|
81f40586a3 | ||
|
|
40deda1e9b | ||
|
|
2d50e893b5 | ||
|
|
5ad4719c08 | ||
|
|
f0acc9526e | ||
|
|
d938d91366 | ||
|
|
0b60985966 | ||
|
|
b6fbdd25a0 | ||
|
|
faf6352a11 | ||
|
|
4cfa486d91 | ||
|
|
2dea8941a5 | ||
|
|
9c7fee0bfe | ||
|
|
3394094dd3 | ||
|
|
58f735e19b | ||
|
|
3a8237f03a | ||
|
|
95174a5f36 | ||
|
|
b569078e96 | ||
|
|
33e66d21bd | ||
|
|
480c109b09 | ||
|
|
4eefebc96c | ||
|
|
1697e9e791 | ||
|
|
8fe2ec63b7 | ||
|
|
c1a63164c0 | ||
|
|
cb1041a289 | ||
|
|
67fa4372aa | ||
|
|
a25005483a | ||
|
|
e285b22398 | ||
|
|
4201a0193c | ||
|
|
dffe79fea8 | ||
|
|
454c8db00d | ||
|
|
27aa678f6a | ||
|
|
f361dd0df2 | ||
|
|
f4f5139eb8 | ||
|
|
976c32e5af | ||
|
|
2e85f7e5fc | ||
|
|
3e8b1d9663 | ||
|
|
05b8b9f22e | ||
|
|
b782569faf | ||
|
|
7dfed2a012 | ||
|
|
64125f0cc8 | ||
|
|
8a64a9b2ad | ||
|
|
0b5038aaa2 | ||
|
|
d37666e91a | ||
|
|
458082c738 | ||
|
|
383b100fc7 | ||
|
|
f4a02b126d | ||
|
|
47756fb8c3 | ||
|
|
efa19d5782 | ||
|
|
66e7f1d490 | ||
|
|
2b2d190d2b | ||
|
|
4617ba5fb9 | ||
|
|
65f54d937f | ||
|
|
ac57d46f30 | ||
|
|
0d6e5eda0c | ||
|
|
32c7964e8c | ||
|
|
a27ef55ff8 | ||
|
|
1b6b0c1732 | ||
|
|
ae6d703a44 | ||
|
|
0b25d19e3b | ||
|
|
9130735ed6 | ||
|
|
da7d4687ca | ||
|
|
352ae5100a | ||
|
|
9b25973b49 | ||
|
|
bf27185feb | ||
|
|
5cbf9a2dc4 | ||
|
|
d0e8802b9d | ||
|
|
ad3ec244e9 | ||
|
|
22ca4e25d9 | ||
|
|
a02ded6fd8 | ||
|
|
ca2a0d41c9 | ||
|
|
30a7893afe | ||
|
|
cfc8820bad | ||
|
|
c53b26dec8 | ||
|
|
973dd5c50f | ||
|
|
896bbb7c56 | ||
|
|
bbeefaed9c | ||
|
|
2276a4c9af | ||
|
|
a0ca3beef8 | ||
|
|
ec2299a7e2 | ||
|
|
6eb904acb1 | ||
|
|
7ed020ecc5 | ||
|
|
42c96b4c7f | ||
|
|
e3f2dcec22 | ||
|
|
c3f20c59b5 | ||
|
|
2a06182d1a | ||
|
|
004a60ded5 | ||
|
|
b4a588a676 | ||
|
|
2f261e6f7b | ||
|
|
cda4bca190 | ||
|
|
127bafa254 | ||
|
|
90cea58d28 | ||
|
|
7c11fd58cf | ||
|
|
9600cc0ed5 | ||
|
|
959348f8cd | ||
|
|
1a1fa5db3e | ||
|
|
6f4eef035d | ||
|
|
e351ad1f3d | ||
|
|
292e5bc3f7 | ||
|
|
06cf2b562f | ||
|
|
09de881036 | ||
|
|
1ffbc122e1 | ||
|
|
f05f1f4359 | ||
|
|
7ff0659e91 | ||
|
|
9b1c5b1050 | ||
|
|
77939ae9bd | ||
|
|
63960c647b | ||
|
|
312aa4b130 | ||
|
|
6be2fb9790 | ||
|
|
4444844443 | ||
|
|
7a9961b0e9 | ||
|
|
0faadc8fa0 | ||
|
|
7684dbc701 | ||
|
|
86f9875662 | ||
|
|
fb16375a19 | ||
|
|
8a9d13c6e4 | ||
|
|
b7d9d549ff | ||
|
|
725c22e776 | ||
|
|
b2c5c8ae2c | ||
|
|
a6e5e7ab84 | ||
|
|
b2faed6b96 | ||
|
|
ec10306ccb | ||
|
|
590be6d6e7 | ||
|
|
8dee2a1c8b | ||
|
|
c9308d04b8 | ||
|
|
07791aa2eb | ||
|
|
c78a15410d | ||
|
|
dc459d454f | ||
|
|
db04e33128 | ||
|
|
d1ff6e583d | ||
|
|
a529932556 | ||
|
|
d52cabb386 | ||
|
|
e632ac631e | ||
|
|
3064a41014 | ||
|
|
40e65eb1e6 | ||
|
|
30f057fff5 | ||
|
|
bebf8e4a03 | ||
|
|
463b628f99 | ||
|
|
e9a92c311b | ||
|
|
4c05da604a | ||
|
|
5da26ffdf8 | ||
|
|
66baeb86a1 | ||
|
|
e368de008d | ||
|
|
58c5bb7247 | ||
|
|
f0a96d0aad | ||
|
|
ed3e37b06a | ||
|
|
83052ec056 | ||
|
|
398237b80e |
11
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
@@ -60,8 +60,19 @@ body:
|
||||
- Other (unofficial) source
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Crash ID
|
||||
description: >
|
||||
If you're reporting a crash, please enter the crash ID from the crash reporter
|
||||
opening on the next launch after crash. **You have to enable beta versions
|
||||
installation in Settings -> Advanced for the reporter to appear.**
|
||||
You don't have to wait for a beta version to arrive.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Logs
|
||||
description: >
|
||||
You can find log.txt using the `viewlogs`
|
||||
[cheat code](https://github.com/telegramdesktop/tdesktop/wiki/Cheat-Codes).
|
||||
placeholder: Insert log.txt here (if necessary)
|
||||
render: shell
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml
vendored
@@ -2,7 +2,6 @@
|
||||
name: Feature request
|
||||
description: Suggest an idea.
|
||||
labels: [enhancement]
|
||||
title: "[Feature Request] "
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
|
||||
1
.github/workflows/linux.yml
vendored
@@ -57,7 +57,6 @@ jobs:
|
||||
matrix:
|
||||
defines:
|
||||
- ""
|
||||
- "DESKTOP_APP_DISABLE_DBUS_INTEGRATION"
|
||||
- "DESKTOP_APP_DISABLE_X11_INTEGRATION"
|
||||
- "DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION"
|
||||
|
||||
|
||||
2
.github/workflows/snap.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
snap:
|
||||
name: Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
env:
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
|
||||
2
.github/workflows/winget.yml
vendored
@@ -10,9 +10,11 @@ jobs:
|
||||
uses: telegramdesktop/winget-releaser@main
|
||||
with:
|
||||
identifier: Telegram.TelegramDesktop
|
||||
installers-regex: 't(setup|portable).*(exe|zip)$'
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
||||
- if: github.event.action == 'prereleased'
|
||||
uses: telegramdesktop/winget-releaser@main
|
||||
with:
|
||||
identifier: Telegram.TelegramDesktop.Beta
|
||||
installers-regex: 't(setup|portable).*(exe|zip)$'
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
||||
|
||||
3
.gitmodules
vendored
@@ -100,3 +100,6 @@
|
||||
[submodule "Telegram/ThirdParty/cld3"]
|
||||
path = Telegram/ThirdParty/cld3
|
||||
url = https://github.com/google/cld3.git
|
||||
[submodule "Telegram/ThirdParty/wayland"]
|
||||
path = Telegram/ThirdParty/wayland
|
||||
url = https://github.com/gitlab-freedesktop-mirrors/wayland.git
|
||||
|
||||
@@ -56,7 +56,7 @@ include(cmake/options.cmake)
|
||||
|
||||
if (NOT DESKTOP_APP_USE_PACKAGED)
|
||||
if (WIN32)
|
||||
set(qt_version 5.15.8)
|
||||
set(qt_version 5.15.9)
|
||||
elseif (APPLE)
|
||||
set(qt_version 6.3.2)
|
||||
endif()
|
||||
|
||||
@@ -173,6 +173,8 @@ PRIVATE
|
||||
boxes/filters/edit_filter_box.h
|
||||
boxes/filters/edit_filter_chats_list.cpp
|
||||
boxes/filters/edit_filter_chats_list.h
|
||||
boxes/filters/edit_filter_links.cpp
|
||||
boxes/filters/edit_filter_links.h
|
||||
boxes/peers/add_bot_to_chat_box.cpp
|
||||
boxes/peers/add_bot_to_chat_box.h
|
||||
boxes/peers/add_participants_box.cpp
|
||||
@@ -228,8 +230,6 @@ PRIVATE
|
||||
boxes/background_box.h
|
||||
boxes/background_preview_box.cpp
|
||||
boxes/background_preview_box.h
|
||||
boxes/change_phone_box.cpp
|
||||
boxes/change_phone_box.h
|
||||
boxes/choose_filter_box.cpp
|
||||
boxes/choose_filter_box.h
|
||||
boxes/connection_box.cpp
|
||||
@@ -348,6 +348,8 @@ PRIVATE
|
||||
calls/calls_video_bubble.h
|
||||
calls/calls_video_incoming.cpp
|
||||
calls/calls_video_incoming.h
|
||||
chat_helpers/compose/compose_show.cpp
|
||||
chat_helpers/compose/compose_show.h
|
||||
chat_helpers/bot_command.cpp
|
||||
chat_helpers/bot_command.h
|
||||
chat_helpers/bot_keyboard.cpp
|
||||
@@ -922,6 +924,8 @@ PRIVATE
|
||||
main/main_account.h
|
||||
main/main_app_config.cpp
|
||||
main/main_app_config.h
|
||||
main/main_app_config_values.cpp
|
||||
main/main_app_config_values.h
|
||||
main/main_domain.cpp
|
||||
main/main_domain.h
|
||||
main/main_session.cpp
|
||||
@@ -930,6 +934,8 @@ PRIVATE
|
||||
main/main_session_settings.h
|
||||
main/session/send_as_peers.cpp
|
||||
main/session/send_as_peers.h
|
||||
main/session/session_show.cpp
|
||||
main/session/session_show.h
|
||||
media/system_media_controls_manager.h
|
||||
media/system_media_controls_manager.cpp
|
||||
media/audio/media_audio.cpp
|
||||
@@ -1078,7 +1084,6 @@ PRIVATE
|
||||
platform/linux/integration_linux.h
|
||||
platform/linux/main_window_linux.cpp
|
||||
platform/linux/main_window_linux.h
|
||||
platform/linux/notifications_manager_linux_dummy.cpp
|
||||
platform/linux/notifications_manager_linux.cpp
|
||||
platform/linux/notifications_manager_linux.h
|
||||
platform/linux/overlay_widget_linux.h
|
||||
@@ -1397,18 +1402,6 @@ if (NOT build_winstore)
|
||||
)
|
||||
endif()
|
||||
|
||||
if (DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
|
||||
remove_target_sources(Telegram ${src_loc}
|
||||
platform/linux/linux_xdp_open_with_dialog.cpp
|
||||
platform/linux/linux_xdp_open_with_dialog.h
|
||||
platform/linux/notifications_manager_linux.cpp
|
||||
)
|
||||
else()
|
||||
remove_target_sources(Telegram ${src_loc}
|
||||
platform/linux/notifications_manager_linux_dummy.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if (DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION)
|
||||
remove_target_sources(Telegram ${src_loc}
|
||||
platform/linux/linux_wayland_integration.cpp
|
||||
@@ -1518,12 +1511,13 @@ elseif (APPLE)
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
if (NOT DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
desktop-app::external_glibmm
|
||||
)
|
||||
endif()
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
desktop-app::external_glibmm
|
||||
)
|
||||
|
||||
include(${cmake_helpers_loc}/external/glib/generate_dbus.cmake)
|
||||
generate_dbus(Telegram org.freedesktop.portal. XdpInhibit ${src_loc}/platform/linux/org.freedesktop.portal.Inhibit.xml)
|
||||
|
||||
if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
|
||||
target_link_libraries(Telegram
|
||||
@@ -1535,6 +1529,7 @@ else()
|
||||
if (NOT DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION)
|
||||
qt_generate_wayland_protocol_client_sources(Telegram
|
||||
FILES
|
||||
${third_party_loc}/wayland/protocol/wayland.xml
|
||||
${third_party_loc}/plasma-wayland-protocols/src/protocols/plasma-shell.xml
|
||||
)
|
||||
|
||||
@@ -1724,7 +1719,6 @@ endif()
|
||||
|
||||
if (LINUX AND DESKTOP_APP_USE_PACKAGED)
|
||||
include(GNUInstallDirs)
|
||||
configure_file("../lib/xdg/org.telegram.desktop.desktop" "${CMAKE_CURRENT_BINARY_DIR}/org.telegram.desktop.desktop" @ONLY)
|
||||
configure_file("../lib/xdg/org.telegram.desktop.metainfo.xml" "${CMAKE_CURRENT_BINARY_DIR}/org.telegram.desktop.metainfo.xml" @ONLY)
|
||||
generate_appdata_changelog(Telegram "${CMAKE_SOURCE_DIR}/changelog.txt" "${CMAKE_CURRENT_BINARY_DIR}/org.telegram.desktop.metainfo.xml")
|
||||
install(TARGETS Telegram RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
@@ -1735,6 +1729,6 @@ if (LINUX AND DESKTOP_APP_USE_PACKAGED)
|
||||
install(FILES "Resources/art/icon128.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps" RENAME "telegram.png")
|
||||
install(FILES "Resources/art/icon256.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps" RENAME "telegram.png")
|
||||
install(FILES "Resources/art/icon512.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps" RENAME "telegram.png")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/org.telegram.desktop.desktop" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications")
|
||||
install(FILES "../lib/xdg/org.telegram.desktop.desktop" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/org.telegram.desktop.metainfo.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo")
|
||||
endif()
|
||||
|
||||
BIN
Telegram/Resources/animations/cloud_filters.tgs
Normal file
BIN
Telegram/Resources/icons/chat/input_draw.png
Normal file
|
After Width: | Height: | Size: 953 B |
BIN
Telegram/Resources/icons/chat/input_draw@2x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
Telegram/Resources/icons/chat/input_draw@3x.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
Telegram/Resources/icons/chat/input_replace.png
Normal file
|
After Width: | Height: | Size: 749 B |
BIN
Telegram/Resources/icons/chat/input_replace@2x.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/Resources/icons/chat/input_replace@3x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
Telegram/Resources/icons/menu/header_mode_day.png
Normal file
|
After Width: | Height: | Size: 420 B |
BIN
Telegram/Resources/icons/menu/header_mode_day@2x.png
Normal file
|
After Width: | Height: | Size: 891 B |
BIN
Telegram/Resources/icons/menu/header_mode_day@3x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/menu/header_mode_night.png
Normal file
|
After Width: | Height: | Size: 636 B |
BIN
Telegram/Resources/icons/menu/header_mode_night@2x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/menu/header_mode_night@3x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
Telegram/Resources/icons/settings/folder_links.png
Normal file
|
After Width: | Height: | Size: 467 B |
BIN
Telegram/Resources/icons/settings/folder_links@2x.png
Normal file
|
After Width: | Height: | Size: 883 B |
BIN
Telegram/Resources/icons/settings/folder_links@3x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
@@ -232,6 +232,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_file_size_limit1" = "The document can't be sent, because it is larger than {size}.";
|
||||
"lng_file_size_limit2" = "You can double this limit to {size} per document by subscribing to **Telegram Premium**.";
|
||||
|
||||
"lng_filter_links_limit_title" = "Limit Reached";
|
||||
"lng_filter_links_limit1#one" = "Sorry, you can't create more than **{count}** invite link.";
|
||||
"lng_filter_links_limit1#other" = "Sorry, you can't create more than **{count}** invite links.";
|
||||
"lng_filter_links_limit2#one" = "You can increase the limit to **{count}** link by subscribing to **Telegram Premium**.";
|
||||
"lng_filter_links_limit2#other" = "You can increase the limit to **{count}** links by subscribing to **Telegram Premium**.";
|
||||
|
||||
"lng_filter_shared_limit_title" = "Limit Reached";
|
||||
"lng_filter_shared_limit1#one" = "Sorry, you can't add more than **{count}** shareable folders.";
|
||||
"lng_filter_shared_limit1#other" = "Sorry, you can't add more than **{count}** shareable folders.";
|
||||
"lng_filter_shared_limit2#one" = "You can increase the limit up to **{count}** folder by subscribing to **Telegram Premium**.";
|
||||
"lng_filter_shared_limit2#other" = "You can increase the limit up to **{count}** folders by subscribing to **Telegram Premium**.";
|
||||
|
||||
"lng_limits_increase" = "Increase Limit";
|
||||
|
||||
"lng_sticker_premium_text" = "This pack contains premium stickers like this one.";
|
||||
@@ -373,6 +385,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_dlg_filter" = "Search";
|
||||
"lng_dlg_new_group_name" = "Group name";
|
||||
"lng_dlg_new_channel_name" = "Channel name";
|
||||
"lng_dlg_new_bot_name" = "Bot name";
|
||||
"lng_no_chats" = "Your chats will be here";
|
||||
"lng_no_chats_filter" = "No chats currently belong to this folder.";
|
||||
"lng_contacts_loading" = "Loading...";
|
||||
@@ -386,7 +399,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_save" = "Save";
|
||||
|
||||
"lng_username_title" = "Username";
|
||||
"lng_username_description" = "You can choose a username on Telegram. If you do, other people will be able to find you by this username and contact you without knowing your phone number.\n\nYou can use **a-z**, **0-9** and **underscores**.\nMinimum length is **5 characters**.";
|
||||
"lng_username_description1" = "You can choose a username on Telegram. If you do, other people will be able to find you by this username and contact you without knowing your phone number.";
|
||||
"lng_username_description2" = "You can use **a-z**, **0-9** and **underscores**.\nMinimum length is **5 characters**.";
|
||||
"lng_username_choose" = "Choose your username.";
|
||||
"lng_username_invalid" = "This username is invalid.";
|
||||
"lng_username_occupied" = "This username is already occupied.";
|
||||
@@ -417,6 +431,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_channel_usernames_deactivate_description" = "Do you want to hide this link from the channel info page?";
|
||||
"lng_channel_usernames_description" = "Drag and drop links to change the order in which they will be displayed on the channel info page.";
|
||||
|
||||
"lng_bot_username_title" = "Username";
|
||||
"lng_bot_username_description1" = "This link cannot be edited. You can acquire additional usernames on {link}.";
|
||||
"lng_bot_username_description1_link" = "Fragment";
|
||||
"lng_bot_usernames_activate_description" = "Do you want to show this link on the bot info page?";
|
||||
"lng_bot_usernames_deactivate_description" = "Do you want to hide this link from the bot info page?";
|
||||
"lng_bot_usernames_description" = "Drag and drop links to change the order in which they will be displayed on the bot info page.";
|
||||
|
||||
"lng_bio_placeholder" = "Bio";
|
||||
|
||||
"lng_settings_section_info" = "My info";
|
||||
@@ -708,7 +729,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_manage_enabled_dictionary" = "Dictionary is enabled";
|
||||
"lng_settings_manage_remove_dictionary" = "Remove Dictionary";
|
||||
|
||||
"lng_backgrounds_header" = "Choose your new chat background";
|
||||
"lng_backgrounds_header" = "Choose Wallpaper";
|
||||
"lng_theme_sure_keep" = "Keep this theme?";
|
||||
"lng_theme_reverting#one" = "Reverting to the old theme in {count} second.";
|
||||
"lng_theme_reverting#other" = "Reverting to the old theme in {count} seconds.";
|
||||
@@ -728,6 +749,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_background_link_copied" = "Link copied to clipboard";
|
||||
"lng_background_blur" = "Blurred";
|
||||
"lng_background_sure_delete" = "Are you sure you want to delete this background?";
|
||||
"lng_background_other_info" = "{user} will be able to apply this wallpaper";
|
||||
"lng_background_apply1" = "Apply the wallpaper in this chat.";
|
||||
"lng_background_apply2" = "Enjoy the view.";
|
||||
"lng_background_apply_button" = "Apply For This Chat";
|
||||
"lng_background_dimming" = "Background dimming";
|
||||
|
||||
"lng_download_path_ask" = "Ask download path for each file";
|
||||
"lng_download_path" = "Download path";
|
||||
@@ -1030,17 +1056,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_self_destruct_sessions_title" = "Session termination";
|
||||
"lng_self_destruct_sessions_description" = "If you don't come online from a specific session at least once within this period, it will be terminated.";
|
||||
|
||||
"lng_change_phone_title" = "Change phone number";
|
||||
"lng_change_phone_about" = "You can change your Telegram number\nhere. Your account and all your cloud data\n— messages, media, contacts, etc. will be\nmoved to the new number.\n\n**Important**: all your Telegram contacts will\nget your **new number** added to their address\nbook, provided they had your old number and\nyou haven't blocked them in Telegram.";
|
||||
"lng_change_phone_warning" = "All your Telegram contacts will get your new number added to their address book, provided they had your old number and you haven't blocked them in Telegram.";
|
||||
"lng_change_phone_occupied" = "The number {phone} is already connected to a Telegram account. Please delete that account before migrating to the new number.";
|
||||
"lng_change_phone_button" = "Change number";
|
||||
"lng_change_phone_new_title" = "Enter new number";
|
||||
"lng_change_phone_new_description" = "We will send an SMS with a confirmation code to your new number.";
|
||||
"lng_change_phone_new_submit" = "Submit";
|
||||
"lng_change_phone_code_title" = "Enter code";
|
||||
"lng_change_phone_code_description" = "We've sent an SMS with a confirmation code to your phone {phone}.";
|
||||
"lng_change_phone_success" = "Your phone number has been changed.";
|
||||
"lng_change_phone_error" = "You can only change your phone number using mobile Telegram apps. Please use an official Telegram app on your phone to update your number.";
|
||||
|
||||
"lng_mute_menu_mute" = "Mute";
|
||||
"lng_mute_menu_unmute" = "Unmute";
|
||||
@@ -1202,6 +1220,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_manage_group_title" = "Manage Group";
|
||||
"lng_manage_channel_title" = "Manage Channel";
|
||||
"lng_manage_bot_title" = "Manage Bot";
|
||||
"lng_manage_peer_recent_actions" = "Recent Actions";
|
||||
"lng_manage_peer_members" = "Members";
|
||||
"lng_manage_peer_subscribers" = "Subscribers";
|
||||
@@ -1252,6 +1271,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_manage_peer_no_forwards_about" = "Members won't be able to forward messages from this group or save media files.";
|
||||
"lng_manage_peer_no_forwards_about_channel" = "Subscribers won't be able to forward messages from this channel or save media files.";
|
||||
|
||||
"lng_manage_peer_bot_public_link" = "Public Link";
|
||||
"lng_manage_peer_bot_public_links" = "Public Links";
|
||||
"lng_manage_peer_bot_edit_intro" = "Edit Intro";
|
||||
"lng_manage_peer_bot_edit_commands" = "Edit Commands";
|
||||
"lng_manage_peer_bot_edit_settings" = "Change Bot Settings";
|
||||
"lng_manage_peer_bot_about" = "Use {bot} to manage this bot.";
|
||||
"lng_manage_peer_bot_delete" = "Delete Bot";
|
||||
|
||||
"lng_manage_discussion_group" = "Discussion";
|
||||
"lng_manage_discussion_group_add" = "Add a group";
|
||||
"lng_manage_linked_channel" = "Linked channel";
|
||||
@@ -1509,6 +1536,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_suggested_video" = "{user} suggests you to use this profile video.";
|
||||
"lng_action_suggested_video_button" = "View Video";
|
||||
"lng_action_attach_menu_bot_allowed" = "You allowed this bot to message you when you added it in the attachment menu.";
|
||||
"lng_action_set_wallpaper_me" = "You set a new wallpaper for this chat";
|
||||
"lng_action_set_wallpaper" = "{user} set a new wallpaper for this chat";
|
||||
"lng_action_set_wallpaper_button" = "View Wallpaper";
|
||||
"lng_action_set_same_wallpaper_me" = "You set the same wallpaper for this chat";
|
||||
"lng_action_set_same_wallpaper" = "{user} set the same wallpaper for this chat";
|
||||
"lng_action_topic_created_inside" = "Topic created";
|
||||
"lng_action_topic_closed_inside" = "Topic closed";
|
||||
"lng_action_topic_reopened_inside" = "Topic reopened";
|
||||
@@ -1568,6 +1600,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_invite_no_joined" = "No one joined yet";
|
||||
"lng_group_invite_joined#one" = "{count} joined";
|
||||
"lng_group_invite_joined#other" = "{count} joined";
|
||||
"lng_group_invite_joined_via_filter" = "joined via a folder invite link";
|
||||
"lng_group_invite_remaining#one" = "{count} remaining";
|
||||
"lng_group_invite_remaining#other" = "{count} remaining";
|
||||
"lng_group_invite_requested#one" = "{count} requested";
|
||||
@@ -2340,6 +2373,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_contact_phone_will_be_shared" = "You can make your phone visible to {user}.";
|
||||
"lng_edit_contact_title" = "Edit contact name";
|
||||
"lng_edit_channel_title" = "Edit channel";
|
||||
"lng_edit_bot_title" = "Edit bot";
|
||||
"lng_edit_sign_messages" = "Sign messages";
|
||||
"lng_edit_group" = "Edit group";
|
||||
"lng_edit_self_title" = "Edit your name";
|
||||
@@ -2516,7 +2550,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_payments_webview_no_use" = "Unfortunately, you can't use payments with current system configuration.";
|
||||
"lng_payments_webview_install_edge" = "Please install {link}.";
|
||||
"lng_payments_webview_install_webkit" = "Please install WebKitGTK (webkit2gtk-5.0/webkit2gtk-4.1/webkit2gtk-4.0) using your package manager.";
|
||||
"lng_payments_webview_install_webkit" = "Please install WebKitGTK (webkitgtk-6.0/webkit2gtk-4.1/webkit2gtk-4.0) using your package manager.";
|
||||
"lng_payments_webview_update_windows" = "Please update your system to Windows 8.1 or later.";
|
||||
"lng_payments_sure_close" = "Are you sure you want to close this payment form? The changes you made will be lost.";
|
||||
"lng_payments_receipt_label" = "Receipt";
|
||||
@@ -3083,6 +3117,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_participant_joined_channel" = "{from} joined the channel";
|
||||
"lng_admin_log_participant_joined_by_link" = "{from} joined the group via {link}";
|
||||
"lng_admin_log_participant_joined_by_link_channel" = "{from} joined the channel via {link}";
|
||||
"lng_admin_log_participant_joined_by_filter_link" = "{from} joined the group via {link} from a folder";
|
||||
"lng_admin_log_participant_joined_by_filter_link_channel" = "{from} joined the channel via {link} from a folder";
|
||||
"lng_admin_log_participant_approved_by_link" = "{from} was approved to join the group via {link} by {user}";
|
||||
"lng_admin_log_participant_approved_by_link_channel" = "{from} was approved to join the channel via {link} by {user}";
|
||||
"lng_admin_log_participant_approved_by_request" = "{from} joined to the group via public request, approved by {user}";
|
||||
@@ -3168,6 +3204,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_admin_post_messages" = "Post messages";
|
||||
"lng_admin_log_admin_edit_messages" = "Edit messages";
|
||||
"lng_admin_log_admin_delete_messages" = "Delete messages";
|
||||
"lng_admin_log_admin_remain_anonymous" = "Remain anonymous";
|
||||
"lng_admin_log_admin_ban_users" = "Ban users";
|
||||
"lng_admin_log_admin_invite_users" = "Add members";
|
||||
"lng_admin_log_admin_invite_link" = "Invite users via link";
|
||||
@@ -3540,11 +3577,83 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_filters_menu_add" = "Add to folder";
|
||||
"lng_filters_toast_add" = "{chat} added to {folder} folder";
|
||||
"lng_filters_toast_remove" = "{chat} removed from {folder} folder";
|
||||
"lng_filters_shareable_status" = "shareable folder";
|
||||
|
||||
"lng_filters_delete_sure" = "Are you sure you want to delete this folder? This will also deactivate all the invite links used to share this folder.";
|
||||
"lng_filters_link" = "Share Folder";
|
||||
"lng_filters_link_has" = "Invite links";
|
||||
"lng_filters_link_badge" = "New";
|
||||
"lng_filters_link_create" = "Create an Invite Link";
|
||||
"lng_filters_link_cant" = "No way to share folders with chat types or excluded chats.";
|
||||
"lng_filters_link_about" = "Share access to some of this folder's groups and channels with others.";
|
||||
"lng_filters_link_about_many" = "Create more links to set up different access levels for different people.";
|
||||
"lng_filters_link_title" = "Share Folder";
|
||||
"lng_filters_link_share_about" = "Anyone with this link can add the {folder} folder and the chats selected below.";
|
||||
"lng_filters_link_subtitle" = "Invite link";
|
||||
"lng_filters_link_chats_none" = "No chats selected";
|
||||
"lng_filters_link_chats#one" = "{count} chat selected";
|
||||
"lng_filters_link_chats#other" = "{count} chats selected";
|
||||
"lng_filters_link_bot_status" = "you can't share chats with bots";
|
||||
"lng_filters_link_bot_error" = "Chats with bots can't be shared.";
|
||||
"lng_filters_link_private_status" = "you can't share private chats";
|
||||
"lng_filters_link_private_error" = "Private chats can't be shared.";
|
||||
"lng_filters_link_noadmin_status" = "you can't invite others here";
|
||||
"lng_filters_link_noadmin_group_error" = "You don't have the admin rights to share invite links to this private group.";
|
||||
"lng_filters_link_noadmin_channel_error" = "You don't have the admin rights to share invite links to this private channel.";
|
||||
"lng_filters_link_already_group" = "you are already a member";
|
||||
"lng_filters_link_already_channel" = "you are already subscribed";
|
||||
"lng_filters_link_inaccessible" = "chat is inaccessible";
|
||||
"lng_filters_link_chats_about" = "Select groups and channels that you want everyone who adds the folder via invite link to join.";
|
||||
"lng_filters_link_no_about" = "There are no chats in this folder that you can share with others.";
|
||||
"lng_filters_link_chats_no" = "These chats cannot be shared";
|
||||
"lng_filters_link_chats_no_about" = "You can only share groups and channels in which you are allowed to create invite links.";
|
||||
"lng_filters_link_name_it" = "Name Link";
|
||||
"lng_filters_link_delete_sure" = "Are you sure you want to delete this link?";
|
||||
"lng_filters_link_qr_about" = "Everyone on Telegram can scan this code to add this folder and join the chats included in this invite link.";
|
||||
"lng_filters_link_group_admin_error" = "One of the groups in this folder can’t be added because one of its admins has too many groups and channels.";
|
||||
"lng_filters_by_link_title" = "Add Folder";
|
||||
"lng_filters_by_link_sure" = "Do you want to add a new chat folder {folder} and join its groups and channels?";
|
||||
"lng_filters_by_link_join#one" = "{count} chat to join";
|
||||
"lng_filters_by_link_join#other" = "{count} chats to join";
|
||||
"lng_filters_by_link_add_button" = "Add {folder}";
|
||||
"lng_filters_by_link_add_no" = "Do not add this folder";
|
||||
"lng_filters_by_link_more" = "Add Chats to Folder";
|
||||
"lng_filters_by_link_more_sure" = "Do you want to join chats and add them to the folder {folder}?";
|
||||
"lng_filters_by_link_about" = "You can deselect the chats you don't want to join.";
|
||||
"lng_filters_by_link_and_join_button#one" = "Join Chat";
|
||||
"lng_filters_by_link_and_join_button#other" = "Join Chats";
|
||||
"lng_filters_by_link_join_no" = "Do not join any chats";
|
||||
"lng_filters_by_link_already" = "Folder already added";
|
||||
"lng_filters_by_link_already_about" = "You have already added the folder {folder} and all its chats.";
|
||||
"lng_filters_by_link_in#one" = "{count} chat in this folder";
|
||||
"lng_filters_by_link_in#other" = "{count} chats in this folder";
|
||||
"lng_filters_by_link_remove" = "Remove Folder";
|
||||
"lng_filters_by_link_remove_sure" = "Do you also want to quit the chats included in the folder {folder}?";
|
||||
"lng_filters_by_link_quit#one" = "{count} chat to quit";
|
||||
"lng_filters_by_link_quit#other" = "{count} chats to quit";
|
||||
"lng_filters_by_link_select" = "Select All";
|
||||
"lng_filters_by_link_deselect" = "Deselect All";
|
||||
"lng_filters_by_link_about_quit" = "You can deselect the chats you don't want to quit.";
|
||||
"lng_filters_by_link_remove_button" = "Remove Folder and Keep Chats";
|
||||
"lng_filters_by_link_and_quit_button#one" = "Remove Folder and Chat";
|
||||
"lng_filters_by_link_and_quit_button#other" = "Remove Folder and Chats";
|
||||
"lng_filters_added_title" = "Folder {folder} Added";
|
||||
"lng_filters_added_also#one" = "You also joined {count} chat.";
|
||||
"lng_filters_added_also#other" = "You also joined {count} chats.";
|
||||
"lng_filters_updated_title" = "Folder {folder} Updated";
|
||||
"lng_filters_updated_also#one" = "You have joined {count} new chat.";
|
||||
"lng_filters_updated_also#other" = "You have joined {count} new chats.";
|
||||
"lng_filters_bar_you_can#one" = "You can join {count} new chat";
|
||||
"lng_filters_bar_you_can#other" = "You can join {count} new chats";
|
||||
"lng_filters_bar_view#one" = "Click here to view it";
|
||||
"lng_filters_bar_view#other" = "Click here to view them";
|
||||
|
||||
"lng_chat_theme_change" = "Change colors";
|
||||
"lng_chat_theme_wallpaper" = "Set Wallpaper";
|
||||
"lng_chat_theme_none" = "No\nTheme";
|
||||
"lng_chat_theme_apply" = "Apply Theme";
|
||||
"lng_chat_theme_title" = "Select theme";
|
||||
"lng_chat_theme_change_wallpaper" = "Change Wallpaper";
|
||||
"lng_chat_theme_title" = "Choose theme";
|
||||
"lng_chat_theme_cant_voice" = "Sorry, you can't change the chat theme while you're having an unsent voice message.";
|
||||
|
||||
"lng_photo_editor_menu_delete" = "Delete";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<RCC>
|
||||
<qresource prefix="/animations">
|
||||
<file alias="change_number.tgs">../../animations/change_number.tgs</file>
|
||||
<file alias="blocked_peers_empty.tgs">../../animations/blocked_peers_empty.tgs</file>
|
||||
<file alias="filters.tgs">../../animations/filters.tgs</file>
|
||||
<file alias="cloud_filters.tgs">../../animations/cloud_filters.tgs</file>
|
||||
<file alias="local_passcode_enter.tgs">../../animations/local_passcode_enter.tgs</file>
|
||||
<file alias="cloud_password/intro.tgs">../../animations/cloud_password/intro.tgs</file>
|
||||
<file alias="cloud_password/password_input.tgs">../../animations/cloud_password/password_input.tgs</file>
|
||||
|
||||
@@ -110,7 +110,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
|
||||
storage.fileWebp#1081464c = storage.FileType;
|
||||
|
||||
userEmpty#d3bc4b7a id:long = User;
|
||||
user#8f97c628 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector<Username> = User;
|
||||
user#8f97c628 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# bot_can_edit:flags2.1?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector<Username> = User;
|
||||
|
||||
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
|
||||
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true personal:flags.2?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
|
||||
@@ -191,11 +191,13 @@ messageActionSetChatTheme#aa786345 emoticon:string = MessageAction;
|
||||
messageActionChatJoinedByRequest#ebbca3cb = MessageAction;
|
||||
messageActionWebViewDataSentMe#47dd8079 text:string data:string = MessageAction;
|
||||
messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction;
|
||||
messageActionGiftPremium#aba0f5c6 currency:string amount:long months:int = MessageAction;
|
||||
messageActionGiftPremium#c83d6aec flags:# currency:string amount:long months:int crypto_currency:flags.0?string crypto_amount:flags.0?long = MessageAction;
|
||||
messageActionTopicCreate#d999256 flags:# title:string icon_color:int icon_emoji_id:flags.0?long = MessageAction;
|
||||
messageActionTopicEdit#c0944820 flags:# title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool hidden:flags.3?Bool = MessageAction;
|
||||
messageActionSuggestProfilePhoto#57de635e photo:Photo = MessageAction;
|
||||
messageActionRequestedPeer#fe77345d button_id:int peer:Peer = MessageAction;
|
||||
messageActionSetChatWallPaper#bc44a927 wallpaper:WallPaper = MessageAction;
|
||||
messageActionSetSameChatWallPaper#c0787d6d wallpaper:WallPaper = MessageAction;
|
||||
|
||||
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
|
||||
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
||||
@@ -247,7 +249,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
|
||||
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
|
||||
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
|
||||
|
||||
userFull#f8d32aed flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> = UserFull;
|
||||
userFull#93eadb53 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper = UserFull;
|
||||
|
||||
contact#145ade0b user_id:long mutual:Bool = Contact;
|
||||
|
||||
@@ -385,7 +387,7 @@ updateGroupCallParticipants#f2ebdb4e call:InputGroupCall participants:Vector<Gro
|
||||
updateGroupCall#14b24500 chat_id:long call:GroupCall = Update;
|
||||
updatePeerHistoryTTL#bb9bb9a5 flags:# peer:Peer ttl_period:flags.0?int = Update;
|
||||
updateChatParticipant#d087663a flags:# chat_id:long date:int actor_id:long user_id:long prev_participant:flags.0?ChatParticipant new_participant:flags.1?ChatParticipant invite:flags.2?ExportedChatInvite qts:int = Update;
|
||||
updateChannelParticipant#985d3abb flags:# channel_id:long date:int actor_id:long user_id:long prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant invite:flags.2?ExportedChatInvite qts:int = Update;
|
||||
updateChannelParticipant#985d3abb flags:# via_chatlist:flags.3?true channel_id:long date:int actor_id:long user_id:long prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant invite:flags.2?ExportedChatInvite qts:int = Update;
|
||||
updateBotStopped#c4870a49 user_id:long date:int stopped:Bool qts:int = Update;
|
||||
updateGroupCallConnection#b783982 flags:# presentation:flags.0?true params:DataJSON = Update;
|
||||
updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector<BotCommand> = Update;
|
||||
@@ -615,7 +617,7 @@ keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton;
|
||||
keyboardButtonCallback#35bbdb6b flags:# requires_password:flags.0?true text:string data:bytes = KeyboardButton;
|
||||
keyboardButtonRequestPhone#b16a6c29 text:string = KeyboardButton;
|
||||
keyboardButtonRequestGeoLocation#fc796b3f text:string = KeyboardButton;
|
||||
keyboardButtonSwitchInline#568a748 flags:# same_peer:flags.0?true text:string query:string = KeyboardButton;
|
||||
keyboardButtonSwitchInline#93b9fbb5 flags:# same_peer:flags.0?true text:string query:string peer_types:flags.1?Vector<InlineQueryPeerType> = KeyboardButton;
|
||||
keyboardButtonGame#50f41ccf text:string = KeyboardButton;
|
||||
keyboardButtonBuy#afd93fbb text:string = KeyboardButton;
|
||||
keyboardButtonUrlAuth#10b78d29 flags:# text:string fwd_text:flags.0?string url:string button_id:int = KeyboardButton;
|
||||
@@ -737,7 +739,7 @@ auth.sentCodeTypeSms#c000bba2 length:int = auth.SentCodeType;
|
||||
auth.sentCodeTypeCall#5353e5a7 length:int = auth.SentCodeType;
|
||||
auth.sentCodeTypeFlashCall#ab03c6d9 pattern:string = auth.SentCodeType;
|
||||
auth.sentCodeTypeMissedCall#82006484 prefix:string length:int = auth.SentCodeType;
|
||||
auth.sentCodeTypeEmailCode#5a159841 flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true email_pattern:string length:int next_phone_login_date:flags.2?int = auth.SentCodeType;
|
||||
auth.sentCodeTypeEmailCode#f450f59b flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true email_pattern:string length:int reset_available_period:flags.3?int reset_pending_date:flags.4?int = auth.SentCodeType;
|
||||
auth.sentCodeTypeSetUpEmailRequired#a5491dea flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true = auth.SentCodeType;
|
||||
auth.sentCodeTypeFragmentSms#d9565c39 url:string length:int = auth.SentCodeType;
|
||||
auth.sentCodeTypeFirebaseSms#e57b1432 flags:# nonce:flags.0?bytes receipt:flags.1?string push_timeout:flags.1?int length:int = auth.SentCodeType;
|
||||
@@ -959,7 +961,7 @@ channelAdminLogEventActionDiscardGroupCall#db9f9140 call:InputGroupCall = Channe
|
||||
channelAdminLogEventActionParticipantMute#f92424d2 participant:GroupCallParticipant = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionParticipantUnmute#e64429c0 participant:GroupCallParticipant = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionToggleGroupCallSetting#56d6a247 join_muted:Bool = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionParticipantJoinByInvite#5cdada77 invite:ExportedChatInvite = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionParticipantJoinByInvite#fe9fc158 flags:# via_chatlist:flags.0?true invite:ExportedChatInvite = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionExportedInviteDelete#5a50fca4 invite:ExportedChatInvite = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionExportedInviteRevoke#410a134e invite:ExportedChatInvite = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionExportedInviteEdit#e90ebb59 prev_invite:ExportedChatInvite new_invite:ExportedChatInvite = ChannelAdminLogEventAction;
|
||||
@@ -1227,6 +1229,7 @@ payments.bankCardData#3e24e573 title:string open_urls:Vector<BankCardOpenUrl> =
|
||||
|
||||
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<InputPeer> include_peers:Vector<InputPeer> exclude_peers:Vector<InputPeer> = DialogFilter;
|
||||
dialogFilterDefault#363293ae = DialogFilter;
|
||||
dialogFilterChatlist#d64a04a8 flags:# has_my_invites:flags.26?true id:int title:string emoticon:flags.25?string pinned_peers:Vector<InputPeer> include_peers:Vector<InputPeer> = DialogFilter;
|
||||
|
||||
dialogFilterSuggested#77744d4a filter:DialogFilter description:string = DialogFilterSuggested;
|
||||
|
||||
@@ -1298,6 +1301,7 @@ inlineQueryPeerTypePM#833c0fac = InlineQueryPeerType;
|
||||
inlineQueryPeerTypeChat#d766c50a = InlineQueryPeerType;
|
||||
inlineQueryPeerTypeMegagroup#5ec4be43 = InlineQueryPeerType;
|
||||
inlineQueryPeerTypeBroadcast#6334ee9a = InlineQueryPeerType;
|
||||
inlineQueryPeerTypeBotPM#e3b2d0c = InlineQueryPeerType;
|
||||
|
||||
messages.historyImport#1662af0b id:long = messages.HistoryImport;
|
||||
|
||||
@@ -1305,7 +1309,7 @@ messages.historyImportParsed#5e0fb7b9 flags:# pm:flags.0?true group:flags.1?true
|
||||
|
||||
messages.affectedFoundMessages#ef8d3e6c pts:int pts_count:int offset:int messages:Vector<int> = messages.AffectedFoundMessages;
|
||||
|
||||
chatInviteImporter#8c5adfd9 flags:# requested:flags.0?true user_id:long date:int about:flags.2?string approved_by:flags.1?long = ChatInviteImporter;
|
||||
chatInviteImporter#8c5adfd9 flags:# requested:flags.0?true via_chatlist:flags.3?true user_id:long date:int about:flags.2?string approved_by:flags.1?long = ChatInviteImporter;
|
||||
|
||||
messages.exportedChatInvites#bdc62dcc count:int invites:Vector<ExportedChatInvite> users:Vector<User> = messages.ExportedChatInvites;
|
||||
|
||||
@@ -1524,6 +1528,21 @@ inlineBotWebView#b57295d5 text:string url:string = InlineBotWebView;
|
||||
|
||||
readParticipantDate#4a4ff172 user_id:long date:int = ReadParticipantDate;
|
||||
|
||||
inputChatlistDialogFilter#f3e0da33 filter_id:int = InputChatlist;
|
||||
|
||||
exportedChatlistInvite#c5181ac flags:# title:string url:string peers:Vector<Peer> = ExportedChatlistInvite;
|
||||
|
||||
chatlists.exportedChatlistInvite#10e6e3a6 filter:DialogFilter invite:ExportedChatlistInvite = chatlists.ExportedChatlistInvite;
|
||||
|
||||
chatlists.exportedInvites#10ab6dc7 invites:Vector<ExportedChatlistInvite> chats:Vector<Chat> users:Vector<User> = chatlists.ExportedInvites;
|
||||
|
||||
chatlists.chatlistInviteAlready#fa87f659 filter_id:int missing_peers:Vector<Peer> already_peers:Vector<Peer> chats:Vector<Chat> users:Vector<User> = chatlists.ChatlistInvite;
|
||||
chatlists.chatlistInvite#1dcd839d flags:# title:string emoticon:flags.0?string peers:Vector<Peer> chats:Vector<Chat> users:Vector<User> = chatlists.ChatlistInvite;
|
||||
|
||||
chatlists.chatlistUpdates#93bd878d missing_peers:Vector<Peer> chats:Vector<Chat> users:Vector<User> = chatlists.ChatlistUpdates;
|
||||
|
||||
bots.botInfo#e8a775b0 name:string about:string description:string = bots.BotInfo;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
@@ -1555,6 +1574,7 @@ auth.acceptLoginToken#e894ad4d token:bytes = Authorization;
|
||||
auth.checkRecoveryPassword#d36bf79 code:string = Bool;
|
||||
auth.importWebTokenAuthorization#2db873a9 api_id:int api_hash:string web_auth_token:string = auth.Authorization;
|
||||
auth.requestFirebaseSms#89464b50 flags:# phone_number:string phone_code_hash:string safety_net_token:flags.0?string ios_push_secret:flags.1?string = Bool;
|
||||
auth.resetLoginEmail#7e960193 phone_number:string phone_code_hash:string = auth.SentCode;
|
||||
|
||||
account.registerDevice#ec86017a flags:# no_muted:flags.0?true token_type:int token:string app_sandbox:Bool secret:bytes other_uids:Vector<long> = Bool;
|
||||
account.unregisterDevice#6a0d3206 token_type:int token:string other_uids:Vector<long> = Bool;
|
||||
@@ -1605,7 +1625,7 @@ account.getContactSignUpNotification#9f07c728 = Bool;
|
||||
account.setContactSignUpNotification#cff43f61 silent:Bool = Bool;
|
||||
account.getNotifyExceptions#53577479 flags:# compare_sound:flags.1?true peer:flags.0?InputNotifyPeer = Updates;
|
||||
account.getWallPaper#fc8ddbea wallpaper:InputWallPaper = WallPaper;
|
||||
account.uploadWallPaper#dd853661 file:InputFile mime_type:string settings:WallPaperSettings = WallPaper;
|
||||
account.uploadWallPaper#e39a8f03 flags:# for_chat:flags.0?true file:InputFile mime_type:string settings:WallPaperSettings = WallPaper;
|
||||
account.saveWallPaper#6c5a5b37 wallpaper:InputWallPaper unsave:Bool settings:WallPaperSettings = Bool;
|
||||
account.installWallPaper#feed5769 wallpaper:InputWallPaper settings:WallPaperSettings = Bool;
|
||||
account.resetWallPapers#bb3b9804 = Bool;
|
||||
@@ -1859,13 +1879,14 @@ messages.searchCustomEmoji#2c11c0d7 emoticon:string hash:long = EmojiList;
|
||||
messages.togglePeerTranslations#e47cb579 flags:# disabled:flags.0?true peer:InputPeer = Bool;
|
||||
messages.getBotApp#34fdc5c3 app:InputBotApp hash:long = messages.BotApp;
|
||||
messages.requestAppWebView#8c5a3b3c flags:# write_allowed:flags.0?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = AppWebViewResult;
|
||||
messages.setChatWallPaper#8ffacae1 flags:# peer:InputPeer wallpaper:flags.0?InputWallPaper settings:flags.2?WallPaperSettings id:flags.1?int = Updates;
|
||||
|
||||
updates.getState#edd4882a = updates.State;
|
||||
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
||||
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
||||
|
||||
photos.updateProfilePhoto#1c3d5956 flags:# fallback:flags.0?true id:InputPhoto = photos.Photo;
|
||||
photos.uploadProfilePhoto#93c9a51 flags:# fallback:flags.3?true file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.4?VideoSize = photos.Photo;
|
||||
photos.updateProfilePhoto#9e82039 flags:# fallback:flags.0?true bot:flags.1?InputUser id:InputPhoto = photos.Photo;
|
||||
photos.uploadProfilePhoto#388a3b5 flags:# fallback:flags.3?true bot:flags.5?InputUser file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.4?VideoSize = photos.Photo;
|
||||
photos.deletePhotos#87cf7f2f id:Vector<InputPhoto> = Vector<long>;
|
||||
photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
|
||||
photos.uploadContactProfilePhoto#e14c4a71 flags:# suggest:flags.3?true save:flags.4?true user_id:InputUser file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.5?VideoSize = photos.Photo;
|
||||
@@ -1968,8 +1989,10 @@ bots.setBotMenuButton#4504d54f user_id:InputUser button:BotMenuButton = Bool;
|
||||
bots.getBotMenuButton#9c60eb28 user_id:InputUser = BotMenuButton;
|
||||
bots.setBotBroadcastDefaultAdminRights#788464e1 admin_rights:ChatAdminRights = Bool;
|
||||
bots.setBotGroupDefaultAdminRights#925ec9ea admin_rights:ChatAdminRights = Bool;
|
||||
bots.setBotInfo#a365df7a flags:# lang_code:string about:flags.0?string description:flags.1?string = Bool;
|
||||
bots.getBotInfo#75ec12e6 lang_code:string = Vector<string>;
|
||||
bots.setBotInfo#10cf3123 flags:# bot:flags.2?InputUser lang_code:string name:flags.3?string about:flags.0?string description:flags.1?string = Bool;
|
||||
bots.getBotInfo#dcd914fd flags:# bot:flags.0?InputUser lang_code:string = bots.BotInfo;
|
||||
bots.reorderUsernames#9709b1c2 bot:InputUser order:Vector<string> = Bool;
|
||||
bots.toggleUsername#53ca973 bot:InputUser username:string active:Bool = Bool;
|
||||
|
||||
payments.getPaymentForm#37148dbb flags:# invoice:InputInvoice theme_params:flags.0?DataJSON = payments.PaymentForm;
|
||||
payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt;
|
||||
@@ -2033,7 +2056,6 @@ langpack.getLanguages#42c6978f lang_pack:string = Vector<LangPackLanguage>;
|
||||
langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLanguage;
|
||||
|
||||
folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
|
||||
folders.deleteFolder#1c295881 folder_id:int = Updates;
|
||||
|
||||
stats.getBroadcastStats#ab42441a flags:# dark:flags.0?true channel:InputChannel = stats.BroadcastStats;
|
||||
stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
|
||||
@@ -2041,4 +2063,16 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel
|
||||
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
|
||||
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
|
||||
|
||||
// LAYER 155
|
||||
chatlists.exportChatlistInvite#8472478e chatlist:InputChatlist title:string peers:Vector<InputPeer> = chatlists.ExportedChatlistInvite;
|
||||
chatlists.deleteExportedInvite#719c5c5e chatlist:InputChatlist slug:string = Bool;
|
||||
chatlists.editExportedInvite#653db63d flags:# chatlist:InputChatlist slug:string title:flags.1?string peers:flags.2?Vector<InputPeer> = ExportedChatlistInvite;
|
||||
chatlists.getExportedInvites#ce03da83 chatlist:InputChatlist = chatlists.ExportedInvites;
|
||||
chatlists.checkChatlistInvite#41c10fff slug:string = chatlists.ChatlistInvite;
|
||||
chatlists.joinChatlistInvite#a6b1e39a slug:string peers:Vector<InputPeer> = Updates;
|
||||
chatlists.getChatlistUpdates#89419521 chatlist:InputChatlist = chatlists.ChatlistUpdates;
|
||||
chatlists.joinChatlistUpdates#e089f8f5 chatlist:InputChatlist peers:Vector<InputPeer> = Updates;
|
||||
chatlists.hideChatlistUpdates#66e486fb chatlist:InputChatlist = Bool;
|
||||
chatlists.getLeaveChatlistSuggestions#fdbcd714 chatlist:InputChatlist = Vector<Peer>;
|
||||
chatlists.leaveChatlist#74fae13a chatlist:InputChatlist peers:Vector<InputPeer> = Updates;
|
||||
|
||||
// LAYER 158
|
||||
|
||||
@@ -111,6 +111,7 @@ tlsBlockDomain = TlsBlock;
|
||||
tlsBlockGrease seed:int = TlsBlock;
|
||||
tlsBlockPublicKey = TlsBlock;
|
||||
tlsBlockScope entries:Vector<TlsBlock> = TlsBlock;
|
||||
tlsBlockPermutation entries:Vector<Vector<TlsBlock>> = TlsBlock;
|
||||
|
||||
---functions---
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="4.7.0.0" />
|
||||
Version="4.8.3.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,7,0,0
|
||||
PRODUCTVERSION 4,7,0,0
|
||||
FILEVERSION 4,8,3,0
|
||||
PRODUCTVERSION 4,8,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "4.7.0.0"
|
||||
VALUE "FileVersion", "4.8.3.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "4.7.0.0"
|
||||
VALUE "ProductVersion", "4.8.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,7,0,0
|
||||
PRODUCTVERSION 4,7,0,0
|
||||
FILEVERSION 4,8,3,0
|
||||
PRODUCTVERSION 4,8,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "4.7.0.0"
|
||||
VALUE "FileVersion", "4.8.3.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "4.7.0.0"
|
||||
VALUE "ProductVersion", "4.8.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -41,7 +41,7 @@ void AttachedStickers::request(
|
||||
return;
|
||||
} else if (result.v.size() > 1) {
|
||||
strongController->show(
|
||||
Box<StickersBox>(strongController, result.v));
|
||||
Box<StickersBox>(strongController->uiShow(), result.v));
|
||||
return;
|
||||
}
|
||||
// Single attached sticker pack.
|
||||
@@ -54,16 +54,14 @@ void AttachedStickers::request(
|
||||
.id = data->vid().v,
|
||||
.accessHash = data->vaccess_hash().v }
|
||||
: StickerSetIdentifier{ .shortName = qs(data->vshort_name()) };
|
||||
strongController->show(
|
||||
Box<StickerSetBox>(
|
||||
strongController,
|
||||
setId,
|
||||
(data->is_emojis()
|
||||
? Data::StickersType::Emoji
|
||||
: data->is_masks()
|
||||
? Data::StickersType::Masks
|
||||
: Data::StickersType::Stickers)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
strongController->show(Box<StickerSetBox>(
|
||||
strongController->uiShow(),
|
||||
setId,
|
||||
(data->is_emojis()
|
||||
? Data::StickersType::Emoji
|
||||
: data->is_masks()
|
||||
? Data::StickersType::Masks
|
||||
: Data::StickersType::Stickers)));
|
||||
}).fail([=] {
|
||||
_requestId = 0;
|
||||
if (const auto strongController = weak.get()) {
|
||||
|
||||
@@ -82,7 +82,7 @@ void SendBotCallbackData(
|
||||
flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_password;
|
||||
}
|
||||
const auto weak = base::make_weak(controller);
|
||||
const auto show = std::make_shared<Window::Show>(controller);
|
||||
const auto show = controller->uiShow();
|
||||
button->requestId = api->request(MTPmessages_GetBotCallbackAnswer(
|
||||
MTP_flags(flags),
|
||||
history->peer->input,
|
||||
@@ -119,7 +119,7 @@ void SendBotCallbackData(
|
||||
if (withPassword) {
|
||||
show->hideLayer();
|
||||
}
|
||||
Ui::Toast::Show(show->toastParent(), message);
|
||||
show->showToast(message);
|
||||
}
|
||||
} else if (!link.isEmpty()) {
|
||||
if (!isGame) {
|
||||
@@ -210,7 +210,7 @@ void SendBotCallbackDataWithPassword(
|
||||
}
|
||||
api->cloudPassword().reload();
|
||||
const auto weak = base::make_weak(controller);
|
||||
const auto show = std::make_shared<Window::Show>(controller);
|
||||
const auto show = controller->uiShow();
|
||||
SendBotCallbackData(controller, item, row, column, {}, {}, [=](
|
||||
const QString &error) {
|
||||
auto box = PrePasswordErrorBox(
|
||||
@@ -279,11 +279,11 @@ void SendBotCallbackDataWithPassword(
|
||||
|
||||
bool SwitchInlineBotButtonReceived(
|
||||
not_null<Window::SessionController*> controller,
|
||||
const QString &query,
|
||||
const QByteArray &queryWithPeerTypes,
|
||||
UserData *samePeerBot,
|
||||
MsgId samePeerReplyTo) {
|
||||
return controller->content()->notify_switchInlineBotButtonReceived(
|
||||
query,
|
||||
QString::fromUtf8(queryWithPeerTypes),
|
||||
samePeerBot,
|
||||
samePeerReplyTo);
|
||||
}
|
||||
@@ -441,14 +441,14 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
||||
if (samePeer) {
|
||||
SwitchInlineBotButtonReceived(
|
||||
controller,
|
||||
QString::fromUtf8(button->data),
|
||||
button->data,
|
||||
bot,
|
||||
item->id);
|
||||
return true;
|
||||
} else if (bot->isBot() && bot->botInfo->inlineReturnTo.key) {
|
||||
const auto switched = SwitchInlineBotButtonReceived(
|
||||
controller,
|
||||
QString::fromUtf8(button->data));
|
||||
button->data);
|
||||
if (switched) {
|
||||
return true;
|
||||
}
|
||||
@@ -466,7 +466,9 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
||||
Window::ShowChooseRecipientBox(
|
||||
controller,
|
||||
chosen,
|
||||
tr::lng_inline_switch_choose());
|
||||
tr::lng_inline_switch_choose(),
|
||||
nullptr,
|
||||
button->peerTypes);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -30,7 +30,7 @@ void SendBotCallbackDataWithPassword(
|
||||
|
||||
bool SwitchInlineBotButtonReceived(
|
||||
not_null<Window::SessionController*> controller,
|
||||
const QString &query,
|
||||
const QByteArray &queryWithPeerTypes,
|
||||
UserData *samePeerBot = nullptr,
|
||||
MsgId samePeerReplyTo = 0);
|
||||
|
||||
|
||||
@@ -7,12 +7,663 @@ 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"
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "boxes/premium_limits_box.h"
|
||||
#include "boxes/filters/edit_filter_links.h" // FilterChatStatusText
|
||||
#include "core/application.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/controls/filter_link_header.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/filter_icons.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_filter_icons.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
enum class ToggleAction {
|
||||
Adding,
|
||||
Removing,
|
||||
};
|
||||
|
||||
class ToggleChatsController final
|
||||
: public PeerListController
|
||||
, public base::has_weak_ptr {
|
||||
public:
|
||||
ToggleChatsController(
|
||||
not_null<Window::SessionController*> window,
|
||||
ToggleAction action,
|
||||
const QString &title,
|
||||
std::vector<not_null<PeerData*>> chats,
|
||||
std::vector<not_null<PeerData*>> additional);
|
||||
|
||||
void prepare() override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
Main::Session &session() const override;
|
||||
|
||||
[[nodiscard]] auto selectedValue() const
|
||||
-> rpl::producer<base::flat_set<not_null<PeerData*>>>;
|
||||
|
||||
void adjust(int minHeight, int maxHeight, int addedTopHeight);
|
||||
void setRealContentHeight(rpl::producer<int> value);
|
||||
rpl::producer<int> boxHeightValue() const override;
|
||||
|
||||
private:
|
||||
void setupAboveWidget();
|
||||
void setupBelowWidget();
|
||||
void initDesiredHeightValue();
|
||||
void toggleAllSelected(bool select);
|
||||
|
||||
const not_null<Window::SessionController*> _window;
|
||||
Ui::RpWidget *_addedTopWidget = nullptr;
|
||||
Ui::RpWidget *_addedBottomWidget = nullptr;
|
||||
|
||||
ToggleAction _action = ToggleAction::Adding;
|
||||
QString _filterTitle;
|
||||
base::flat_set<not_null<PeerData*>> _checkable;
|
||||
std::vector<not_null<PeerData*>> _chats;
|
||||
std::vector<not_null<PeerData*>> _additional;
|
||||
rpl::variable<base::flat_set<not_null<PeerData*>>> _selected;
|
||||
|
||||
int _minTopHeight = 0;
|
||||
rpl::variable<int> _maxTopHeight;
|
||||
rpl::variable<int> _aboveHeight;
|
||||
rpl::variable<int> _belowHeight;
|
||||
rpl::variable<int> _desiredHeight;
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] tr::phrase<> TitleText(Ui::FilterLinkHeaderType type) {
|
||||
using Type = Ui::FilterLinkHeaderType;
|
||||
switch (type) {
|
||||
case Type::AddingFilter: return tr::lng_filters_by_link_title;
|
||||
case Type::AddingChats: return tr::lng_filters_by_link_more;
|
||||
case Type::AllAdded: return tr::lng_filters_by_link_already;
|
||||
case Type::Removing: return tr::lng_filters_by_link_remove;
|
||||
}
|
||||
Unexpected("Ui::FilterLinkHeaderType in TitleText.");
|
||||
}
|
||||
|
||||
[[nodiscard]] TextWithEntities AboutText(
|
||||
Ui::FilterLinkHeaderType type,
|
||||
const QString &title) {
|
||||
using Type = Ui::FilterLinkHeaderType;
|
||||
auto boldTitle = Ui::Text::Bold(title);
|
||||
return (type == Type::AddingFilter)
|
||||
? tr::lng_filters_by_link_sure(
|
||||
tr::now,
|
||||
lt_folder,
|
||||
std::move(boldTitle),
|
||||
Ui::Text::WithEntities)
|
||||
: (type == Type::AddingChats)
|
||||
? tr::lng_filters_by_link_more_sure(
|
||||
tr::now,
|
||||
lt_folder,
|
||||
std::move(boldTitle),
|
||||
Ui::Text::WithEntities)
|
||||
: (type == Type::AllAdded)
|
||||
? tr::lng_filters_by_link_already_about(
|
||||
tr::now,
|
||||
lt_folder,
|
||||
std::move(boldTitle),
|
||||
Ui::Text::WithEntities)
|
||||
: tr::lng_filters_by_link_remove_sure(
|
||||
tr::now,
|
||||
lt_folder,
|
||||
std::move(boldTitle),
|
||||
Ui::Text::WithEntities);
|
||||
}
|
||||
|
||||
void InitFilterLinkHeader(
|
||||
not_null<PeerListBox*> box,
|
||||
Fn<void(int minHeight, int maxHeight, int addedTopHeight)> adjust,
|
||||
Ui::FilterLinkHeaderType type,
|
||||
const QString &title,
|
||||
const QString &iconEmoji,
|
||||
rpl::producer<int> count) {
|
||||
const auto icon = Ui::LookupFilterIcon(
|
||||
Ui::LookupFilterIconByEmoji(
|
||||
iconEmoji
|
||||
).value_or(Ui::FilterIcon::Custom)).active;
|
||||
auto header = Ui::MakeFilterLinkHeader(box, {
|
||||
.type = type,
|
||||
.title = TitleText(type)(tr::now),
|
||||
.about = AboutText(type, title),
|
||||
.folderTitle = title,
|
||||
.folderIcon = icon,
|
||||
.badge = (type == Ui::FilterLinkHeaderType::AddingChats
|
||||
? std::move(count)
|
||||
: rpl::single(0)),
|
||||
});
|
||||
const auto widget = header.widget;
|
||||
widget->resizeToWidth(st::boxWideWidth);
|
||||
Ui::SendPendingMoveResizeEvents(widget);
|
||||
|
||||
const auto min = widget->minimumHeight(), max = widget->maximumHeight();
|
||||
widget->resize(st::boxWideWidth, max);
|
||||
|
||||
box->setAddedTopScrollSkip(max);
|
||||
std::move(
|
||||
header.wheelEvents
|
||||
) | rpl::start_with_next([=](not_null<QWheelEvent*> e) {
|
||||
box->sendScrollViewportEvent(e);
|
||||
}, widget->lifetime());
|
||||
|
||||
std::move(
|
||||
header.closeRequests
|
||||
) | rpl::start_with_next([=] {
|
||||
box->closeBox();
|
||||
}, widget->lifetime());
|
||||
|
||||
struct State {
|
||||
bool processing = false;
|
||||
int addedTopHeight = 0;
|
||||
};
|
||||
const auto state = widget->lifetime().make_state<State>();
|
||||
|
||||
box->scrolls(
|
||||
) | rpl::filter([=] {
|
||||
return !state->processing;
|
||||
}) | rpl::start_with_next([=] {
|
||||
state->processing = true;
|
||||
const auto guard = gsl::finally([&] { state->processing = false; });
|
||||
|
||||
const auto top = box->scrollTop();
|
||||
const auto headerHeight = std::max(max - top, min);
|
||||
const auto addedTopHeight = max - headerHeight;
|
||||
widget->resize(widget->width(), headerHeight);
|
||||
if (state->addedTopHeight < addedTopHeight) {
|
||||
adjust(min, max, addedTopHeight);
|
||||
box->setAddedTopScrollSkip(headerHeight);
|
||||
} else {
|
||||
box->setAddedTopScrollSkip(headerHeight);
|
||||
adjust(min, max, addedTopHeight);
|
||||
}
|
||||
state->addedTopHeight = addedTopHeight;
|
||||
box->peerListRefreshRows();
|
||||
}, widget->lifetime());
|
||||
|
||||
box->setNoContentMargin(true);
|
||||
adjust(min, max, 0);
|
||||
}
|
||||
|
||||
void ImportInvite(
|
||||
const QString &slug,
|
||||
FilterId filterId,
|
||||
const base::flat_set<not_null<PeerData*>> &peers,
|
||||
Fn<void()> done,
|
||||
Fn<void(QString)> fail) {
|
||||
Expects(!peers.empty());
|
||||
|
||||
const auto peer = peers.front();
|
||||
const auto api = &peer->session().api();
|
||||
const auto callback = [=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result);
|
||||
if (slug.isEmpty()) {
|
||||
peer->owner().chatsFilters().moreChatsHide(filterId, true);
|
||||
}
|
||||
done();
|
||||
};
|
||||
const auto error = [=](const MTP::Error &error) {
|
||||
fail(error.type());
|
||||
};
|
||||
auto inputs = peers | ranges::views::transform([](auto peer) {
|
||||
return MTPInputPeer(peer->input);
|
||||
}) | ranges::to<QVector<MTPInputPeer>>();
|
||||
if (!slug.isEmpty()) {
|
||||
api->request(MTPchatlists_JoinChatlistInvite(
|
||||
MTP_string(slug),
|
||||
MTP_vector<MTPInputPeer>(std::move(inputs))
|
||||
)).done(callback).fail(error).send();
|
||||
} else {
|
||||
api->request(MTPchatlists_JoinChatlistUpdates(
|
||||
MTP_inputChatlistDialogFilter(MTP_int(filterId)),
|
||||
MTP_vector<MTPInputPeer>(std::move(inputs))
|
||||
)).done(callback).fail(error).send();
|
||||
}
|
||||
}
|
||||
|
||||
ToggleChatsController::ToggleChatsController(
|
||||
not_null<Window::SessionController*> window,
|
||||
ToggleAction action,
|
||||
const QString &title,
|
||||
std::vector<not_null<PeerData*>> chats,
|
||||
std::vector<not_null<PeerData*>> additional)
|
||||
: _window(window)
|
||||
, _action(action)
|
||||
, _filterTitle(title)
|
||||
, _chats(std::move(chats))
|
||||
, _additional(std::move(additional)) {
|
||||
setStyleOverrides(&st::filterLinkChatsList);
|
||||
}
|
||||
|
||||
void ToggleChatsController::prepare() {
|
||||
auto selected = base::flat_set<not_null<PeerData*>>();
|
||||
const auto disabled = [](not_null<PeerData*> peer) {
|
||||
return peer->isChat()
|
||||
? peer->asChat()->isForbidden()
|
||||
: peer->isChannel()
|
||||
? peer->asChannel()->isForbidden()
|
||||
: false;
|
||||
};
|
||||
const auto add = [&](not_null<PeerData*> peer, bool additional = false) {
|
||||
const auto disable = disabled(peer);
|
||||
auto row = (additional || !disable)
|
||||
? std::make_unique<PeerListRow>(peer)
|
||||
: MakeFilterChatRow(
|
||||
peer,
|
||||
tr::lng_filters_link_inaccessible(tr::now),
|
||||
true);
|
||||
if (delegate()->peerListFindRow(peer->id.value)) {
|
||||
return;
|
||||
}
|
||||
const auto raw = row.get();
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
if (!disable
|
||||
&& (!additional || _action == ToggleAction::Removing)) {
|
||||
_checkable.emplace(peer);
|
||||
if (const auto status = FilterChatStatusText(peer)
|
||||
; !status.isEmpty()) {
|
||||
raw->setCustomStatus(status);
|
||||
}
|
||||
}
|
||||
if (disable) {
|
||||
} else if (!additional) {
|
||||
delegate()->peerListSetRowChecked(raw, true);
|
||||
raw->finishCheckedAnimation();
|
||||
selected.emplace(peer);
|
||||
} else if (_action == ToggleAction::Adding) {
|
||||
raw->setDisabledState(PeerListRow::State::DisabledChecked);
|
||||
raw->setCustomStatus(peer->isBroadcast()
|
||||
? tr::lng_filters_link_already_channel(tr::now)
|
||||
: tr::lng_filters_link_already_group(tr::now));
|
||||
}
|
||||
};
|
||||
for (const auto &peer : _chats) {
|
||||
if (!disabled(peer)) {
|
||||
add(peer);
|
||||
}
|
||||
}
|
||||
for (const auto &peer : _additional) {
|
||||
add(peer, true);
|
||||
}
|
||||
for (const auto &peer : _chats) {
|
||||
if (disabled(peer)) {
|
||||
add(peer);
|
||||
}
|
||||
}
|
||||
setupAboveWidget();
|
||||
setupBelowWidget();
|
||||
initDesiredHeightValue();
|
||||
delegate()->peerListRefreshRows();
|
||||
_selected = std::move(selected);
|
||||
}
|
||||
|
||||
void ToggleChatsController::rowClicked(not_null<PeerListRow*> row) {
|
||||
const auto peer = row->peer();
|
||||
if (!_checkable.contains(peer)) {
|
||||
return;
|
||||
}
|
||||
const auto checked = row->checked();
|
||||
auto selected = _selected.current();
|
||||
delegate()->peerListSetRowChecked(row, !checked);
|
||||
if (checked) {
|
||||
selected.remove(peer);
|
||||
} else {
|
||||
selected.emplace(peer);
|
||||
}
|
||||
_selected = std::move(selected);
|
||||
}
|
||||
|
||||
void ToggleChatsController::setupAboveWidget() {
|
||||
using namespace Settings;
|
||||
|
||||
auto wrap = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
|
||||
const auto container = wrap.data();
|
||||
|
||||
_addedTopWidget = container->add(object_ptr<Ui::RpWidget>(container));
|
||||
const auto realAbove = container->add(
|
||||
object_ptr<Ui::VerticalLayout>(container));
|
||||
AddDivider(realAbove);
|
||||
const auto totalCount = [&] {
|
||||
if (_chats.empty()) {
|
||||
return _additional.size();
|
||||
} else if (_additional.empty()) {
|
||||
return _chats.size();
|
||||
}
|
||||
auto result = _chats.size();
|
||||
for (const auto &peer : _additional) {
|
||||
if (!ranges::contains(_chats, peer)) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const auto count = (_action == ToggleAction::Removing)
|
||||
? totalCount()
|
||||
: _chats.empty()
|
||||
? _additional.size()
|
||||
: _chats.size();
|
||||
const auto selectableCount = int(_checkable.size());
|
||||
auto selectedCount = _selected.value(
|
||||
) | rpl::map([](const base::flat_set<not_null<PeerData*>> &selected) {
|
||||
return int(selected.size());
|
||||
});
|
||||
AddFilterSubtitleWithToggles(
|
||||
realAbove,
|
||||
(_action == ToggleAction::Removing
|
||||
? tr::lng_filters_by_link_quit
|
||||
: _chats.empty()
|
||||
? tr::lng_filters_by_link_in
|
||||
: tr::lng_filters_by_link_join)(
|
||||
lt_count,
|
||||
rpl::single(float64(count))),
|
||||
selectableCount,
|
||||
std::move(selectedCount),
|
||||
[=](bool select) { toggleAllSelected(select); });
|
||||
|
||||
_aboveHeight = realAbove->heightValue();
|
||||
delegate()->peerListSetAboveWidget(std::move(wrap));
|
||||
}
|
||||
|
||||
void ToggleChatsController::toggleAllSelected(bool select) {
|
||||
auto selected = _selected.current();
|
||||
if (!select) {
|
||||
if (selected.empty()) {
|
||||
return;
|
||||
}
|
||||
for (const auto &peer : selected) {
|
||||
const auto row = delegate()->peerListFindRow(peer->id.value);
|
||||
Assert(row != nullptr);
|
||||
delegate()->peerListSetRowChecked(row, false);
|
||||
}
|
||||
selected = {};
|
||||
} else {
|
||||
const auto count = delegate()->peerListFullRowsCount();
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
const auto row = delegate()->peerListRowAt(i);
|
||||
const auto peer = row->peer();
|
||||
if (_action != ToggleAction::Adding ||
|
||||
!ranges::contains(_additional, peer)) {
|
||||
delegate()->peerListSetRowChecked(row, true);
|
||||
selected.emplace(peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
_selected = std::move(selected);
|
||||
}
|
||||
|
||||
void ToggleChatsController::setupBelowWidget() {
|
||||
if (_chats.empty()) {
|
||||
auto widget = object_ptr<Ui::RpWidget>((QWidget*)nullptr);
|
||||
_addedBottomWidget = widget.data();
|
||||
delegate()->peerListSetBelowWidget(std::move(widget));
|
||||
return;
|
||||
}
|
||||
auto layout = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
|
||||
const auto raw = layout.data();
|
||||
auto widget = object_ptr<Ui::DividerLabel>(
|
||||
(QWidget*)nullptr,
|
||||
std::move(layout),
|
||||
st::settingsDividerLabelPadding);
|
||||
raw->add(object_ptr<Ui::FlatLabel>(
|
||||
raw,
|
||||
(_action == ToggleAction::Removing
|
||||
? tr::lng_filters_by_link_about_quit
|
||||
: tr::lng_filters_by_link_about)(tr::now),
|
||||
st::boxDividerLabel));
|
||||
_addedBottomWidget = raw->add(object_ptr<Ui::RpWidget>(raw));
|
||||
_belowHeight = widget->heightValue() | rpl::map([=](int value) {
|
||||
return value - _addedBottomWidget->height();
|
||||
});
|
||||
delegate()->peerListSetBelowWidget(std::move(widget));
|
||||
}
|
||||
|
||||
Main::Session &ToggleChatsController::session() const {
|
||||
return _window->session();
|
||||
}
|
||||
|
||||
auto ToggleChatsController::selectedValue() const
|
||||
-> rpl::producer<base::flat_set<not_null<PeerData*>>> {
|
||||
return _selected.value();
|
||||
}
|
||||
|
||||
void ToggleChatsController::adjust(
|
||||
int minHeight,
|
||||
int maxHeight,
|
||||
int addedTopHeight) {
|
||||
Expects(addedTopHeight >= 0);
|
||||
|
||||
_addedTopWidget->resize(_addedTopWidget->width(), addedTopHeight);
|
||||
_minTopHeight = minHeight;
|
||||
_maxTopHeight = maxHeight;
|
||||
}
|
||||
|
||||
void ToggleChatsController::setRealContentHeight(rpl::producer<int> value) {
|
||||
std::move(
|
||||
value
|
||||
) | rpl::start_with_next([=](int height) {
|
||||
const auto desired = _desiredHeight.current();
|
||||
if (height <= computeListSt().item.height) {
|
||||
return;
|
||||
} else if (height >= desired) {
|
||||
_addedBottomWidget->resize(_addedBottomWidget->width(), 0);
|
||||
} else {
|
||||
const auto available = desired - height;
|
||||
const auto required = _maxTopHeight.current() - _minTopHeight;
|
||||
const auto added = required - available;
|
||||
_addedBottomWidget->resize(
|
||||
_addedBottomWidget->width(),
|
||||
std::max(added, 0));
|
||||
}
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
void ToggleChatsController::initDesiredHeightValue() {
|
||||
using namespace rpl::mappers;
|
||||
|
||||
const auto &st = computeListSt();
|
||||
const auto count = int(delegate()->peerListFullRowsCount());
|
||||
const auto middle = st.padding.top()
|
||||
+ (count * st.item.height)
|
||||
+ st.padding.bottom();
|
||||
_desiredHeight = rpl::combine(
|
||||
_maxTopHeight.value(),
|
||||
_aboveHeight.value(),
|
||||
_belowHeight.value(),
|
||||
_1 + _2 + middle + _3);
|
||||
}
|
||||
|
||||
rpl::producer<int> ToggleChatsController::boxHeightValue() const {
|
||||
return _desiredHeight.value() | rpl::map([=](int value) {
|
||||
return std::min(value, st::boxMaxListHeight);
|
||||
});
|
||||
}
|
||||
|
||||
void ShowImportError(
|
||||
not_null<Window::SessionController*> window,
|
||||
FilterId id,
|
||||
int added,
|
||||
const QString &error) {
|
||||
const auto session = &window->session();
|
||||
const auto &list = session->data().chatsFilters().list();
|
||||
const auto i = ranges::find(list, id, &Data::ChatFilter::id);
|
||||
const auto count = added
|
||||
+ ((i != end(list)) ? int(i->always().size()) : 0);
|
||||
if (error == u"CHANNELS_TOO_MUCH"_q) {
|
||||
window->show(Box(ChannelsLimitBox, session));
|
||||
} else if (error == u"FILTER_INCLUDE_TOO_MUCH"_q) {
|
||||
window->show(Box(FilterChatsLimitBox, session, count));
|
||||
} else if (error == u"CHATLISTS_TOO_MUCH"_q) {
|
||||
window->show(Box(ShareableFiltersLimitBox, session));
|
||||
} else {
|
||||
window->showToast((error == u"INVITE_SLUG_EXPIRED"_q)
|
||||
? tr::lng_group_invite_bad_link(tr::now)
|
||||
: error);
|
||||
}
|
||||
}
|
||||
|
||||
void ShowImportToast(
|
||||
base::weak_ptr<Window::SessionController> weak,
|
||||
const QString &title,
|
||||
Ui::FilterLinkHeaderType type,
|
||||
int added) {
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
const auto created = (type == Ui::FilterLinkHeaderType::AddingFilter);
|
||||
const auto phrase = created
|
||||
? tr::lng_filters_added_title
|
||||
: tr::lng_filters_updated_title;
|
||||
auto text = Ui::Text::Bold(phrase(tr::now, lt_folder, title));
|
||||
if (added > 0) {
|
||||
const auto phrase = created
|
||||
? tr::lng_filters_added_also
|
||||
: tr::lng_filters_updated_also;
|
||||
text.append('\n').append(phrase(tr::now, lt_count, added));
|
||||
}
|
||||
strong->showToast(std::move(text));
|
||||
}
|
||||
|
||||
void ProcessFilterInvite(
|
||||
base::weak_ptr<Window::SessionController> weak,
|
||||
const QString &slug,
|
||||
FilterId filterId,
|
||||
const QString &title,
|
||||
const QString &iconEmoji,
|
||||
std::vector<not_null<PeerData*>> peers,
|
||||
std::vector<not_null<PeerData*>> already) {
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
Core::App().hideMediaView();
|
||||
if (peers.empty() && !filterId) {
|
||||
strong->showToast(tr::lng_group_invite_bad_link(tr::now));
|
||||
return;
|
||||
}
|
||||
const auto fullyAdded = (peers.empty() && filterId);
|
||||
auto controller = std::make_unique<ToggleChatsController>(
|
||||
strong,
|
||||
ToggleAction::Adding,
|
||||
title,
|
||||
std::move(peers),
|
||||
std::move(already));
|
||||
const auto raw = controller.get();
|
||||
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||
box->setStyle(st::filterInviteBox);
|
||||
|
||||
using Type = Ui::FilterLinkHeaderType;
|
||||
const auto type = fullyAdded
|
||||
? Type::AllAdded
|
||||
: !filterId
|
||||
? Type::AddingFilter
|
||||
: Type::AddingChats;
|
||||
auto badge = raw->selectedValue(
|
||||
) | rpl::map([=](const base::flat_set<not_null<PeerData*>> &peers) {
|
||||
return int(peers.size());
|
||||
});
|
||||
InitFilterLinkHeader(box, [=](int min, int max, int addedTop) {
|
||||
raw->adjust(min, max, addedTop);
|
||||
}, type, title, iconEmoji, rpl::duplicate(badge));
|
||||
|
||||
raw->setRealContentHeight(box->heightValue());
|
||||
|
||||
auto owned = Ui::FilterLinkProcessButton(
|
||||
box,
|
||||
type,
|
||||
title,
|
||||
std::move(badge));
|
||||
|
||||
const auto button = owned.data();
|
||||
box->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
const auto &padding = st::filterInviteBox.buttonPadding;
|
||||
button->resizeToWidth(width
|
||||
- padding.left()
|
||||
- padding.right());
|
||||
button->moveToLeft(padding.left(), padding.top());
|
||||
}, button->lifetime());
|
||||
|
||||
box->addButton(std::move(owned));
|
||||
|
||||
struct State {
|
||||
bool importing = false;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
|
||||
raw->selectedValue(
|
||||
) | rpl::start_with_next([=](
|
||||
base::flat_set<not_null<PeerData*>> &&peers) {
|
||||
button->setClickedCallback([=] {
|
||||
if (peers.empty()) {
|
||||
box->closeBox();
|
||||
} else if (!state->importing) {
|
||||
state->importing = true;
|
||||
const auto added = int(peers.size());
|
||||
ImportInvite(slug, filterId, peers, crl::guard(box, [=] {
|
||||
ShowImportToast(weak, title, type, peers.size());
|
||||
box->closeBox();
|
||||
}), crl::guard(box, [=](QString text) {
|
||||
if (const auto strong = weak.get()) {
|
||||
ShowImportError(strong, filterId, added, text);
|
||||
}
|
||||
state->importing = false;
|
||||
}));
|
||||
}
|
||||
});
|
||||
}, box->lifetime());
|
||||
};
|
||||
strong->show(
|
||||
Box<PeerListBox>(std::move(controller), std::move(initBox)));
|
||||
}
|
||||
|
||||
void ProcessFilterInvite(
|
||||
base::weak_ptr<Window::SessionController> weak,
|
||||
const QString &slug,
|
||||
FilterId filterId,
|
||||
std::vector<not_null<PeerData*>> peers,
|
||||
std::vector<not_null<PeerData*>> already) {
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
Core::App().hideMediaView();
|
||||
const auto &list = strong->session().data().chatsFilters().list();
|
||||
const auto it = ranges::find(list, filterId, &Data::ChatFilter::id);
|
||||
if (it == end(list)) {
|
||||
strong->showToast(u"Filter not found :shrug:"_q);
|
||||
return;
|
||||
}
|
||||
ProcessFilterInvite(
|
||||
weak,
|
||||
slug,
|
||||
filterId,
|
||||
it->title(),
|
||||
it->iconEmoji(),
|
||||
std::move(peers),
|
||||
std::move(already));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SaveNewFilterPinned(
|
||||
not_null<Main::Session*> session,
|
||||
@@ -25,7 +676,176 @@ void SaveNewFilterPinned(
|
||||
MTP_int(filterId),
|
||||
filter.tl()
|
||||
)).send();
|
||||
}
|
||||
|
||||
void CheckFilterInvite(
|
||||
not_null<Window::SessionController*> controller,
|
||||
const QString &slug) {
|
||||
const auto session = &controller->session();
|
||||
const auto weak = base::make_weak(controller);
|
||||
session->api().checkFilterInvite(slug, [=](
|
||||
const MTPchatlists_ChatlistInvite &result) {
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
auto title = QString();
|
||||
auto iconEmoji = QString();
|
||||
auto filterId = FilterId();
|
||||
auto peers = std::vector<not_null<PeerData*>>();
|
||||
auto already = std::vector<not_null<PeerData*>>();
|
||||
auto &owner = strong->session().data();
|
||||
result.match([&](const auto &data) {
|
||||
owner.processUsers(data.vusers());
|
||||
owner.processChats(data.vchats());
|
||||
});
|
||||
const auto parseList = [&](const MTPVector<MTPPeer> &list) {
|
||||
auto result = std::vector<not_null<PeerData*>>();
|
||||
result.reserve(list.v.size());
|
||||
for (const auto &peer : list.v) {
|
||||
result.push_back(owner.peer(peerFromMTP(peer)));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
result.match([&](const MTPDchatlists_chatlistInvite &data) {
|
||||
title = qs(data.vtitle());
|
||||
iconEmoji = data.vemoticon().value_or_empty();
|
||||
peers = parseList(data.vpeers());
|
||||
}, [&](const MTPDchatlists_chatlistInviteAlready &data) {
|
||||
filterId = data.vfilter_id().v;
|
||||
peers = parseList(data.vmissing_peers());
|
||||
already = parseList(data.valready_peers());
|
||||
});
|
||||
|
||||
const auto notLoaded = filterId
|
||||
&& !ranges::contains(
|
||||
owner.chatsFilters().list(),
|
||||
filterId,
|
||||
&Data::ChatFilter::id);
|
||||
if (notLoaded) {
|
||||
const auto lifetime = std::make_shared<rpl::lifetime>();
|
||||
owner.chatsFilters().changed(
|
||||
) | rpl::start_with_next([=] {
|
||||
lifetime->destroy();
|
||||
ProcessFilterInvite(
|
||||
weak,
|
||||
slug,
|
||||
filterId,
|
||||
std::move(peers),
|
||||
std::move(already));
|
||||
}, *lifetime);
|
||||
owner.chatsFilters().reload();
|
||||
} else if (filterId) {
|
||||
ProcessFilterInvite(
|
||||
weak,
|
||||
slug,
|
||||
filterId,
|
||||
std::move(peers),
|
||||
std::move(already));
|
||||
} else {
|
||||
ProcessFilterInvite(
|
||||
weak,
|
||||
slug,
|
||||
filterId,
|
||||
title,
|
||||
iconEmoji,
|
||||
std::move(peers),
|
||||
std::move(already));
|
||||
}
|
||||
}, [=](const MTP::Error &error) {
|
||||
if (error.code() != 400) {
|
||||
return;
|
||||
}
|
||||
ProcessFilterInvite(weak, slug, {}, {}, {}, {}, {});
|
||||
});
|
||||
}
|
||||
|
||||
void ProcessFilterUpdate(
|
||||
base::weak_ptr<Window::SessionController> weak,
|
||||
FilterId filterId,
|
||||
std::vector<not_null<PeerData*>> missing) {
|
||||
if (const auto strong = missing.empty() ? weak.get() : nullptr) {
|
||||
strong->session().data().chatsFilters().moreChatsHide(filterId);
|
||||
return;
|
||||
}
|
||||
ProcessFilterInvite(weak, QString(), filterId, std::move(missing), {});
|
||||
}
|
||||
|
||||
void ProcessFilterRemove(
|
||||
base::weak_ptr<Window::SessionController> weak,
|
||||
const QString &title,
|
||||
const QString &iconEmoji,
|
||||
std::vector<not_null<PeerData*>> all,
|
||||
std::vector<not_null<PeerData*>> suggest,
|
||||
Fn<void(std::vector<not_null<PeerData*>>)> done) {
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
Core::App().hideMediaView();
|
||||
if (all.empty() && suggest.empty()) {
|
||||
done({});
|
||||
return;
|
||||
}
|
||||
auto controller = std::make_unique<ToggleChatsController>(
|
||||
strong,
|
||||
ToggleAction::Removing,
|
||||
title,
|
||||
std::move(suggest),
|
||||
std::move(all));
|
||||
const auto raw = controller.get();
|
||||
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||
box->setStyle(st::filterInviteBox);
|
||||
|
||||
const auto type = Ui::FilterLinkHeaderType::Removing;
|
||||
auto badge = raw->selectedValue(
|
||||
) | rpl::map([=](const base::flat_set<not_null<PeerData*>> &peers) {
|
||||
return int(peers.size());
|
||||
});
|
||||
InitFilterLinkHeader(box, [=](int min, int max, int addedTop) {
|
||||
raw->adjust(min, max, addedTop);
|
||||
}, type, title, iconEmoji, rpl::single(0));
|
||||
|
||||
auto owned = Ui::FilterLinkProcessButton(
|
||||
box,
|
||||
type,
|
||||
title,
|
||||
std::move(badge));
|
||||
|
||||
const auto button = owned.data();
|
||||
box->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
const auto &padding = st::filterInviteBox.buttonPadding;
|
||||
button->resizeToWidth(width
|
||||
- padding.left()
|
||||
- padding.right());
|
||||
button->moveToLeft(padding.left(), padding.top());
|
||||
}, button->lifetime());
|
||||
|
||||
box->addButton(std::move(owned));
|
||||
|
||||
raw->selectedValue(
|
||||
) | rpl::start_with_next([=](
|
||||
base::flat_set<not_null<PeerData*>> &&peers) {
|
||||
button->setClickedCallback([=] {
|
||||
done(peers | ranges::to_vector);
|
||||
box->closeBox();
|
||||
});
|
||||
}, box->lifetime());
|
||||
};
|
||||
strong->show(
|
||||
Box<PeerListBox>(std::move(controller), std::move(initBox)));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<not_null<PeerData*>> ExtractSuggestRemoving(
|
||||
const Data::ChatFilter &filter) {
|
||||
if (!filter.chatlist()) {
|
||||
return {};
|
||||
}
|
||||
return filter.always() | ranges::views::filter([](
|
||||
not_null<History*> history) {
|
||||
return history->peer->isChannel();
|
||||
}) | ranges::views::transform(&History::peer) | ranges::to_vector;
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -11,10 +11,38 @@ namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace Data {
|
||||
class ChatFilter;
|
||||
} // namespace Data
|
||||
|
||||
namespace Api {
|
||||
|
||||
void SaveNewFilterPinned(
|
||||
not_null<Main::Session*> session,
|
||||
FilterId filterId);
|
||||
|
||||
void CheckFilterInvite(
|
||||
not_null<Window::SessionController*> controller,
|
||||
const QString &slug);
|
||||
|
||||
void ProcessFilterUpdate(
|
||||
base::weak_ptr<Window::SessionController> weak,
|
||||
FilterId filterId,
|
||||
std::vector<not_null<PeerData*>> missing);
|
||||
|
||||
void ProcessFilterRemove(
|
||||
base::weak_ptr<Window::SessionController> weak,
|
||||
const QString &title,
|
||||
const QString &iconEmoji,
|
||||
std::vector<not_null<PeerData*>> all,
|
||||
std::vector<not_null<PeerData*>> suggest,
|
||||
Fn<void(std::vector<not_null<PeerData*>>)> done);
|
||||
|
||||
[[nodiscard]] std::vector<not_null<PeerData*>> ExtractSuggestRemoving(
|
||||
const Data::ChatFilter &filter);
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -22,7 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "boxes/premium_limits_box.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_layers.h"
|
||||
@@ -85,20 +85,17 @@ void SubmitChatInvite(
|
||||
}
|
||||
|
||||
strongController->hideLayer();
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = Window::Show(strongController).toastParent(),
|
||||
.text = { [&] {
|
||||
if (type == u"INVITE_REQUEST_SENT"_q) {
|
||||
return isGroup
|
||||
? tr::lng_group_request_sent(tr::now)
|
||||
: tr::lng_group_request_sent_channel(tr::now);
|
||||
} else if (type == u"USERS_TOO_MUCH"_q) {
|
||||
return tr::lng_group_invite_no_room(tr::now);
|
||||
} else {
|
||||
return tr::lng_group_invite_bad_link(tr::now);
|
||||
}
|
||||
}() },
|
||||
.duration = ApiWrap::kJoinErrorDuration });
|
||||
strongController->showToast([&] {
|
||||
if (type == u"INVITE_REQUEST_SENT"_q) {
|
||||
return isGroup
|
||||
? tr::lng_group_request_sent(tr::now)
|
||||
: tr::lng_group_request_sent_channel(tr::now);
|
||||
} else if (type == u"USERS_TOO_MUCH"_q) {
|
||||
return tr::lng_group_invite_no_room(tr::now);
|
||||
} else {
|
||||
return tr::lng_group_invite_bad_link(tr::now);
|
||||
}
|
||||
}(), ApiWrap::kJoinErrorDuration);
|
||||
}).send();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "api/api_common.h"
|
||||
|
||||
#include "base/qt/qt_key_modifiers.h"
|
||||
#include "data/data_thread.h"
|
||||
|
||||
namespace Api {
|
||||
@@ -20,4 +21,11 @@ SendAction::SendAction(
|
||||
, topicRootId(replyTo) {
|
||||
}
|
||||
|
||||
SendOptions DefaultSendWhenOnlineOptions() {
|
||||
return {
|
||||
.scheduled = kScheduledUntilOnlineTimestamp,
|
||||
.silent = base::IsCtrlPressed(),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -15,6 +15,8 @@ class Thread;
|
||||
|
||||
namespace Api {
|
||||
|
||||
inline constexpr auto kScheduledUntilOnlineTimestamp = TimeId(0x7FFFFFFE);
|
||||
|
||||
struct SendOptions {
|
||||
PeerData *sendAs = nullptr;
|
||||
TimeId scheduled = 0;
|
||||
@@ -23,6 +25,7 @@ struct SendOptions {
|
||||
bool removeWebPageId = false;
|
||||
bool hideViaBot = false;
|
||||
};
|
||||
[[nodiscard]] SendOptions DefaultSendWhenOnlineOptions();
|
||||
|
||||
enum class SendType {
|
||||
Normal,
|
||||
|
||||
@@ -58,6 +58,7 @@ JoinedByLinkSlice ParseJoinedByLinkSlice(
|
||||
result.users.push_back({
|
||||
.user = owner.user(data.vuser_id()),
|
||||
.date = data.vdate().v,
|
||||
.viaFilterLink = data.is_via_chatlist(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ struct PeerInviteLinks {
|
||||
struct JoinedByLinkUser {
|
||||
not_null<UserData*> user;
|
||||
TimeId date = 0;
|
||||
bool viaFilterLink = false;
|
||||
};
|
||||
|
||||
struct JoinedByLinkSlice {
|
||||
|
||||
@@ -186,6 +186,7 @@ void PeerPhoto::updateSelf(
|
||||
const auto usedFileReference = photo->fileReference();
|
||||
_api.request(MTPphotos_UpdateProfilePhoto(
|
||||
MTP_flags(0),
|
||||
MTPInputUser(), // bot
|
||||
photo->mtpInput()
|
||||
)).done([=](const MTPphotos_Photo &result) {
|
||||
result.match([&](const MTPDphotos_photo &data) {
|
||||
@@ -252,6 +253,7 @@ void PeerPhoto::clear(not_null<PhotoData*> photo) {
|
||||
if (self->userpicPhotoId() == photo->id) {
|
||||
_api.request(MTPphotos_UpdateProfilePhoto(
|
||||
MTP_flags(0),
|
||||
MTPInputUser(), // bot
|
||||
MTP_inputPhotoEmpty()
|
||||
)).done([=](const MTPphotos_Photo &result) {
|
||||
self->setPhoto(MTP_userProfilePhotoEmpty());
|
||||
@@ -276,6 +278,7 @@ void PeerPhoto::clear(not_null<PhotoData*> photo) {
|
||||
if (fallbackPhotoId && (*fallbackPhotoId) == photo->id) {
|
||||
_api.request(MTPphotos_UpdateProfilePhoto(
|
||||
MTP_flags(MTPphotos_UpdateProfilePhoto::Flag::f_fallback),
|
||||
MTPInputUser(), // bot
|
||||
MTP_inputPhotoEmpty()
|
||||
)).send();
|
||||
_session->storage().add(Storage::UserPhotosSetBack(
|
||||
@@ -321,6 +324,7 @@ void PeerPhoto::set(not_null<PeerData*> peer, not_null<PhotoData*> photo) {
|
||||
if (peer == _session->user()) {
|
||||
_api.request(MTPphotos_UpdateProfilePhoto(
|
||||
MTP_flags(0),
|
||||
MTPInputUser(), // bot
|
||||
photo->mtpInput()
|
||||
)).done([=](const MTPphotos_Photo &result) {
|
||||
result.match([&](const MTPDphotos_photo &data) {
|
||||
@@ -363,13 +367,21 @@ void PeerPhoto::ready(
|
||||
done();
|
||||
}
|
||||
};
|
||||
if (peer->isSelf()) {
|
||||
const auto botUserInput = [&] {
|
||||
const auto user = peer->asUser();
|
||||
return (user && user->botInfo && user->botInfo->canEditInformation)
|
||||
? std::make_optional<MTPInputUser>(user->inputUser)
|
||||
: std::nullopt;
|
||||
}();
|
||||
if (peer->isSelf() || botUserInput) {
|
||||
using Flag = MTPphotos_UploadProfilePhoto::Flag;
|
||||
const auto none = MTPphotos_UploadProfilePhoto::Flags(0);
|
||||
_api.request(MTPphotos_UploadProfilePhoto(
|
||||
MTP_flags((file ? Flag::f_file : none)
|
||||
| (botUserInput ? Flag::f_bot : none)
|
||||
| (videoSize ? Flag::f_video_emoji_markup : none)
|
||||
| ((type == UploadType::Fallback) ? Flag::f_fallback : none)),
|
||||
botUserInput ? (*botUserInput) : MTPInputUser(), // bot
|
||||
file ? (*file) : MTPInputFile(),
|
||||
MTPInputFile(), // video
|
||||
MTPdouble(), // video_start_ts
|
||||
|
||||
@@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/boxes/report_box.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/layers/show.h"
|
||||
|
||||
namespace Api {
|
||||
|
||||
@@ -39,15 +39,14 @@ MTPreportReason ReasonToTL(const Ui::ReportReason &reason) {
|
||||
} // namespace
|
||||
|
||||
void SendReport(
|
||||
not_null<QWidget*> toastParent,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<PeerData*> peer,
|
||||
Ui::ReportReason reason,
|
||||
const QString &comment,
|
||||
std::variant<v::null_t, MessageIdsList, not_null<PhotoData*>> data) {
|
||||
auto weak = Ui::MakeWeak(toastParent.get());
|
||||
auto done = crl::guard(toastParent, [=] {
|
||||
Ui::Toast::Show(toastParent, tr::lng_report_thanks(tr::now));
|
||||
});
|
||||
auto done = [=] {
|
||||
show->showToast(tr::lng_report_thanks(tr::now));
|
||||
};
|
||||
v::match(data, [&](v::null_t) {
|
||||
peer->session().api().request(MTPaccount_ReportPeer(
|
||||
peer->input,
|
||||
|
||||
@@ -11,13 +11,14 @@ class PeerData;
|
||||
class PhotoData;
|
||||
|
||||
namespace Ui {
|
||||
class Show;
|
||||
enum class ReportReason;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Api {
|
||||
|
||||
void SendReport(
|
||||
not_null<QWidget*> toastParent,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<PeerData*> peer,
|
||||
Ui::ReportReason reason,
|
||||
const QString &comment,
|
||||
|
||||
@@ -48,27 +48,26 @@ void ToggleExistingMedia(
|
||||
} // namespace
|
||||
|
||||
void ToggleFavedSticker(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin) {
|
||||
ToggleFavedSticker(
|
||||
controller,
|
||||
std::move(show),
|
||||
document,
|
||||
std::move(origin),
|
||||
!document->owner().stickers().isFaved(document));
|
||||
}
|
||||
|
||||
void ToggleFavedSticker(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
bool faved) {
|
||||
if (faved && !document->sticker()) {
|
||||
return;
|
||||
}
|
||||
const auto weak = base::make_weak(controller);
|
||||
auto done = [=] {
|
||||
document->owner().stickers().setFaved(weak.get(), document, faved);
|
||||
document->owner().stickers().setFaved(show, document, faved);
|
||||
};
|
||||
ToggleExistingMedia(
|
||||
document,
|
||||
@@ -104,17 +103,16 @@ void ToggleRecentSticker(
|
||||
}
|
||||
|
||||
void ToggleSavedGif(
|
||||
Window::SessionController *controller,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
bool saved) {
|
||||
if (saved && !document->isGifv()) {
|
||||
return;
|
||||
}
|
||||
const auto weak = base::make_weak(controller);
|
||||
auto done = [=] {
|
||||
if (saved) {
|
||||
document->owner().stickers().addSavedGif(weak.get(), document);
|
||||
document->owner().stickers().addSavedGif(show, document);
|
||||
}
|
||||
};
|
||||
ToggleExistingMedia(
|
||||
|
||||
@@ -7,19 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
namespace ChatHelpers {
|
||||
class Show;
|
||||
} // namespace ChatHelpers
|
||||
|
||||
namespace Api {
|
||||
|
||||
void ToggleFavedSticker(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin);
|
||||
|
||||
void ToggleFavedSticker(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
bool faved);
|
||||
@@ -30,7 +30,7 @@ void ToggleRecentSticker(
|
||||
bool saved);
|
||||
|
||||
void ToggleSavedGif(
|
||||
Window::SessionController *controller,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
bool saved);
|
||||
|
||||
@@ -24,6 +24,14 @@ namespace {
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<MTPInputUser> BotUserInput(
|
||||
not_null<PeerData*> peer) {
|
||||
const auto user = peer->asUser();
|
||||
return (user && user->botInfo && user->botInfo->canEditInformation)
|
||||
? std::make_optional<MTPInputUser>(user->inputUser)
|
||||
: std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Usernames::Usernames(not_null<ApiWrap*> api)
|
||||
@@ -157,6 +165,12 @@ rpl::producer<rpl::no_value, Usernames::Error> Usernames::toggle(
|
||||
MTP_string(username),
|
||||
MTP_bool(active)
|
||||
)).done(done).fail(fail).send();
|
||||
} else if (const auto botUserInput = BotUserInput(peer)) {
|
||||
_api.request(MTPbots_ToggleUsername(
|
||||
*botUserInput,
|
||||
MTP_string(username),
|
||||
MTP_bool(active)
|
||||
)).done(done).fail(fail).send();
|
||||
} else {
|
||||
return rpl::never<rpl::no_value, Error>();
|
||||
}
|
||||
@@ -204,6 +218,12 @@ rpl::producer<> Usernames::reorder(
|
||||
MTP_vector<MTPstring>(std::move(tlUsernames))
|
||||
)).done(finish).fail(finish).send();
|
||||
_reorderRequests.emplace(peerId, requestId);
|
||||
} else if (const auto botUserInput = BotUserInput(peer)) {
|
||||
const auto requestId = _api.request(MTPbots_ReorderUsernames(
|
||||
*botUserInput,
|
||||
MTP_vector<MTPstring>(std::move(tlUsernames))
|
||||
)).done(finish).fail(finish).send();
|
||||
_reorderRequests.emplace(peerId, requestId);
|
||||
}
|
||||
return lifetime;
|
||||
};
|
||||
|
||||
@@ -619,8 +619,11 @@ bool WhoReadExists(not_null<HistoryItem*> item) {
|
||||
return false;
|
||||
}
|
||||
const auto type = DetectSeenType(item);
|
||||
const auto thread = item->topic()
|
||||
? (Data::Thread*)item->topic()
|
||||
: item->history();
|
||||
const auto unseen = (type == Ui::WhoReadType::Seen)
|
||||
? item->unread(item->history())
|
||||
? item->unread(thread)
|
||||
: item->isUnreadMedia();
|
||||
if (unseen) {
|
||||
return false;
|
||||
@@ -630,7 +633,6 @@ bool WhoReadExists(not_null<HistoryItem*> item) {
|
||||
const auto chat = peer->asChat();
|
||||
const auto megagroup = peer->asMegagroup();
|
||||
if ((!chat && !megagroup)
|
||||
|| peer->isForum()
|
||||
|| (megagroup
|
||||
&& (megagroup->flags() & ChannelDataFlag::ParticipantsHidden))) {
|
||||
return false;
|
||||
|
||||
@@ -87,7 +87,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/emoji_config.h"
|
||||
#include "ui/chat/attach/attach_prepare.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "support/support_helper.h"
|
||||
#include "settings/settings_premium.h"
|
||||
#include "storage/localimageloader.h"
|
||||
@@ -121,9 +121,16 @@ using UpdatedFileReferences = Data::UpdatedFileReferences;
|
||||
return TimeId(msgId >> 32);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<Window::Show> ShowForPeer(
|
||||
[[nodiscard]] std::shared_ptr<ChatHelpers::Show> ShowForPeer(
|
||||
not_null<PeerData*> peer) {
|
||||
return std::make_shared<Window::Show>(Core::App().windowFor(peer));
|
||||
if (const auto window = Core::App().windowFor(peer)) {
|
||||
if (const auto controller = window->sessionController()) {
|
||||
if (&controller->session() == &peer->session()) {
|
||||
return controller->uiShow();
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ShowChannelsLimitBox(not_null<PeerData*> peer) {
|
||||
@@ -380,6 +387,16 @@ void ApiWrap::checkChatInvite(
|
||||
)).done(std::move(done)).fail(std::move(fail)).send();
|
||||
}
|
||||
|
||||
void ApiWrap::checkFilterInvite(
|
||||
const QString &slug,
|
||||
FnMut<void(const MTPchatlists_ChatlistInvite &)> done,
|
||||
Fn<void(const MTP::Error &)> fail) {
|
||||
request(base::take(_checkFilterInviteRequestId)).cancel();
|
||||
_checkFilterInviteRequestId = request(
|
||||
MTPchatlists_CheckChatlistInvite(MTP_string(slug))
|
||||
).done(std::move(done)).fail(std::move(fail)).send();
|
||||
}
|
||||
|
||||
void ApiWrap::savePinnedOrder(Data::Folder *folder) {
|
||||
const auto &order = _session->data().pinnedChatsOrder(folder);
|
||||
const auto input = [](Dialogs::Key key) {
|
||||
@@ -474,13 +491,12 @@ void ApiWrap::sendMessageFail(
|
||||
uint64 randomId,
|
||||
FullMsgId itemId) {
|
||||
const auto show = ShowForPeer(peer);
|
||||
|
||||
if (error == u"PEER_FLOOD"_q) {
|
||||
if (show && error == u"PEER_FLOOD"_q) {
|
||||
show->showBox(
|
||||
Ui::MakeInformBox(
|
||||
PeerFloodErrorText(&session(), PeerFloodType::Send)),
|
||||
Ui::LayerOption::CloseOther);
|
||||
} else if (error == u"USER_BANNED_IN_CHANNEL"_q) {
|
||||
} else if (show && error == u"USER_BANNED_IN_CHANNEL"_q) {
|
||||
const auto link = Ui::Text::Link(
|
||||
tr::lng_cant_more_info(tr::now),
|
||||
session().createInternalLinkFull(u"spambot"_q));
|
||||
@@ -509,21 +525,16 @@ void ApiWrap::sendMessageFail(
|
||||
Assert(peer->isUser());
|
||||
if (const auto item = scheduled.lookupItem(peer->id, itemId.msg)) {
|
||||
scheduled.removeSending(item);
|
||||
show->showBox(
|
||||
Ui::MakeInformBox(tr::lng_cant_do_this()),
|
||||
Ui::LayerOption::CloseOther);
|
||||
}
|
||||
} else if (error == u"CHAT_FORWARDS_RESTRICTED"_q) {
|
||||
if (show->valid()) {
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = show->toastParent(),
|
||||
.text = { peer->isBroadcast()
|
||||
? tr::lng_error_noforwards_channel(tr::now)
|
||||
: tr::lng_error_noforwards_group(tr::now)
|
||||
},
|
||||
.duration = kJoinErrorDuration
|
||||
});
|
||||
if (show) {
|
||||
show->showBox(
|
||||
Ui::MakeInformBox(tr::lng_cant_do_this()),
|
||||
Ui::LayerOption::CloseOther);
|
||||
}
|
||||
}
|
||||
} else if (show && error == u"CHAT_FORWARDS_RESTRICTED"_q) {
|
||||
show->showToast(peer->isBroadcast()
|
||||
? tr::lng_error_noforwards_channel(tr::now)
|
||||
: tr::lng_error_noforwards_group(tr::now), kJoinErrorDuration);
|
||||
} else if (error == u"PREMIUM_ACCOUNT_REQUIRED"_q) {
|
||||
Settings::ShowPremium(&session(), "premium_stickers");
|
||||
}
|
||||
@@ -1692,12 +1703,8 @@ void ApiWrap::joinChannel(not_null<ChannelData*> channel) {
|
||||
}
|
||||
return QString();
|
||||
}();
|
||||
if (!text.isEmpty() && show->valid()) {
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = show->toastParent(),
|
||||
.text = { text },
|
||||
.duration = kJoinErrorDuration,
|
||||
});
|
||||
if (!text.isEmpty()) {
|
||||
show->showToast(text, kJoinErrorDuration);
|
||||
}
|
||||
}
|
||||
_channelAmInRequests.remove(channel);
|
||||
@@ -2058,8 +2065,8 @@ void ApiWrap::applyAffectedMessages(
|
||||
}
|
||||
|
||||
void ApiWrap::saveCurrentDraftToCloud() {
|
||||
Core::App().materializeLocalDrafts();
|
||||
for (const auto &controller : _session->windows()) {
|
||||
controller->materializeLocalDrafts();
|
||||
if (const auto thread = controller->activeChatCurrent().thread()) {
|
||||
const auto topic = thread->asTopic();
|
||||
if (topic && topic->creating()) {
|
||||
|
||||
@@ -202,6 +202,10 @@ public:
|
||||
const QString &hash,
|
||||
FnMut<void(const MTPChatInvite &)> done,
|
||||
Fn<void(const MTP::Error &)> fail);
|
||||
void checkFilterInvite(
|
||||
const QString &slug,
|
||||
FnMut<void(const MTPchatlists_ChatlistInvite &)> done,
|
||||
Fn<void(const MTP::Error &)> fail);
|
||||
|
||||
void processFullPeer(
|
||||
not_null<PeerData*> peer,
|
||||
@@ -653,6 +657,7 @@ private:
|
||||
mtpRequestId _termsUpdateRequestId = 0;
|
||||
|
||||
mtpRequestId _checkInviteRequestId = 0;
|
||||
mtpRequestId _checkFilterInviteRequestId = 0;
|
||||
|
||||
struct MigrateCallbacks {
|
||||
FnMut<void(not_null<ChannelData*>)> done;
|
||||
|
||||
@@ -112,7 +112,7 @@ void ChatCreateDone(
|
||||
if (done) {
|
||||
done(chat);
|
||||
} else {
|
||||
const auto show = std::make_shared<Window::Show>(navigation);
|
||||
const auto show = navigation->uiShow();
|
||||
navigation->showPeerHistory(chat);
|
||||
ChatInviteForbidden(
|
||||
show,
|
||||
@@ -141,7 +141,7 @@ void MustBePublicFailed(
|
||||
const auto text = channel->isMegagroup()
|
||||
? "Can't create a public group :("
|
||||
: "Can't create a public channel :(";
|
||||
Ui::Toast::Show(Window::Show(navigation).toastParent(), text);
|
||||
navigation->showToast(text);
|
||||
MustBePublicDestroy(channel);
|
||||
}
|
||||
|
||||
@@ -607,7 +607,7 @@ void GroupInfoBox::prepare() {
|
||||
: QString());
|
||||
(*menu)->addAction(
|
||||
text,
|
||||
[=, show = std::make_shared<Ui::BoxShow>(this)] {
|
||||
[=, show = uiShow()] {
|
||||
show->showBox(Box(TTLMenu::TTLBox, TTLMenu::Args{
|
||||
.show = show,
|
||||
.startTtl = _ttlPeriod,
|
||||
@@ -727,19 +727,14 @@ void GroupInfoBox::createGroup(
|
||||
}
|
||||
} else if (type == u"USERS_TOO_FEW"_q) {
|
||||
controller->show(
|
||||
Ui::MakeInformBox(tr::lng_cant_invite_privacy()),
|
||||
Ui::LayerOption::KeepOther);
|
||||
Ui::MakeInformBox(tr::lng_cant_invite_privacy()));
|
||||
} else if (type == u"PEER_FLOOD"_q) {
|
||||
controller->show(
|
||||
Ui::MakeInformBox(
|
||||
PeerFloodErrorText(
|
||||
&_navigation->session(),
|
||||
PeerFloodType::InviteGroup)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
controller->show(Ui::MakeInformBox(
|
||||
PeerFloodErrorText(
|
||||
&_navigation->session(),
|
||||
PeerFloodType::InviteGroup)));
|
||||
} else if (type == u"USER_RESTRICTED"_q) {
|
||||
controller->show(
|
||||
Ui::MakeInformBox(tr::lng_cant_do_this()),
|
||||
Ui::LayerOption::KeepOther);
|
||||
controller->show(Ui::MakeInformBox(tr::lng_cant_do_this()));
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
@@ -1220,9 +1215,7 @@ void SetupChannelBox::mousePressEvent(QMouseEvent *e) {
|
||||
return;
|
||||
} else if (!_channel->inviteLink().isEmpty()) {
|
||||
QGuiApplication::clipboard()->setText(_channel->inviteLink());
|
||||
Ui::Toast::Show(
|
||||
Ui::BoxShow(this).toastParent(),
|
||||
tr::lng_create_channel_link_copied(tr::now));
|
||||
showToast(tr::lng_create_channel_link_copied(tr::now));
|
||||
} else if (_channel->isFullLoaded() && !_creatingInviteLink) {
|
||||
_creatingInviteLink = true;
|
||||
_channel->session().api().inviteLinks().create(_channel);
|
||||
@@ -1456,12 +1449,10 @@ void SetupChannelBox::showRevokePublicLinkBoxForEdit() {
|
||||
const auto callback = [=] {
|
||||
*revoked = true;
|
||||
navigation->parentController()->show(
|
||||
Box<SetupChannelBox>(navigation, channel, mustBePublic, done),
|
||||
Ui::LayerOption::KeepOther);
|
||||
Box<SetupChannelBox>(navigation, channel, mustBePublic, done));
|
||||
};
|
||||
const auto revoker = navigation->parentController()->show(
|
||||
Box(PublicLinksLimitBox, navigation, callback),
|
||||
Ui::LayerOption::KeepOther);
|
||||
Box(PublicLinksLimitBox, navigation, callback));
|
||||
const auto session = &navigation->session();
|
||||
revoker->boxClosing(
|
||||
) | rpl::start_with_next(crl::guard(session, [=] {
|
||||
|
||||
@@ -10,23 +10,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/effects/round_checkbox.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/chat/attach/attach_extensions.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "boxes/background_preview_box.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "styles/style_overview.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_info.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -61,11 +68,15 @@ class BackgroundBox::Inner final : public Ui::RpWidget {
|
||||
public:
|
||||
Inner(
|
||||
QWidget *parent,
|
||||
not_null<Main::Session*> session);
|
||||
not_null<Main::Session*> session,
|
||||
PeerData *forPeer);
|
||||
~Inner();
|
||||
|
||||
rpl::producer<Data::WallPaper> chooseEvents() const;
|
||||
rpl::producer<Data::WallPaper> removeRequests() const;
|
||||
[[nodiscard]] rpl::producer<Data::WallPaper> chooseEvents() const;
|
||||
[[nodiscard]] rpl::producer<Data::WallPaper> removeRequests() const;
|
||||
|
||||
[[nodiscard]] auto resolveResetCustomPaper() const
|
||||
->std::optional<Data::WallPaper>;
|
||||
|
||||
void removePaper(const Data::WallPaper &data);
|
||||
|
||||
@@ -109,6 +120,7 @@ private:
|
||||
void resizeToContentAndPreload();
|
||||
void updatePapers();
|
||||
void requestPapers();
|
||||
void pushCustomPapers();
|
||||
void sortPapers();
|
||||
void paintPaper(
|
||||
QPainter &p,
|
||||
@@ -118,9 +130,13 @@ private:
|
||||
void validatePaperThumbnail(const Paper &paper) const;
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
PeerData * const _forPeer = nullptr;
|
||||
|
||||
MTP::Sender _api;
|
||||
|
||||
std::vector<Paper> _papers;
|
||||
uint64 _currentId = 0;
|
||||
uint64 _insertedResetId = 0;
|
||||
|
||||
Selection _over;
|
||||
Selection _overDown;
|
||||
@@ -133,8 +149,10 @@ private:
|
||||
|
||||
BackgroundBox::BackgroundBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: _controller(controller) {
|
||||
not_null<Window::SessionController*> controller,
|
||||
PeerData *forPeer)
|
||||
: _controller(controller)
|
||||
, _forPeer(forPeer) {
|
||||
}
|
||||
|
||||
void BackgroundBox::prepare() {
|
||||
@@ -144,15 +162,38 @@ void BackgroundBox::prepare() {
|
||||
|
||||
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
|
||||
|
||||
_inner = setInnerWidget(
|
||||
object_ptr<Inner>(this, &_controller->session()),
|
||||
st::backgroundScroll);
|
||||
auto wrap = object_ptr<Ui::VerticalLayout>(this);
|
||||
const auto container = wrap.data();
|
||||
|
||||
Settings::AddSkip(container);
|
||||
|
||||
const auto button = container->add(Settings::CreateButton(
|
||||
container,
|
||||
tr::lng_settings_bg_from_file(),
|
||||
st::infoProfileButton));
|
||||
object_ptr<Info::Profile::FloatingIcon>(
|
||||
button,
|
||||
st::infoIconMediaPhoto,
|
||||
st::infoSharedMediaButtonIconPosition);
|
||||
|
||||
button->setClickedCallback([=] {
|
||||
chooseFromFile();
|
||||
});
|
||||
|
||||
Settings::AddSkip(container);
|
||||
Settings::AddDivider(container);
|
||||
|
||||
_inner = container->add(
|
||||
object_ptr<Inner>(this, &_controller->session(), _forPeer));
|
||||
|
||||
container->resizeToWidth(st::boxWideWidth);
|
||||
|
||||
setInnerWidget(std::move(wrap), st::backgroundScroll);
|
||||
setInnerTopSkip(st::lineWidth);
|
||||
|
||||
_inner->chooseEvents(
|
||||
) | rpl::start_with_next([=](const Data::WallPaper &paper) {
|
||||
_controller->show(
|
||||
Box<BackgroundPreviewBox>(_controller, paper),
|
||||
Ui::LayerOption::KeepOther);
|
||||
chosen(paper);
|
||||
}, _inner->lifetime());
|
||||
|
||||
_inner->removeRequests(
|
||||
@@ -161,6 +202,120 @@ void BackgroundBox::prepare() {
|
||||
}, _inner->lifetime());
|
||||
}
|
||||
|
||||
void BackgroundBox::chooseFromFile() {
|
||||
const auto filterStart = _forPeer
|
||||
? u"Image files (*"_q
|
||||
: u"Theme files (*.tdesktop-theme *.tdesktop-palette *"_q;
|
||||
auto filters = QStringList(
|
||||
filterStart
|
||||
+ Ui::ImageExtensions().join(u" *"_q)
|
||||
+ u")"_q);
|
||||
filters.push_back(FileDialog::AllFilesFilter());
|
||||
const auto callback = [=](const FileDialog::OpenResult &result) {
|
||||
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_forPeer && !result.paths.isEmpty()) {
|
||||
const auto filePath = result.paths.front();
|
||||
const auto hasExtension = [&](QLatin1String extension) {
|
||||
return filePath.endsWith(extension, Qt::CaseInsensitive);
|
||||
};
|
||||
if (hasExtension(qstr(".tdesktop-theme"))
|
||||
|| hasExtension(qstr(".tdesktop-palette"))) {
|
||||
Window::Theme::Apply(filePath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto image = Images::Read({
|
||||
.path = result.paths.isEmpty() ? QString() : result.paths.front(),
|
||||
.content = result.remoteContent,
|
||||
.forceOpaque = true,
|
||||
}).image;
|
||||
if (image.isNull() || image.width() <= 0 || image.height() <= 0) {
|
||||
return;
|
||||
}
|
||||
auto local = Data::CustomWallPaper();
|
||||
local.setLocalImageAsThumbnail(std::make_shared<Image>(
|
||||
std::move(image)));
|
||||
_controller->show(Box<BackgroundPreviewBox>(
|
||||
_controller,
|
||||
local,
|
||||
BackgroundPreviewArgs{ _forPeer }));
|
||||
};
|
||||
FileDialog::GetOpenPath(
|
||||
this,
|
||||
tr::lng_choose_image(tr::now),
|
||||
filters.join(u";;"_q),
|
||||
crl::guard(this, callback));
|
||||
}
|
||||
|
||||
bool BackgroundBox::hasDefaultForPeer() const {
|
||||
Expects(_forPeer != nullptr);
|
||||
|
||||
const auto paper = _forPeer->wallPaper();
|
||||
if (!paper) {
|
||||
return true;
|
||||
}
|
||||
const auto reset = _inner->resolveResetCustomPaper();
|
||||
Assert(reset.has_value());
|
||||
return (paper->id() == reset->id());
|
||||
}
|
||||
|
||||
bool BackgroundBox::chosenDefaultForPeer(
|
||||
const Data::WallPaper &paper) const {
|
||||
if (!_forPeer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto reset = _inner->resolveResetCustomPaper();
|
||||
Assert(reset.has_value());
|
||||
return (paper.id() == reset->id());
|
||||
}
|
||||
|
||||
void BackgroundBox::chosen(const Data::WallPaper &paper) {
|
||||
if (chosenDefaultForPeer(paper)) {
|
||||
if (!hasDefaultForPeer()) {
|
||||
const auto reset = crl::guard(this, [=](Fn<void()> close) {
|
||||
resetForPeer();
|
||||
close();
|
||||
});
|
||||
_controller->show(Ui::MakeConfirmBox({
|
||||
.text = u"Are you sure you want to reset the wallpaper?"_q,
|
||||
.confirmed = reset,
|
||||
.confirmText = u"Reset"_q,
|
||||
}));
|
||||
} else {
|
||||
closeBox();
|
||||
}
|
||||
return;
|
||||
}
|
||||
_controller->show(Box<BackgroundPreviewBox>(
|
||||
_controller,
|
||||
paper,
|
||||
BackgroundPreviewArgs{ _forPeer }));
|
||||
}
|
||||
|
||||
void BackgroundBox::resetForPeer() {
|
||||
const auto api = &_controller->session().api();
|
||||
api->request(MTPmessages_SetChatWallPaper(
|
||||
MTP_flags(0),
|
||||
_forPeer->input,
|
||||
MTPInputWallPaper(),
|
||||
MTPWallPaperSettings(),
|
||||
MTPint()
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result);
|
||||
}).send();
|
||||
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
_forPeer->setWallPaper(std::nullopt);
|
||||
if (weak) {
|
||||
_controller->finishChatThemeEdit(_forPeer);
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundBox::removePaper(const Data::WallPaper &paper) {
|
||||
const auto session = &_controller->session();
|
||||
const auto remove = [=, weak = Ui::MakeWeak(this)](Fn<void()> &&close) {
|
||||
@@ -175,28 +330,28 @@ void BackgroundBox::removePaper(const Data::WallPaper &paper) {
|
||||
paper.mtpSettings()
|
||||
)).send();
|
||||
};
|
||||
_controller->show(
|
||||
Ui::MakeConfirmBox({
|
||||
.text = tr::lng_background_sure_delete(),
|
||||
.confirmed = remove,
|
||||
.confirmText = tr::lng_selected_delete(),
|
||||
}),
|
||||
Ui::LayerOption::KeepOther);
|
||||
_controller->show(Ui::MakeConfirmBox({
|
||||
.text = tr::lng_background_sure_delete(),
|
||||
.confirmed = remove,
|
||||
.confirmText = tr::lng_selected_delete(),
|
||||
}));
|
||||
}
|
||||
|
||||
BackgroundBox::Inner::Inner(
|
||||
QWidget *parent,
|
||||
not_null<Main::Session*> session)
|
||||
not_null<Main::Session*> session,
|
||||
PeerData *forPeer)
|
||||
: RpWidget(parent)
|
||||
, _session(session)
|
||||
, _forPeer(forPeer)
|
||||
, _api(&_session->mtp())
|
||||
, _check(std::make_unique<Ui::RoundCheckbox>(st::overviewCheck, [=] { update(); })) {
|
||||
_check->setChecked(true, anim::type::instant);
|
||||
if (_session->data().wallpapers().empty()) {
|
||||
resize(st::boxWideWidth, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
|
||||
} else {
|
||||
resize(st::boxWideWidth, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
|
||||
Window::Theme::IsNightModeValue(
|
||||
) | rpl::start_with_next([=] {
|
||||
updatePapers();
|
||||
}
|
||||
}, lifetime());
|
||||
requestPapers();
|
||||
|
||||
_session->downloaderTaskFinished(
|
||||
@@ -219,6 +374,7 @@ BackgroundBox::Inner::Inner(
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
@@ -232,35 +388,106 @@ void BackgroundBox::Inner::requestPapers() {
|
||||
}).send();
|
||||
}
|
||||
|
||||
auto BackgroundBox::Inner::resolveResetCustomPaper() const
|
||||
-> std::optional<Data::WallPaper> {
|
||||
if (!_forPeer) {
|
||||
return {};
|
||||
}
|
||||
const auto nonCustom = Window::Theme::Background()->paper();
|
||||
const auto themeEmoji = _forPeer->themeEmoji();
|
||||
if (themeEmoji.isEmpty()) {
|
||||
return nonCustom;
|
||||
}
|
||||
const auto &themes = _forPeer->owner().cloudThemes();
|
||||
const auto theme = themes.themeForEmoji(themeEmoji);
|
||||
if (!theme) {
|
||||
return nonCustom;
|
||||
}
|
||||
using Type = Data::CloudTheme::Type;
|
||||
const auto dark = Window::Theme::IsNightMode();
|
||||
const auto i = theme->settings.find(dark ? Type::Dark : Type::Light);
|
||||
if (i != end(theme->settings) && i->second.paper) {
|
||||
return *i->second.paper;
|
||||
}
|
||||
return nonCustom;
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::pushCustomPapers() {
|
||||
auto customId = uint64();
|
||||
if (const auto custom = _forPeer ? _forPeer->wallPaper() : nullptr) {
|
||||
customId = custom->id();
|
||||
const auto j = ranges::find(
|
||||
_papers,
|
||||
custom->id(),
|
||||
[](const Paper &paper) { return paper.data.id(); });
|
||||
if (j != end(_papers)) {
|
||||
j->data = j->data.withParamsFrom(*custom);
|
||||
} else {
|
||||
_papers.insert(begin(_papers), Paper{ *custom });
|
||||
}
|
||||
}
|
||||
if (const auto reset = resolveResetCustomPaper()) {
|
||||
_insertedResetId = reset->id();
|
||||
const auto j = ranges::find(
|
||||
_papers,
|
||||
_insertedResetId,
|
||||
[](const Paper &paper) { return paper.data.id(); });
|
||||
if (j != end(_papers)) {
|
||||
if (_insertedResetId != customId) {
|
||||
j->data = j->data.withParamsFrom(*reset);
|
||||
}
|
||||
} else {
|
||||
_papers.insert(begin(_papers), Paper{ *reset });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::sortPapers() {
|
||||
const auto current = Window::Theme::Background()->id();
|
||||
const auto night = Window::Theme::IsNightMode();
|
||||
const auto currentCustom = _forPeer ? _forPeer->wallPaper() : nullptr;
|
||||
_currentId = currentCustom
|
||||
? currentCustom->id()
|
||||
: _insertedResetId
|
||||
? _insertedResetId
|
||||
: Window::Theme::Background()->id();
|
||||
const auto dark = Window::Theme::IsNightMode();
|
||||
ranges::stable_sort(_papers, std::greater<>(), [&](const Paper &paper) {
|
||||
const auto &data = paper.data;
|
||||
return std::make_tuple(
|
||||
data.id() == current,
|
||||
night ? data.isDark() : !data.isDark(),
|
||||
_insertedResetId && (_insertedResetId == data.id()),
|
||||
data.id() == _currentId,
|
||||
dark ? data.isDark() : !data.isDark(),
|
||||
Data::IsDefaultWallPaper(data),
|
||||
!data.isDefault() && !Data::IsLegacy1DefaultWallPaper(data),
|
||||
Data::IsLegacy3DefaultWallPaper(data),
|
||||
Data::IsLegacy2DefaultWallPaper(data),
|
||||
Data::IsLegacy1DefaultWallPaper(data));
|
||||
});
|
||||
if (!_papers.empty() && _papers.front().data.id() == current) {
|
||||
if (!_papers.empty()
|
||||
&& _papers.front().data.id() == _currentId
|
||||
&& !currentCustom
|
||||
&& !_insertedResetId) {
|
||||
_papers.front().data = _papers.front().data.withParamsFrom(
|
||||
Window::Theme::Background()->paper());
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::updatePapers() {
|
||||
if (_session->data().wallpapers().empty()) {
|
||||
return;
|
||||
}
|
||||
_over = _overDown = Selection();
|
||||
|
||||
_papers = _session->data().wallpapers(
|
||||
) | ranges::views::filter([](const Data::WallPaper &paper) {
|
||||
return !paper.isPattern() || !paper.backgroundColors().empty();
|
||||
) | ranges::views::filter([&](const Data::WallPaper &paper) {
|
||||
return (!paper.isPattern() || !paper.backgroundColors().empty())
|
||||
&& (!_forPeer
|
||||
|| (!Data::IsDefaultWallPaper(paper)
|
||||
&& (Data::IsCloudWallPaper(paper)
|
||||
|| Data::IsCustomWallPaper(paper))));
|
||||
}) | ranges::views::transform([](const Data::WallPaper &paper) {
|
||||
return Paper{ paper };
|
||||
}) | ranges::to_vector;
|
||||
pushCustomPapers();
|
||||
sortPapers();
|
||||
resizeToContentAndPreload();
|
||||
}
|
||||
@@ -373,7 +600,7 @@ void BackgroundBox::Inner::paintPaper(
|
||||
}
|
||||
|
||||
const auto over = !v::is_null(_overDown) ? _overDown : _over;
|
||||
if (paper.data.id() == Window::Theme::Background()->id()) {
|
||||
if (paper.data.id() == _currentId) {
|
||||
const auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
|
||||
const auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
|
||||
_check->paint(p, checkLeft, checkTop, width());
|
||||
@@ -415,14 +642,13 @@ void BackgroundBox::Inner::mouseMoveEvent(QMouseEvent *e) {
|
||||
- st::stickerPanDeleteIconBg.width();
|
||||
const auto deleteBottom = row * (height + skip) + skip
|
||||
+ st::stickerPanDeleteIconBg.height();
|
||||
const auto currentId = Window::Theme::Background()->id();
|
||||
const auto inDelete = (x >= deleteLeft)
|
||||
&& (y < deleteBottom)
|
||||
&& Data::IsCloudWallPaper(data)
|
||||
&& !Data::IsDefaultWallPaper(data)
|
||||
&& !Data::IsLegacy2DefaultWallPaper(data)
|
||||
&& !Data::IsLegacy3DefaultWallPaper(data)
|
||||
&& (currentId != data.id());
|
||||
&& (_currentId != data.id());
|
||||
return (result >= _papers.size())
|
||||
? Selection()
|
||||
: inDelete
|
||||
|
||||
@@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
|
||||
class PeerData;
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
@@ -19,7 +21,10 @@ class WallPaper;
|
||||
|
||||
class BackgroundBox : public Ui::BoxContent {
|
||||
public:
|
||||
BackgroundBox(QWidget*, not_null<Window::SessionController*> controller);
|
||||
BackgroundBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller,
|
||||
PeerData *forPeer = nullptr);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -27,10 +32,18 @@ protected:
|
||||
private:
|
||||
class Inner;
|
||||
|
||||
void chosen(const Data::WallPaper &paper);
|
||||
[[nodiscard]] bool hasDefaultForPeer() const;
|
||||
[[nodiscard]] bool chosenDefaultForPeer(
|
||||
const Data::WallPaper &paper) const;
|
||||
void removePaper(const Data::WallPaper &paper);
|
||||
void resetForPeer();
|
||||
|
||||
void chooseFromFile();
|
||||
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
|
||||
QPointer<Inner> _inner;
|
||||
PeerData *_forPeer = nullptr;
|
||||
|
||||
};
|
||||
|
||||
@@ -17,10 +17,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "history/view/history_view_message.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
@@ -33,10 +36,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/background_preview_box.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/themes/window_themes_embedded.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "storage/file_upload.h"
|
||||
#include "storage/localimageloader.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtGui/QGuiApplication>
|
||||
@@ -44,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxWallPaperSlugLength = 255;
|
||||
constexpr auto kDefaultDimming = 50;
|
||||
|
||||
[[nodiscard]] bool IsValidWallPaperSlug(const QString &slug) {
|
||||
if (slug.isEmpty() || slug.size() > kMaxWallPaperSlugLength) {
|
||||
@@ -59,6 +67,24 @@ constexpr auto kMaxWallPaperSlugLength = 255;
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] AdminLog::OwnedItem GenerateServiceItem(
|
||||
not_null<HistoryView::ElementDelegate*> delegate,
|
||||
not_null<History*> history,
|
||||
const QString &text,
|
||||
bool out) {
|
||||
Expects(history->peer->isUser());
|
||||
|
||||
const auto flags = MessageFlag::FakeHistoryItem
|
||||
| MessageFlag::HasFromId
|
||||
| (out ? MessageFlag::Outgoing : MessageFlag(0));
|
||||
const auto item = history->makeMessage(
|
||||
history->owner().nextLocalMessageId(),
|
||||
flags,
|
||||
base::unixtime::now(),
|
||||
PreparedServiceText{ { text } });
|
||||
return AdminLog::OwnedItem(delegate, item);
|
||||
}
|
||||
|
||||
[[nodiscard]] AdminLog::OwnedItem GenerateTextItem(
|
||||
not_null<HistoryView::ElementDelegate*> delegate,
|
||||
not_null<History*> history,
|
||||
@@ -133,28 +159,49 @@ constexpr auto kMaxWallPaperSlugLength = 255;
|
||||
|
||||
} // namespace
|
||||
|
||||
struct BackgroundPreviewBox::OverridenStyle {
|
||||
style::Box box;
|
||||
style::IconButton toggle;
|
||||
style::MediaSlider slider;
|
||||
style::FlatLabel subtitle;
|
||||
};
|
||||
|
||||
BackgroundPreviewBox::BackgroundPreviewBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller,
|
||||
const Data::WallPaper &paper)
|
||||
const Data::WallPaper &paper,
|
||||
BackgroundPreviewArgs args)
|
||||
: SimpleElementDelegate(controller, [=] { update(); })
|
||||
, _controller(controller)
|
||||
, _forPeer(args.forPeer)
|
||||
, _fromMessageId(args.fromMessageId)
|
||||
, _chatStyle(std::make_unique<Ui::ChatStyle>())
|
||||
, _serviceHistory(_controller->session().data().history(
|
||||
PeerData::kServiceNotificationsId))
|
||||
, _service(nullptr)
|
||||
, _text1(GenerateTextItem(
|
||||
delegate(),
|
||||
_controller->session().data().history(PeerData::kServiceNotificationsId),
|
||||
tr::lng_background_text1(tr::now),
|
||||
_serviceHistory,
|
||||
(_forPeer
|
||||
? tr::lng_background_apply1(tr::now)
|
||||
: tr::lng_background_text1(tr::now)),
|
||||
false))
|
||||
, _text2(GenerateTextItem(
|
||||
delegate(),
|
||||
_controller->session().data().history(PeerData::kServiceNotificationsId),
|
||||
tr::lng_background_text2(tr::now),
|
||||
_serviceHistory,
|
||||
(_forPeer
|
||||
? tr::lng_background_apply2(tr::now)
|
||||
: tr::lng_background_text2(tr::now)),
|
||||
true))
|
||||
, _paper(paper)
|
||||
, _media(_paper.document() ? _paper.document()->createMediaView() : nullptr)
|
||||
, _radial([=](crl::time now) { radialAnimationCallback(now); }) {
|
||||
_chatStyle->apply(controller->defaultChatTheme().get());
|
||||
|
||||
, _radial([=](crl::time now) { radialAnimationCallback(now); })
|
||||
, _appNightMode(Window::Theme::IsNightModeValue())
|
||||
, _boxDarkMode(_appNightMode.current())
|
||||
, _dimmingIntensity(std::clamp(paper.patternIntensity(), 0, 100))
|
||||
, _dimmed(_forPeer
|
||||
&& (paper.document() || paper.localThumbnail())
|
||||
&& !paper.isPattern()) {
|
||||
if (_media) {
|
||||
_media->thumbnailWanted(_paper.fileOrigin());
|
||||
}
|
||||
@@ -163,6 +210,201 @@ BackgroundPreviewBox::BackgroundPreviewBox(
|
||||
) | rpl::start_with_next([=] {
|
||||
update();
|
||||
}, lifetime());
|
||||
|
||||
_appNightMode.changes(
|
||||
) | rpl::start_with_next([=](bool night) {
|
||||
_boxDarkMode = night;
|
||||
update();
|
||||
}, lifetime());
|
||||
|
||||
_boxDarkMode.changes(
|
||||
) | rpl::start_with_next([=](bool dark) {
|
||||
applyDarkMode(dark);
|
||||
}, lifetime());
|
||||
|
||||
const auto prepare = [=](bool dark, auto pointer) {
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
crl::async([=] {
|
||||
auto result = std::make_unique<style::palette>();
|
||||
Window::Theme::PreparePaletteCallback(dark, {})(*result);
|
||||
crl::on_main([=, result = std::move(result)]() mutable {
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->*pointer = std::move(result);
|
||||
strong->paletteReady();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
prepare(false, &BackgroundPreviewBox::_lightPalette);
|
||||
prepare(true, &BackgroundPreviewBox::_darkPalette);
|
||||
}
|
||||
|
||||
BackgroundPreviewBox::~BackgroundPreviewBox() = default;
|
||||
|
||||
void BackgroundPreviewBox::applyDarkMode(bool dark) {
|
||||
const auto equals = (dark == Window::Theme::IsNightMode());
|
||||
const auto &palette = (dark ? _darkPalette : _lightPalette);
|
||||
if (!equals && !palette) {
|
||||
_waitingForPalette = true;
|
||||
return;
|
||||
}
|
||||
_waitingForPalette = false;
|
||||
if (equals) {
|
||||
setStyle(st::defaultBox);
|
||||
_chatStyle->applyCustomPalette(nullptr);
|
||||
_paletteServiceBg = rpl::single(
|
||||
rpl::empty
|
||||
) | rpl::then(
|
||||
style::PaletteChanged()
|
||||
) | rpl::map([=] {
|
||||
return st::msgServiceBg->c;
|
||||
});
|
||||
} else {
|
||||
setStyle(overridenStyle(dark));
|
||||
_chatStyle->applyCustomPalette(palette.get());
|
||||
_paletteServiceBg = palette->msgServiceBg()->c;
|
||||
}
|
||||
resetTitle();
|
||||
rebuildButtons(dark);
|
||||
update();
|
||||
if (const auto parent = parentWidget()) {
|
||||
parent->update();
|
||||
}
|
||||
|
||||
if (_dimmed) {
|
||||
createDimmingSlider(dark);
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::createDimmingSlider(bool dark) {
|
||||
const auto created = !_dimmingWrap;
|
||||
if (created) {
|
||||
_dimmingWrap.create(this, object_ptr<Ui::RpWidget>(this));
|
||||
_dimmingContent = _dimmingWrap->entity();
|
||||
}
|
||||
_dimmingSlider = nullptr;
|
||||
for (const auto &child : _dimmingContent->children()) {
|
||||
if (child->isWidgetType()) {
|
||||
static_cast<QWidget*>(child)->hide();
|
||||
child->deleteLater();
|
||||
}
|
||||
}
|
||||
const auto equals = (dark == Window::Theme::IsNightMode());
|
||||
const auto inner = Ui::CreateChild<Ui::VerticalLayout>(_dimmingContent);
|
||||
inner->show();
|
||||
Settings::AddSubsectionTitle(
|
||||
inner,
|
||||
tr::lng_background_dimming(),
|
||||
style::margins(0, st::settingsSectionSkip, 0, 0),
|
||||
equals ? nullptr : dark ? &_dark->subtitle : &_light->subtitle);
|
||||
_dimmingSlider = inner->add(
|
||||
object_ptr<Ui::MediaSlider>(
|
||||
inner,
|
||||
(equals
|
||||
? st::defaultContinuousSlider
|
||||
: dark
|
||||
? _dark->slider
|
||||
: _light->slider)),
|
||||
st::localStorageLimitMargin);
|
||||
_dimmingSlider->setValue(_dimmingIntensity / 100.);
|
||||
_dimmingSlider->setAlwaysDisplayMarker(true);
|
||||
_dimmingSlider->resize(st::defaultContinuousSlider.seekSize);
|
||||
const auto handle = [=](float64 value) {
|
||||
const auto intensity = std::clamp(
|
||||
int(base::SafeRound(value * 100)),
|
||||
0,
|
||||
100);
|
||||
_paper = _paper.withPatternIntensity(intensity);
|
||||
_dimmingIntensity = intensity;
|
||||
update();
|
||||
};
|
||||
_dimmingSlider->setChangeProgressCallback(handle);
|
||||
_dimmingSlider->setChangeFinishedCallback(handle);
|
||||
inner->resizeToWidth(st::boxWideWidth);
|
||||
Ui::SendPendingMoveResizeEvents(inner);
|
||||
inner->move(0, 0);
|
||||
_dimmingContent->resize(inner->size());
|
||||
|
||||
_dimmingContent->paintRequest(
|
||||
) | rpl::start_with_next([=](QRect clip) {
|
||||
auto p = QPainter(_dimmingContent);
|
||||
const auto palette = (dark ? _darkPalette : _lightPalette).get();
|
||||
p.fillRect(clip, equals ? st::boxBg : palette->boxBg());
|
||||
}, _dimmingContent->lifetime());
|
||||
|
||||
_dimmingToggleScheduled = true;
|
||||
|
||||
if (created) {
|
||||
rpl::combine(
|
||||
heightValue(),
|
||||
_dimmingWrap->heightValue(),
|
||||
rpl::mappers::_1 - rpl::mappers::_2
|
||||
) | rpl::start_with_next([=](int top) {
|
||||
_dimmingWrap->move(0, top);
|
||||
}, _dimmingWrap->lifetime());
|
||||
|
||||
_dimmingWrap->toggle(dark, anim::type::instant);
|
||||
_dimmingHeight = _dimmingWrap->heightValue();
|
||||
_dimmingHeight.changes() | rpl::start_with_next([=] {
|
||||
update();
|
||||
}, _dimmingWrap->lifetime());
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::paletteReady() {
|
||||
if (_waitingForPalette) {
|
||||
applyDarkMode(_boxDarkMode.current());
|
||||
}
|
||||
}
|
||||
|
||||
const style::Box &BackgroundPreviewBox::overridenStyle(bool dark) {
|
||||
auto &st = dark ? _dark : _light;
|
||||
if (!st) {
|
||||
st = std::make_unique<OverridenStyle>(prepareOverridenStyle(dark));
|
||||
}
|
||||
return st->box;
|
||||
}
|
||||
|
||||
auto BackgroundPreviewBox::prepareOverridenStyle(bool dark)
|
||||
-> OverridenStyle {
|
||||
const auto p = (dark ? _darkPalette : _lightPalette).get();
|
||||
Assert(p != nullptr);
|
||||
|
||||
const auto &toggle = dark
|
||||
? st::backgroundSwitchToLight
|
||||
: st::backgroundSwitchToDark;
|
||||
auto result = OverridenStyle{
|
||||
.box = st::defaultBox,
|
||||
.toggle = toggle,
|
||||
.slider = st::defaultContinuousSlider,
|
||||
.subtitle = st::settingsSubsectionTitle,
|
||||
};
|
||||
result.box.button.textFg = p->lightButtonFg();
|
||||
result.box.button.textFgOver = p->lightButtonFgOver();
|
||||
result.box.button.numbersTextFg = p->lightButtonFg();
|
||||
result.box.button.numbersTextFgOver = p->lightButtonFgOver();
|
||||
result.box.button.textBg = p->lightButtonBg();
|
||||
result.box.button.textBgOver = p->lightButtonBgOver();
|
||||
result.box.button.ripple.color = p->lightButtonBgRipple();
|
||||
result.box.title.textFg = p->boxTitleFg();
|
||||
result.box.bg = p->boxBg();
|
||||
result.box.titleAdditionalFg = p->boxTitleAdditionalFg();
|
||||
|
||||
result.toggle.ripple.color = p->windowBgOver();
|
||||
result.toggle.icon = toggle.icon.withPalette(*p);
|
||||
result.toggle.iconOver = toggle.iconOver.withPalette(*p);
|
||||
|
||||
result.slider.activeFg = p->mediaPlayerActiveFg();
|
||||
result.slider.inactiveFg = p->mediaPlayerInactiveFg();
|
||||
result.slider.activeFgOver = p->mediaPlayerActiveFg();
|
||||
result.slider.inactiveFgOver = p->mediaPlayerInactiveFg();
|
||||
result.slider.activeFgDisabled = p->mediaPlayerInactiveFg();
|
||||
result.slider.inactiveFgDisabled = p->windowBg();
|
||||
result.slider.receivedTillFg = p->mediaPlayerInactiveFg();
|
||||
|
||||
result.subtitle.textFg = p->windowActiveTextFg();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::generateBackground() {
|
||||
@@ -184,30 +426,45 @@ not_null<HistoryView::ElementDelegate*> BackgroundPreviewBox::delegate() {
|
||||
return static_cast<HistoryView::ElementDelegate*>(this);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::prepare() {
|
||||
void BackgroundPreviewBox::resetTitle() {
|
||||
setTitle(tr::lng_background_header());
|
||||
}
|
||||
|
||||
addButton(tr::lng_background_apply(), [=] { apply(); });
|
||||
void BackgroundPreviewBox::rebuildButtons(bool dark) {
|
||||
clearButtons();
|
||||
addButton(_forPeer
|
||||
? tr::lng_background_apply_button()
|
||||
: tr::lng_background_apply(), [=] { apply(); });
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
if (_paper.hasShareUrl()) {
|
||||
if (!_forPeer && _paper.hasShareUrl()) {
|
||||
addLeftButton(tr::lng_background_share(), [=] { share(); });
|
||||
}
|
||||
updateServiceBg(_paper.backgroundColors());
|
||||
const auto equals = (dark == Window::Theme::IsNightMode());
|
||||
auto toggle = object_ptr<Ui::IconButton>(this, equals
|
||||
? (dark ? st::backgroundSwitchToLight : st::backgroundSwitchToDark)
|
||||
: dark ? _dark->toggle : _light->toggle);
|
||||
toggle->setClickedCallback([=] {
|
||||
_boxDarkMode = !_boxDarkMode.current();
|
||||
});
|
||||
addTopButton(std::move(toggle));
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::prepare() {
|
||||
applyDarkMode(Window::Theme::IsNightMode());
|
||||
|
||||
_paper.loadDocument();
|
||||
const auto document = _paper.document();
|
||||
if (document && document->loading()) {
|
||||
_radial.start(_media->progress());
|
||||
}
|
||||
if (!_paper.isPattern()
|
||||
&& (_paper.localThumbnail()
|
||||
|| (document && document->hasThumbnail()))) {
|
||||
createBlurCheckbox();
|
||||
if (const auto document = _paper.document()) {
|
||||
if (document->loading()) {
|
||||
_radial.start(_media->progress());
|
||||
}
|
||||
}
|
||||
|
||||
updateServiceBg(_paper.backgroundColors());
|
||||
|
||||
setScaledFromThumb();
|
||||
checkLoadedDocument();
|
||||
|
||||
_text1->setDisplayDate(true);
|
||||
_text1->setDisplayDate(false);
|
||||
_text1->initDimensions();
|
||||
_text1->resizeGetHeight(st::boxWideWidth);
|
||||
_text2->initDimensions();
|
||||
@@ -216,34 +473,158 @@ void BackgroundPreviewBox::prepare() {
|
||||
setDimensions(st::boxWideWidth, st::boxWideWidth);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::createBlurCheckbox() {
|
||||
void BackgroundPreviewBox::recreateBlurCheckbox() {
|
||||
const auto document = _paper.document();
|
||||
if (_paper.isPattern()
|
||||
|| (!_paper.localThumbnail()
|
||||
&& (!document || !document->hasThumbnail()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto blurred = _blur ? _blur->checked() : _paper.isBlurred();
|
||||
_blur = Ui::MakeChatServiceCheckbox(
|
||||
this,
|
||||
tr::lng_background_blur(tr::now),
|
||||
st::backgroundCheckbox,
|
||||
st::backgroundCheck,
|
||||
_paper.isBlurred(),
|
||||
blurred,
|
||||
[=] { return _serviceBg.value_or(QColor(255, 255, 255, 0)); });
|
||||
_blur->show();
|
||||
|
||||
rpl::combine(
|
||||
sizeValue(),
|
||||
_blur->sizeValue()
|
||||
) | rpl::start_with_next([=](QSize outer, QSize inner) {
|
||||
_blur->sizeValue(),
|
||||
_dimmingHeight.value()
|
||||
) | rpl::start_with_next([=](QSize outer, QSize inner, int dimming) {
|
||||
const auto bottom = st::historyPaddingBottom;
|
||||
_blur->move(
|
||||
(outer.width() - inner.width()) / 2,
|
||||
outer.height() - st::historyPaddingBottom - inner.height());
|
||||
outer.height() - dimming - bottom - inner.height());
|
||||
}, _blur->lifetime());
|
||||
|
||||
_blur->checkedChanges(
|
||||
) | rpl::start_with_next([=](bool checked) {
|
||||
checkBlurAnimationStart();
|
||||
update();
|
||||
}, lifetime());
|
||||
}, _blur->lifetime());
|
||||
|
||||
_blur->setDisabled(true);
|
||||
_blur->setDisabled(_paper.document() && _full.isNull());
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::apply() {
|
||||
if (_forPeer) {
|
||||
applyForPeer();
|
||||
} else {
|
||||
applyForEveryone();
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::uploadForPeer() {
|
||||
Expects(_forPeer != nullptr);
|
||||
|
||||
if (_uploadId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto session = &_controller->session();
|
||||
const auto ready = Window::Theme::PrepareWallPaper(
|
||||
session->mainDcId(),
|
||||
_paper.localThumbnail()->original());
|
||||
const auto documentId = ready.id;
|
||||
_uploadId = FullMsgId(
|
||||
session->userPeerId(),
|
||||
session->data().nextLocalMessageId());
|
||||
session->uploader().uploadMedia(_uploadId, ready);
|
||||
if (_uploadLifetime) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto document = session->data().document(documentId);
|
||||
document->uploadingData = std::make_unique<Data::UploadState>(
|
||||
document->size);
|
||||
|
||||
session->uploader().documentProgress(
|
||||
) | rpl::start_with_next([=](const FullMsgId &fullId) {
|
||||
if (fullId != _uploadId) {
|
||||
return;
|
||||
}
|
||||
_uploadProgress = document->uploading()
|
||||
? ((document->uploadingData->offset * 100)
|
||||
/ document->uploadingData->size)
|
||||
: 0.;
|
||||
update(radialRect());
|
||||
}, _uploadLifetime);
|
||||
|
||||
session->uploader().documentReady(
|
||||
) | rpl::start_with_next([=](const Storage::UploadedMedia &data) {
|
||||
if (data.fullId != _uploadId) {
|
||||
return;
|
||||
}
|
||||
_uploadProgress = 1.;
|
||||
_uploadLifetime.destroy();
|
||||
update(radialRect());
|
||||
session->api().request(MTPaccount_UploadWallPaper(
|
||||
MTP_flags(MTPaccount_UploadWallPaper::Flag::f_for_chat),
|
||||
data.info.file,
|
||||
MTP_string("image/jpeg"),
|
||||
_paper.mtpSettings()
|
||||
)).done([=](const MTPWallPaper &result) {
|
||||
result.match([&](const MTPDwallPaper &data) {
|
||||
session->data().documentConvert(
|
||||
session->data().document(documentId),
|
||||
data.vdocument());
|
||||
}, [&](const MTPDwallPaperNoFile &data) {
|
||||
LOG(("API Error: "
|
||||
"Got wallPaperNoFile after account.UploadWallPaper."));
|
||||
});
|
||||
if (const auto paper = Data::WallPaper::Create(session, result)) {
|
||||
setExistingForPeer(*paper);
|
||||
}
|
||||
}).send();
|
||||
}, _uploadLifetime);
|
||||
|
||||
_uploadProgress = 0.;
|
||||
_radial.start(_uploadProgress);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::setExistingForPeer(const Data::WallPaper &paper) {
|
||||
Expects(_forPeer != nullptr);
|
||||
|
||||
if (const auto already = _forPeer->wallPaper()) {
|
||||
if (already->equals(paper)) {
|
||||
_controller->finishChatThemeEdit(_forPeer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto api = &_controller->session().api();
|
||||
using Flag = MTPmessages_SetChatWallPaper::Flag;
|
||||
api->request(MTPmessages_SetChatWallPaper(
|
||||
MTP_flags((_fromMessageId ? Flag::f_id : Flag())
|
||||
| (_fromMessageId ? Flag() : Flag::f_wallpaper)
|
||||
| Flag::f_settings),
|
||||
_forPeer->input,
|
||||
paper.mtpInput(&_controller->session()),
|
||||
paper.mtpSettings(),
|
||||
MTP_int(_fromMessageId.msg)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result);
|
||||
}).send();
|
||||
|
||||
_forPeer->setWallPaper(paper);
|
||||
_controller->finishChatThemeEdit(_forPeer);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::applyForPeer() {
|
||||
Expects(_forPeer != nullptr);
|
||||
|
||||
if (Data::IsCustomWallPaper(_paper)) {
|
||||
uploadForPeer();
|
||||
} else {
|
||||
setExistingForPeer(_paper);
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::applyForEveryone() {
|
||||
const auto install = (_paper.id() != Window::Theme::Background()->id())
|
||||
&& Data::IsCloudWallPaper(_paper);
|
||||
_controller->content()->setChatBackground(_paper, std::move(_full));
|
||||
@@ -259,9 +640,7 @@ void BackgroundPreviewBox::apply() {
|
||||
void BackgroundPreviewBox::share() {
|
||||
QGuiApplication::clipboard()->setText(
|
||||
_paper.shareUrl(&_controller->session()));
|
||||
Ui::Toast::Show(
|
||||
Ui::BoxShow(this).toastParent(),
|
||||
tr::lng_background_link_copied(tr::now));
|
||||
showToast(tr::lng_background_link_copied(tr::now));
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
|
||||
@@ -278,6 +657,13 @@ void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
if (!_scaled.isNull()) {
|
||||
paintImage(p);
|
||||
const auto dimming = (_dimmed && _boxDarkMode.current())
|
||||
? _dimmingIntensity
|
||||
: 0;
|
||||
if (dimming > 0) {
|
||||
const auto alpha = 255 * dimming / 100;
|
||||
p.fillRect(e->rect(), QColor(0, 0, 0, alpha));
|
||||
}
|
||||
paintRadial(p);
|
||||
} else if (_generated.isNull()) {
|
||||
p.fillRect(e->rect(), st::boxBg);
|
||||
@@ -287,6 +673,15 @@ void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
|
||||
paintRadial(p);
|
||||
}
|
||||
paintTexts(p, ms);
|
||||
if (_dimmingToggleScheduled) {
|
||||
crl::on_main(this, [=] {
|
||||
if (!_dimmingToggleScheduled) {
|
||||
return;
|
||||
}
|
||||
_dimmingToggleScheduled = false;
|
||||
_dimmingWrap->toggle(_boxDarkMode.current(), anim::type::normal);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::paintImage(Painter &p) {
|
||||
@@ -336,9 +731,12 @@ void BackgroundPreviewBox::paintRadial(Painter &p) {
|
||||
}
|
||||
|
||||
int BackgroundPreviewBox::textsTop() const {
|
||||
const auto bottom = _blur ? _blur->y() : height();
|
||||
const auto bottom = _blur
|
||||
? _blur->y()
|
||||
: (height() - _dimmingHeight.current());
|
||||
return bottom
|
||||
- st::historyPaddingBottom
|
||||
- (_service ? _service->height() : 0)
|
||||
- _text1->height()
|
||||
- _text2->height();
|
||||
}
|
||||
@@ -353,6 +751,7 @@ QRect BackgroundPreviewBox::radialRect() const {
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::paintTexts(Painter &p, crl::time ms) {
|
||||
const auto heights = _service ? _service->height() : 0;
|
||||
const auto height1 = _text1->height();
|
||||
const auto height2 = _text2->height();
|
||||
auto context = _controller->defaultChatTheme()->preparePaintContext(
|
||||
@@ -361,7 +760,10 @@ void BackgroundPreviewBox::paintTexts(Painter &p, crl::time ms) {
|
||||
rect(),
|
||||
_controller->isGifPausedAtLeastFor(Window::GifPauseReason::Layer));
|
||||
p.translate(0, textsTop());
|
||||
paintDate(p);
|
||||
if (_service) {
|
||||
_service->draw(p, context);
|
||||
p.translate(0, heights);
|
||||
}
|
||||
|
||||
context.outbg = _text1->hasOutLayout();
|
||||
_text1->draw(p, context);
|
||||
@@ -372,36 +774,12 @@ void BackgroundPreviewBox::paintTexts(Painter &p, crl::time ms) {
|
||||
p.translate(0, height2);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::paintDate(Painter &p) {
|
||||
const auto date = _text1->Get<HistoryView::DateBadge>();
|
||||
if (!date || !_serviceBg) {
|
||||
return;
|
||||
}
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto text = date->text;
|
||||
const auto bubbleHeight = st::msgServicePadding.top() + st::msgServiceFont->height + st::msgServicePadding.bottom();
|
||||
const auto bubbleTop = st::msgServiceMargin.top();
|
||||
const auto textWidth = st::msgServiceFont->width(text);
|
||||
const auto bubbleWidth = st::msgServicePadding.left() + textWidth + st::msgServicePadding.right();
|
||||
const auto bubbleLeft = (width() - bubbleWidth) / 2;
|
||||
const auto radius = bubbleHeight / 2;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(*_serviceBg);
|
||||
p.drawRoundedRect(bubbleLeft, bubbleTop, bubbleWidth, bubbleHeight, radius, radius);
|
||||
p.setPen(st::msgServiceFg);
|
||||
p.setFont(st::msgServiceFont);
|
||||
p.drawText(bubbleLeft + st::msgServicePadding.left(), bubbleTop + st::msgServicePadding.top() + st::msgServiceFont->ascent, text);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::radialAnimationCallback(crl::time now) {
|
||||
Expects(_paper.document() != nullptr);
|
||||
|
||||
const auto document = _paper.document();
|
||||
const auto wasAnimating = _radial.animating();
|
||||
const auto updated = _radial.update(
|
||||
_media->progress(),
|
||||
!document->loading(),
|
||||
now);
|
||||
const auto updated = _uploadId
|
||||
? _radial.update(_uploadProgress, !_uploadLifetime, now)
|
||||
: _radial.update(_media->progress(), !document->loading(), now);
|
||||
if ((wasAnimating || _radial.animating())
|
||||
&& (!anim::Disabled() || updated)) {
|
||||
update(radialRect());
|
||||
@@ -448,8 +826,8 @@ void BackgroundPreviewBox::setScaledFromImage(
|
||||
}
|
||||
_scaled = Ui::PixmapFromImage(std::move(image));
|
||||
_blurred = Ui::PixmapFromImage(std::move(blurred));
|
||||
if (_blur && (!_paper.document() || !_full.isNull())) {
|
||||
_blur->setDisabled(false);
|
||||
if (_blur) {
|
||||
_blur->setDisabled(_paper.document() && _full.isNull());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,15 +852,34 @@ void BackgroundPreviewBox::updateServiceBg(const std::vector<QColor> &bg) {
|
||||
if (!count) {
|
||||
return;
|
||||
}
|
||||
auto red = 0, green = 0, blue = 0;
|
||||
auto red = 0LL, green = 0LL, blue = 0LL;
|
||||
for (const auto &color : bg) {
|
||||
red += color.red();
|
||||
green += color.green();
|
||||
blue += color.blue();
|
||||
}
|
||||
_serviceBg = Ui::ThemeAdjustedColor(
|
||||
st::msgServiceBg->c,
|
||||
QColor(red / count, green / count, blue / count));
|
||||
|
||||
_serviceBgLifetime = _paletteServiceBg.value(
|
||||
) | rpl::start_with_next([=](QColor color) {
|
||||
_serviceBg = Ui::ThemeAdjustedColor(
|
||||
color,
|
||||
QColor(red / count, green / count, blue / count));
|
||||
_chatStyle->applyAdjustedServiceBg(*_serviceBg);
|
||||
recreateBlurCheckbox();
|
||||
});
|
||||
|
||||
_service = GenerateServiceItem(
|
||||
delegate(),
|
||||
_serviceHistory,
|
||||
((_forPeer && !_fromMessageId)
|
||||
? tr::lng_background_other_info(
|
||||
tr::now,
|
||||
lt_user,
|
||||
_forPeer->shortName())
|
||||
: ItemDateText(_text1->data(), false)),
|
||||
false);
|
||||
_service->initDimensions();
|
||||
_service->resizeGetHeight(st::boxWideWidth);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::checkLoadedDocument() {
|
||||
|
||||
@@ -26,8 +26,16 @@ class SessionController;
|
||||
namespace Ui {
|
||||
class Checkbox;
|
||||
class ChatStyle;
|
||||
class MediaSlider;
|
||||
template <typename Widget>
|
||||
class SlideWrap;
|
||||
} // namespace Ui
|
||||
|
||||
struct BackgroundPreviewArgs {
|
||||
PeerData *forPeer = nullptr;
|
||||
FullMsgId fromMessageId;
|
||||
};
|
||||
|
||||
class BackgroundPreviewBox
|
||||
: public Ui::BoxContent
|
||||
, private HistoryView::SimpleElementDelegate {
|
||||
@@ -35,7 +43,9 @@ public:
|
||||
BackgroundPreviewBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller,
|
||||
const Data::WallPaper &paper);
|
||||
const Data::WallPaper &paper,
|
||||
BackgroundPreviewArgs args = {});
|
||||
~BackgroundPreviewBox();
|
||||
|
||||
static bool Start(
|
||||
not_null<Window::SessionController*> controller,
|
||||
@@ -48,11 +58,17 @@ protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
struct OverridenStyle;
|
||||
|
||||
using Element = HistoryView::Element;
|
||||
not_null<HistoryView::ElementDelegate*> delegate();
|
||||
HistoryView::Context elementContext() override;
|
||||
|
||||
void apply();
|
||||
void applyForPeer();
|
||||
void applyForEveryone();
|
||||
void uploadForPeer();
|
||||
void setExistingForPeer(const Data::WallPaper &paper);
|
||||
void share();
|
||||
void radialAnimationCallback(crl::time now);
|
||||
QRect radialRect() const;
|
||||
@@ -65,14 +81,26 @@ private:
|
||||
void paintImage(Painter &p);
|
||||
void paintRadial(Painter &p);
|
||||
void paintTexts(Painter &p, crl::time ms);
|
||||
void paintDate(Painter &p);
|
||||
void createBlurCheckbox();
|
||||
void recreateBlurCheckbox();
|
||||
int textsTop() const;
|
||||
void startFadeInFrom(QPixmap previous);
|
||||
void checkBlurAnimationStart();
|
||||
|
||||
[[nodiscard]] const style::Box &overridenStyle(bool dark);
|
||||
void paletteReady();
|
||||
void applyDarkMode(bool dark);
|
||||
[[nodiscard]] OverridenStyle prepareOverridenStyle(bool dark);
|
||||
|
||||
void resetTitle();
|
||||
void rebuildButtons(bool dark);
|
||||
void createDimmingSlider(bool dark);
|
||||
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
PeerData * const _forPeer = nullptr;
|
||||
FullMsgId _fromMessageId;
|
||||
std::unique_ptr<Ui::ChatStyle> _chatStyle;
|
||||
const not_null<History*> _serviceHistory;
|
||||
AdminLog::OwnedItem _service;
|
||||
AdminLog::OwnedItem _text1;
|
||||
AdminLog::OwnedItem _text2;
|
||||
Data::WallPaper _paper;
|
||||
@@ -85,4 +113,25 @@ private:
|
||||
std::optional<QColor> _serviceBg;
|
||||
object_ptr<Ui::Checkbox> _blur = { nullptr };
|
||||
|
||||
rpl::variable<bool> _appNightMode;
|
||||
rpl::variable<bool> _boxDarkMode;
|
||||
std::unique_ptr<OverridenStyle> _light, _dark;
|
||||
std::unique_ptr<style::palette> _lightPalette, _darkPalette;
|
||||
bool _waitingForPalette = false;
|
||||
|
||||
object_ptr<Ui::SlideWrap<Ui::RpWidget>> _dimmingWrap = { nullptr };
|
||||
Ui::RpWidget *_dimmingContent = nullptr;
|
||||
Ui::MediaSlider *_dimmingSlider = nullptr;
|
||||
int _dimmingIntensity = 0;
|
||||
rpl::variable<int> _dimmingHeight = 0;
|
||||
bool _dimmed = false;
|
||||
bool _dimmingToggleScheduled = false;
|
||||
|
||||
FullMsgId _uploadId;
|
||||
float64 _uploadProgress = 0.;
|
||||
rpl::lifetime _uploadLifetime;
|
||||
|
||||
rpl::variable<QColor> _paletteServiceBg;
|
||||
rpl::lifetime _serviceBgLifetime;
|
||||
|
||||
};
|
||||
|
||||
@@ -1,575 +0,0 @@
|
||||
/*
|
||||
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/change_phone_box.h"
|
||||
|
||||
#include "core/file_utilities.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/sent_code_field.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/text/format_values.h" // Ui::FormatPhone
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/fields/special_fields.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "boxes/phone_banned_box.h"
|
||||
#include "countries/countries_instance.h" // Countries::ExtractPhoneCode.
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "lottie/lottie_icon.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "apiwrap.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void CreateErrorLabel(
|
||||
QWidget *parent,
|
||||
object_ptr<Ui::FadeWrap<Ui::FlatLabel>> &label,
|
||||
const QString &text,
|
||||
int x,
|
||||
int y) {
|
||||
if (label) {
|
||||
label->hide(anim::type::normal);
|
||||
|
||||
auto saved = label.data();
|
||||
auto destroy = [old = std::move(label)]() mutable {
|
||||
old.destroyDelayed();
|
||||
};
|
||||
|
||||
using namespace rpl::mappers;
|
||||
saved->shownValue()
|
||||
| rpl::filter(_1 == false)
|
||||
| rpl::take(1)
|
||||
| rpl::start_with_done(
|
||||
std::move(destroy),
|
||||
saved->lifetime());
|
||||
}
|
||||
if (!text.isEmpty()) {
|
||||
label.create(
|
||||
parent,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
parent,
|
||||
text,
|
||||
st::changePhoneError));
|
||||
label->hide(anim::type::instant);
|
||||
label->moveToLeft(x, y);
|
||||
label->show(anim::type::normal);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] int ErrorSkip() {
|
||||
return st::boxLittleSkip + st::changePhoneError.style.font->height;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Settings {
|
||||
|
||||
class ChangePhone::EnterPhone : public Ui::BoxContent {
|
||||
public:
|
||||
EnterPhone(QWidget*, not_null<Window::SessionController*> controller);
|
||||
|
||||
void setInnerFocus() override {
|
||||
_phone->setFocusFast();
|
||||
}
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
private:
|
||||
void submit();
|
||||
void sendPhoneDone(
|
||||
const MTPauth_SentCode &result,
|
||||
const QString &phoneNumber);
|
||||
void sendPhoneFail(const MTP::Error &error, const QString &phoneNumber);
|
||||
void showError(const QString &text);
|
||||
void hideError() {
|
||||
showError(QString());
|
||||
}
|
||||
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
MTP::Sender _api;
|
||||
|
||||
object_ptr<Ui::PhoneInput> _phone = { nullptr };
|
||||
object_ptr<Ui::FadeWrap<Ui::FlatLabel>> _error = { nullptr };
|
||||
mtpRequestId _requestId = 0;
|
||||
|
||||
};
|
||||
|
||||
class ChangePhone::EnterCode : public Ui::BoxContent {
|
||||
public:
|
||||
EnterCode(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller,
|
||||
const QString &phone,
|
||||
const QString &hash,
|
||||
const QString &openUrl,
|
||||
int codeLength,
|
||||
int callTimeout);
|
||||
|
||||
void setInnerFocus() override {
|
||||
_code->setFocusFast();
|
||||
}
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
private:
|
||||
void submit(const QString &code);
|
||||
void sendCall();
|
||||
void updateCall();
|
||||
void sendCodeFail(const MTP::Error &error);
|
||||
void showError(const QString &text);
|
||||
void hideError() {
|
||||
showError(QString());
|
||||
}
|
||||
[[nodiscard]] int countHeight() const;
|
||||
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
MTP::Sender _api;
|
||||
|
||||
QString _phone;
|
||||
QString _hash;
|
||||
QString _openUrl;
|
||||
int _codeLength = 0;
|
||||
int _callTimeout = 0;
|
||||
object_ptr<Ui::SentCodeField> _code = { nullptr };
|
||||
object_ptr<Ui::FadeWrap<Ui::FlatLabel>> _error = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _callLabel = { nullptr };
|
||||
object_ptr<Ui::RoundButton> _fragment = { nullptr };
|
||||
mtpRequestId _requestId = 0;
|
||||
Ui::SentCodeCall _call;
|
||||
|
||||
};
|
||||
|
||||
ChangePhone::EnterPhone::EnterPhone(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: _controller(controller)
|
||||
, _api(&controller->session().mtp()) {
|
||||
}
|
||||
|
||||
void ChangePhone::EnterPhone::prepare() {
|
||||
setTitle(tr::lng_change_phone_title());
|
||||
|
||||
const auto phoneValue = QString();
|
||||
_phone.create(
|
||||
this,
|
||||
st::defaultInputField,
|
||||
tr::lng_change_phone_new_title(),
|
||||
Countries::ExtractPhoneCode(_controller->session().user()->phone()),
|
||||
phoneValue,
|
||||
[](const QString &s) { return Countries::Groups(s); });
|
||||
|
||||
_phone->resize(
|
||||
st::boxWidth - 2 * st::boxPadding.left(),
|
||||
_phone->height());
|
||||
_phone->moveToLeft(st::boxPadding.left(), st::boxLittleSkip);
|
||||
connect(_phone, &Ui::PhoneInput::submitted, [=] { submit(); });
|
||||
|
||||
const auto description = object_ptr<Ui::FlatLabel>(
|
||||
this,
|
||||
tr::lng_change_phone_new_description(tr::now),
|
||||
st::changePhoneLabel);
|
||||
description->moveToLeft(
|
||||
st::boxPadding.left(),
|
||||
_phone->y() + _phone->height() + ErrorSkip() + st::boxLittleSkip);
|
||||
|
||||
setDimensions(
|
||||
st::boxWidth,
|
||||
description->bottomNoMargins() + st::boxLittleSkip);
|
||||
|
||||
addButton(tr::lng_change_phone_new_submit(), [this] { submit(); });
|
||||
addButton(tr::lng_cancel(), [this] { closeBox(); });
|
||||
}
|
||||
|
||||
void ChangePhone::EnterPhone::submit() {
|
||||
if (_requestId) {
|
||||
return;
|
||||
}
|
||||
hideError();
|
||||
|
||||
const auto phoneNumber = _phone->getLastText().trimmed();
|
||||
_requestId = _api.request(MTPaccount_SendChangePhoneCode(
|
||||
MTP_string(phoneNumber),
|
||||
MTP_codeSettings(
|
||||
MTP_flags(0),
|
||||
MTPVector<MTPbytes>(),
|
||||
MTPstring(),
|
||||
MTPBool())
|
||||
)).done([=](const MTPauth_SentCode &result) {
|
||||
_requestId = 0;
|
||||
sendPhoneDone(result, phoneNumber);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_requestId = 0;
|
||||
sendPhoneFail(error, phoneNumber);
|
||||
}).handleFloodErrors().send();
|
||||
}
|
||||
|
||||
void ChangePhone::EnterPhone::sendPhoneDone(
|
||||
const MTPauth_SentCode &result,
|
||||
const QString &phoneNumber) {
|
||||
const auto data = result.match([](const MTPDauth_sentCode &data) {
|
||||
return &data;
|
||||
}, [](const MTPDauth_sentCodeSuccess &) -> const MTPDauth_sentCode* {
|
||||
LOG(("API Error: Unexpected auth.sentCodeSuccess "
|
||||
"(ChangePhone::EnterPhone)."));
|
||||
return nullptr;
|
||||
});
|
||||
if (!data) {
|
||||
showError(Lang::Hard::ServerError());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto bad = [&](const char *type) {
|
||||
LOG(("API Error: Should not be '%1'.").arg(type));
|
||||
showError(Lang::Hard::ServerError());
|
||||
return false;
|
||||
};
|
||||
auto codeLength = 0;
|
||||
auto codeByFragmentUrl = QString();
|
||||
const auto hasLength = data->vtype().match([&](
|
||||
const MTPDauth_sentCodeTypeApp &typeData) {
|
||||
LOG(("Error: should not be in-app code!"));
|
||||
showError(Lang::Hard::ServerError());
|
||||
return false;
|
||||
}, [&](const MTPDauth_sentCodeTypeSms &typeData) {
|
||||
codeLength = typeData.vlength().v;
|
||||
return true;
|
||||
}, [&](const MTPDauth_sentCodeTypeFragmentSms &typeData) {
|
||||
codeLength = typeData.vlength().v;
|
||||
codeByFragmentUrl = qs(typeData.vurl());
|
||||
return true;
|
||||
}, [&](const MTPDauth_sentCodeTypeCall &typeData) {
|
||||
codeLength = typeData.vlength().v;
|
||||
return true;
|
||||
}, [&](const MTPDauth_sentCodeTypeFlashCall &) {
|
||||
return bad("FlashCall");
|
||||
}, [&](const MTPDauth_sentCodeTypeMissedCall &) {
|
||||
return bad("MissedCall");
|
||||
}, [&](const MTPDauth_sentCodeTypeFirebaseSms &) {
|
||||
return bad("FirebaseSms");
|
||||
}, [&](const MTPDauth_sentCodeTypeEmailCode &) {
|
||||
return bad("EmailCode");
|
||||
}, [&](const MTPDauth_sentCodeTypeSetUpEmailRequired &) {
|
||||
return bad("SetUpEmailRequired");
|
||||
});
|
||||
if (!hasLength) {
|
||||
return;
|
||||
}
|
||||
const auto phoneCodeHash = qs(data->vphone_code_hash());
|
||||
const auto callTimeout = [&] {
|
||||
if (const auto nextType = data->vnext_type()) {
|
||||
return nextType->match([&](const MTPDauth_sentCodeTypeCall &) {
|
||||
return data->vtimeout().value_or(60);
|
||||
}, [](const auto &) {
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
return 0;
|
||||
}();
|
||||
_controller->show(
|
||||
Box<EnterCode>(
|
||||
_controller,
|
||||
phoneNumber,
|
||||
phoneCodeHash,
|
||||
codeByFragmentUrl,
|
||||
codeLength,
|
||||
callTimeout),
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
void ChangePhone::EnterPhone::sendPhoneFail(
|
||||
const MTP::Error &error,
|
||||
const QString &phoneNumber) {
|
||||
if (MTP::IsFloodError(error)) {
|
||||
showError(tr::lng_flood_error(tr::now));
|
||||
} else if (error.type() == u"PHONE_NUMBER_INVALID"_q) {
|
||||
showError(tr::lng_bad_phone(tr::now));
|
||||
} else if (error.type() == u"PHONE_NUMBER_BANNED"_q) {
|
||||
Ui::ShowPhoneBannedError(&_controller->window(), phoneNumber);
|
||||
} else if (error.type() == u"PHONE_NUMBER_OCCUPIED"_q) {
|
||||
_controller->show(
|
||||
Ui::MakeInformBox(
|
||||
tr::lng_change_phone_occupied(
|
||||
tr::now,
|
||||
lt_phone,
|
||||
Ui::FormatPhone(phoneNumber))),
|
||||
Ui::LayerOption::CloseOther);
|
||||
} else {
|
||||
showError(Lang::Hard::ServerError());
|
||||
}
|
||||
}
|
||||
|
||||
void ChangePhone::EnterPhone::showError(const QString &text) {
|
||||
CreateErrorLabel(
|
||||
this,
|
||||
_error,
|
||||
text,
|
||||
st::boxPadding.left(),
|
||||
_phone->y() + _phone->height() + st::boxLittleSkip);
|
||||
if (!text.isEmpty()) {
|
||||
_phone->showError();
|
||||
}
|
||||
}
|
||||
|
||||
ChangePhone::EnterCode::EnterCode(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller,
|
||||
const QString &phone,
|
||||
const QString &hash,
|
||||
const QString &openUrl,
|
||||
int codeLength,
|
||||
int callTimeout)
|
||||
: _controller(controller)
|
||||
, _api(&controller->session().mtp())
|
||||
, _phone(phone)
|
||||
, _hash(hash)
|
||||
, _openUrl(openUrl)
|
||||
, _codeLength(codeLength)
|
||||
, _callTimeout(callTimeout)
|
||||
, _call([this] { sendCall(); }, [this] { updateCall(); }) {
|
||||
}
|
||||
|
||||
void ChangePhone::EnterCode::prepare() {
|
||||
const auto width = st::boxWidth;
|
||||
setTitle(tr::lng_change_phone_title());
|
||||
|
||||
const auto descriptionText = tr::lng_change_phone_code_description(
|
||||
tr::now,
|
||||
lt_phone,
|
||||
Ui::Text::Bold(Ui::FormatPhone(_phone)),
|
||||
Ui::Text::WithEntities);
|
||||
const auto description = object_ptr<Ui::FlatLabel>(
|
||||
this,
|
||||
rpl::single(descriptionText),
|
||||
st::changePhoneLabel);
|
||||
description->moveToLeft(st::boxPadding.left(), 0);
|
||||
|
||||
const auto submitInput = [=] { submit(_code->getDigitsOnly()); };
|
||||
|
||||
const auto phoneValue = QString();
|
||||
_code.create(
|
||||
this,
|
||||
st::defaultInputField,
|
||||
tr::lng_change_phone_code_title(),
|
||||
phoneValue);
|
||||
_code->setAutoSubmit(_codeLength, submitInput);
|
||||
_code->setChangedCallback([=] { hideError(); });
|
||||
|
||||
_code->resize(width - 2 * st::boxPadding.left(), _code->height());
|
||||
_code->moveToLeft(st::boxPadding.left(), description->bottomNoMargins());
|
||||
connect(_code, &Ui::InputField::submitted, submitInput);
|
||||
|
||||
if (!_openUrl.isEmpty()) {
|
||||
_fragment.create(
|
||||
this,
|
||||
tr::lng_intro_fragment_button(),
|
||||
st::fragmentBoxButton);
|
||||
_fragment->setClickedCallback([=] { File::OpenUrl(_openUrl); });
|
||||
_fragment->setTextTransform(
|
||||
Ui::RoundButton::TextTransform::NoTransform);
|
||||
const auto codeBottom = _code->y() + _code->height();
|
||||
_fragment->setFullWidth(_code->width());
|
||||
_fragment->moveToLeft(
|
||||
(width - _fragment->width()) / 2,
|
||||
codeBottom + ErrorSkip() + st::boxLittleSkip);
|
||||
}
|
||||
|
||||
_controller->session().account().setHandleLoginCode([=](QString code) {
|
||||
submit(code);
|
||||
});
|
||||
boxClosing(
|
||||
) | rpl::start_with_next([controller = _controller] {
|
||||
controller->session().account().setHandleLoginCode(nullptr);
|
||||
}, lifetime());
|
||||
|
||||
setDimensions(width, countHeight());
|
||||
|
||||
if (_callTimeout > 0) {
|
||||
_call.setStatus({ Ui::SentCodeCall::State::Waiting, _callTimeout });
|
||||
updateCall();
|
||||
}
|
||||
|
||||
addButton(tr::lng_change_phone_new_submit(), submitInput);
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
}
|
||||
|
||||
int ChangePhone::EnterCode::countHeight() const {
|
||||
return _code->bottomNoMargins()
|
||||
+ ErrorSkip()
|
||||
+ 3 * st::boxLittleSkip
|
||||
+ (_fragment ? _fragment->height() : 0);
|
||||
}
|
||||
|
||||
void ChangePhone::EnterCode::submit(const QString &code) {
|
||||
if (_requestId) {
|
||||
return;
|
||||
}
|
||||
hideError();
|
||||
|
||||
const auto session = &_controller->session();
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
_requestId = session->api().request(MTPaccount_ChangePhone(
|
||||
MTP_string(_phone),
|
||||
MTP_string(_hash),
|
||||
MTP_string(code)
|
||||
)).done([=, show = Window::Show(_controller)](const MTPUser &result) {
|
||||
_requestId = 0;
|
||||
session->data().processUser(result);
|
||||
if (show.valid()) {
|
||||
if (weak) {
|
||||
show.hideLayer();
|
||||
}
|
||||
Ui::Toast::Show(
|
||||
show.toastParent(),
|
||||
tr::lng_change_phone_success(tr::now));
|
||||
}
|
||||
}).fail(crl::guard(this, [=](const MTP::Error &error) {
|
||||
_requestId = 0;
|
||||
sendCodeFail(error);
|
||||
})).handleFloodErrors().send();
|
||||
}
|
||||
|
||||
void ChangePhone::EnterCode::sendCall() {
|
||||
_api.request(MTPauth_ResendCode(
|
||||
MTP_string(_phone),
|
||||
MTP_string(_hash)
|
||||
)).done([=] {
|
||||
_call.callDone();
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ChangePhone::EnterCode::updateCall() {
|
||||
const auto text = _call.getText();
|
||||
if (text.isEmpty()) {
|
||||
_callLabel.destroy();
|
||||
} else if (!_callLabel) {
|
||||
_callLabel.create(this, text, st::changePhoneLabel);
|
||||
_callLabel->moveToLeft(
|
||||
st::boxPadding.left(),
|
||||
countHeight() - _callLabel->height());
|
||||
_callLabel->show();
|
||||
} else {
|
||||
_callLabel->setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
void ChangePhone::EnterCode::showError(const QString &text) {
|
||||
CreateErrorLabel(
|
||||
this,
|
||||
_error,
|
||||
text,
|
||||
st::boxPadding.left(),
|
||||
_code->y() + _code->height() + st::boxLittleSkip);
|
||||
if (!text.isEmpty()) {
|
||||
_code->showError();
|
||||
}
|
||||
}
|
||||
|
||||
void ChangePhone::EnterCode::sendCodeFail(const MTP::Error &error) {
|
||||
if (MTP::IsFloodError(error)) {
|
||||
showError(tr::lng_flood_error(tr::now));
|
||||
} else if (error.type() == u"PHONE_CODE_EMPTY"_q
|
||||
|| error.type() == u"PHONE_CODE_INVALID"_q) {
|
||||
showError(tr::lng_bad_code(tr::now));
|
||||
} else if (error.type() == u"PHONE_CODE_EXPIRED"_q
|
||||
|| error.type() == u"PHONE_NUMBER_BANNED"_q) {
|
||||
closeBox(); // Go back to phone input.
|
||||
} else if (error.type() == u"PHONE_NUMBER_INVALID"_q) {
|
||||
showError(tr::lng_bad_phone(tr::now));
|
||||
} else {
|
||||
showError(Lang::Hard::ServerError());
|
||||
}
|
||||
}
|
||||
|
||||
ChangePhone::ChangePhone(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: Section(parent)
|
||||
, _controller(controller) {
|
||||
setupContent();
|
||||
}
|
||||
|
||||
rpl::producer<QString> ChangePhone::title() {
|
||||
return Info::Profile::PhoneValue(
|
||||
_controller->session().user()
|
||||
) | rpl::map([](const TextWithEntities &text) {
|
||||
return text.text;
|
||||
});
|
||||
}
|
||||
|
||||
void ChangePhone::setupContent() {
|
||||
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||
|
||||
auto icon = CreateLottieIcon(content, {
|
||||
.name = u"change_number"_q,
|
||||
.sizeOverride = {
|
||||
st::changePhoneIconSize,
|
||||
st::changePhoneIconSize,
|
||||
},
|
||||
}, st::changePhoneIconPadding);
|
||||
content->add(std::move(icon.widget));
|
||||
_animate = std::move(icon.animate);
|
||||
|
||||
content->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
tr::lng_change_phone_button(),
|
||||
st::changePhoneTitle)),
|
||||
st::changePhoneTitlePadding);
|
||||
|
||||
content->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
tr::lng_change_phone_about(Ui::Text::RichLangValue),
|
||||
st::changePhoneDescription)),
|
||||
st::changePhoneDescriptionPadding);
|
||||
|
||||
const auto button = content->add(
|
||||
object_ptr<Ui::CenterWrap<Ui::RoundButton>>(
|
||||
content,
|
||||
object_ptr<Ui::RoundButton>(
|
||||
content,
|
||||
tr::lng_change_phone_button(),
|
||||
st::changePhoneButton)),
|
||||
st::changePhoneButtonPadding)->entity();
|
||||
button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
|
||||
button->setClickedCallback([=] {
|
||||
auto callback = [=] {
|
||||
_controller->show(
|
||||
Box<EnterPhone>(_controller),
|
||||
Ui::LayerOption::CloseOther);
|
||||
};
|
||||
_controller->show(
|
||||
Ui::MakeConfirmBox({
|
||||
.text = tr::lng_change_phone_warning(),
|
||||
.confirmed = std::move(callback),
|
||||
}),
|
||||
Ui::LayerOption::CloseOther);
|
||||
});
|
||||
|
||||
Ui::ResizeFitChild(this, content);
|
||||
}
|
||||
|
||||
void ChangePhone::showFinished() {
|
||||
_animate(anim::repeat::loop);
|
||||
}
|
||||
|
||||
} // namespace Settings
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
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/abstract_box.h"
|
||||
#include "settings/settings_common.h"
|
||||
|
||||
namespace Lottie {
|
||||
class Icon;
|
||||
} // namespace Lottie
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace Settings {
|
||||
|
||||
class ChangePhone : public Section<ChangePhone> {
|
||||
public:
|
||||
ChangePhone(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller);
|
||||
|
||||
void showFinished() override;
|
||||
[[nodiscard]] rpl::producer<QString> title() override;
|
||||
|
||||
private:
|
||||
class EnterPhone;
|
||||
class EnterCode;
|
||||
|
||||
void setupContent();
|
||||
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
Fn<void(anim::repeat)> _animate;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Settings
|
||||
@@ -17,9 +17,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "ui/filter_icons.h"
|
||||
#include "ui/text/text_utilities.h" // Ui::Text::Bold
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_settings.h"
|
||||
#include "styles/style_payments.h" // paymentsSectionButton
|
||||
@@ -74,7 +74,7 @@ void ChangeFilterById(
|
||||
// We can safely show toast there.
|
||||
const auto account = &history->session().account();
|
||||
if (const auto controller = Core::App().windowFor(account)) {
|
||||
auto text = (add
|
||||
controller->showToast((add
|
||||
? tr::lng_filters_toast_add
|
||||
: tr::lng_filters_toast_remove)(
|
||||
tr::now,
|
||||
@@ -82,10 +82,7 @@ void ChangeFilterById(
|
||||
Ui::Text::Bold(chat),
|
||||
lt_folder,
|
||||
Ui::Text::Bold(name),
|
||||
Ui::Text::WithEntities);
|
||||
Ui::Toast::Show(
|
||||
Window::Show(controller).toastParent(),
|
||||
{ .text = std::move(text), .st = &st::defaultToast });
|
||||
Ui::Text::WithEntities));
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
// Revert filter on fail.
|
||||
|
||||
@@ -1265,7 +1265,7 @@ object_ptr<Ui::BoxContent> ProxiesBoxController::CreateOwningBox(
|
||||
|
||||
object_ptr<Ui::BoxContent> ProxiesBoxController::create() {
|
||||
auto result = Box<ProxiesBox>(this, _settings);
|
||||
_show = std::make_shared<Ui::BoxShow>(result.data());
|
||||
_show = result->uiShow();
|
||||
for (const auto &item : _list) {
|
||||
updateView(item);
|
||||
}
|
||||
@@ -1548,9 +1548,7 @@ void ProxiesBoxController::share(const ProxyData &proxy) {
|
||||
+ ((proxy.type == Type::Mtproto && !proxy.password.isEmpty())
|
||||
? "&secret=" + proxy.password : "");
|
||||
QGuiApplication::clipboard()->setText(link);
|
||||
Ui::Toast::Show(
|
||||
_show->toastParent(),
|
||||
tr::lng_username_copied(tr::now));
|
||||
_show->showToast(tr::lng_username_copied(tr::now));
|
||||
}
|
||||
|
||||
ProxiesBoxController::~ProxiesBoxController() {
|
||||
|
||||
@@ -775,7 +775,7 @@ void CreatePollBox::setInnerFocus() {
|
||||
}
|
||||
|
||||
void CreatePollBox::submitFailed(const QString &error) {
|
||||
Ui::Toast::Show(Ui::BoxShow(this).toastParent(), error);
|
||||
showToast(error);
|
||||
}
|
||||
|
||||
not_null<Ui::InputField*> CreatePollBox::setupQuestion(
|
||||
@@ -850,10 +850,7 @@ not_null<Ui::InputField*> CreatePollBox::setupSolution(
|
||||
Core::App().settings().replaceEmojiValue());
|
||||
solution->setMarkdownReplacesEnabled(rpl::single(true));
|
||||
solution->setEditLinkCallback(
|
||||
DefaultEditLinkCallback(
|
||||
std::make_shared<Window::Show>(_controller),
|
||||
session,
|
||||
solution));
|
||||
DefaultEditLinkCallback(_controller->uiShow(), solution));
|
||||
solution->customTab(true);
|
||||
|
||||
const auto warning = CreateWarningLabel(
|
||||
@@ -988,12 +985,10 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
|| (_chosen & PollData::Flag::Quiz));
|
||||
multiple->events(
|
||||
) | rpl::filter([=](not_null<QEvent*> e) {
|
||||
return (e->type() == QEvent::MouseButtonPress) && quiz->checked();
|
||||
}) | rpl::start_with_next([
|
||||
toastParent = Ui::BoxShow(this).toastParent()] {
|
||||
Ui::Toast::Show(
|
||||
toastParent,
|
||||
tr::lng_polls_create_one_answer(tr::now));
|
||||
return (e->type() == QEvent::MouseButtonPress)
|
||||
&& quiz->checked();
|
||||
}) | rpl::start_with_next([show = uiShow()] {
|
||||
show->showToast(tr::lng_polls_create_one_answer(tr::now));
|
||||
}, multiple->lifetime());
|
||||
}
|
||||
|
||||
@@ -1070,10 +1065,9 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
*error &= ~Error::Solution;
|
||||
}
|
||||
};
|
||||
const auto showError = [
|
||||
toastParent = Ui::BoxShow(this).toastParent()](
|
||||
const auto showError = [show = uiShow()](
|
||||
tr::phrase<> text) {
|
||||
Ui::Toast::Show(toastParent, text(tr::now));
|
||||
show->showToast(text(tr::now));
|
||||
};
|
||||
const auto send = [=](Api::SendOptions sendOptions) {
|
||||
collectError();
|
||||
@@ -1099,8 +1093,10 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
HistoryView::PrepareScheduleBox(
|
||||
this,
|
||||
SendMenu::Type::Scheduled,
|
||||
send),
|
||||
Ui::LayerOption::KeepOther);
|
||||
send));
|
||||
};
|
||||
const auto sendWhenOnline = [=] {
|
||||
send(Api::DefaultSendWhenOnlineOptions());
|
||||
};
|
||||
|
||||
options->scrollToWidget(
|
||||
@@ -1130,7 +1126,8 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
submit.data(),
|
||||
sendMenuType,
|
||||
sendSilent,
|
||||
sendScheduled);
|
||||
sendScheduled,
|
||||
sendWhenOnline);
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
|
||||
return result;
|
||||
|
||||
@@ -285,7 +285,7 @@ void DeleteMessagesBox::prepare() {
|
||||
|
||||
if (_wipeHistoryJustClear && _wipeHistoryPeer) {
|
||||
const auto validator = TTLMenu::TTLValidator(
|
||||
std::make_shared<Ui::BoxShow>(this),
|
||||
uiShow(),
|
||||
_wipeHistoryPeer);
|
||||
if (validator.can()) {
|
||||
_wipeHistoryPeer->updateFull();
|
||||
|
||||
@@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "mainwidget.h" // controller->content() -> QWidget*
|
||||
#include "mtproto/mtproto_config.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "storage/localimageloader.h" // SendMediaType
|
||||
@@ -69,7 +70,9 @@ namespace {
|
||||
|
||||
constexpr auto kChangesDebounceTimeout = crl::time(1000);
|
||||
|
||||
auto ListFromMimeData(not_null<const QMimeData*> data, bool premium) {
|
||||
[[nodiscard]] Ui::PreparedList ListFromMimeData(
|
||||
not_null<const QMimeData*> data,
|
||||
bool premium) {
|
||||
using Error = Ui::PreparedList::Error;
|
||||
const auto list = Core::ReadMimeUrls(data);
|
||||
auto result = !list.isEmpty()
|
||||
@@ -89,7 +92,7 @@ auto ListFromMimeData(not_null<const QMimeData*> data, bool premium) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Ui::AlbumType ComputeAlbumType(not_null<HistoryItem*> item) {
|
||||
[[nodiscard]] Ui::AlbumType ComputeAlbumType(not_null<HistoryItem*> item) {
|
||||
if (item->groupId().empty()) {
|
||||
return Ui::AlbumType();
|
||||
}
|
||||
@@ -109,17 +112,130 @@ Ui::AlbumType ComputeAlbumType(not_null<HistoryItem*> item) {
|
||||
return Ui::AlbumType();
|
||||
}
|
||||
|
||||
bool CanBeCompressed(Ui::AlbumType type) {
|
||||
[[nodiscard]] bool CanBeCompressed(Ui::AlbumType type) {
|
||||
return (type == Ui::AlbumType::None)
|
||||
|| (type == Ui::AlbumType::PhotoVideo);
|
||||
}
|
||||
|
||||
void ChooseReplacement(
|
||||
not_null<Window::SessionController*> controller,
|
||||
Ui::AlbumType type,
|
||||
Fn<void(Ui::PreparedList&&)> chosen) {
|
||||
const auto weak = base::make_weak(controller);
|
||||
const auto callback = [=](FileDialog::OpenResult &&result) {
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
const auto showError = [=](tr::phrase<> t) {
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->showToast(t(tr::now));
|
||||
}
|
||||
};
|
||||
|
||||
const auto checkResult = [=](const Ui::PreparedList &list) {
|
||||
if (list.files.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
const auto &file = list.files.front();
|
||||
const auto mime = file.information->filemime;
|
||||
if (Core::IsMimeSticker(mime)) {
|
||||
showError(tr::lng_edit_media_invalid_file);
|
||||
return false;
|
||||
} else if (type != Ui::AlbumType::None
|
||||
&& !file.canBeInAlbumType(type)) {
|
||||
showError(tr::lng_edit_media_album_error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const auto premium = strong->session().premium();
|
||||
auto list = Storage::PreparedFileFromFilesDialog(
|
||||
std::move(result),
|
||||
checkResult,
|
||||
showError,
|
||||
st::sendMediaPreviewSize,
|
||||
premium);
|
||||
|
||||
if (list) {
|
||||
chosen(std::move(*list));
|
||||
}
|
||||
};
|
||||
|
||||
const auto filters = (type == Ui::AlbumType::PhotoVideo)
|
||||
? FileDialog::PhotoVideoFilesFilter()
|
||||
: FileDialog::AllFilesFilter();
|
||||
FileDialog::GetOpenPath(
|
||||
controller->content().get(),
|
||||
tr::lng_choose_file(tr::now),
|
||||
filters,
|
||||
crl::guard(controller, callback));
|
||||
}
|
||||
|
||||
void EditPhotoImage(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::shared_ptr<Data::PhotoMedia> media,
|
||||
bool wasSpoiler,
|
||||
Fn<void(Ui::PreparedList)> done) {
|
||||
const auto large = media
|
||||
? media->image(Data::PhotoSize::Large)
|
||||
: nullptr;
|
||||
const auto parent = controller->content();
|
||||
const auto previewWidth = st::sendMediaPreviewSize;
|
||||
auto callback = [=](const Editor::PhotoModifications &mods) {
|
||||
if (!mods) {
|
||||
return;
|
||||
}
|
||||
const auto large = media->image(Data::PhotoSize::Large);
|
||||
if (!large) {
|
||||
return;
|
||||
}
|
||||
auto copy = large->original();
|
||||
auto list = Storage::PrepareMediaFromImage(
|
||||
std::move(copy),
|
||||
QByteArray(),
|
||||
previewWidth);
|
||||
|
||||
using ImageInfo = Ui::PreparedFileInformation::Image;
|
||||
auto &file = list.files.front();
|
||||
file.spoiler = wasSpoiler;
|
||||
const auto image = std::get_if<ImageInfo>(&file.information->media);
|
||||
|
||||
image->modifications = mods;
|
||||
const auto sideLimit = PhotoSideLimit();
|
||||
Storage::UpdateImageDetails(file, previewWidth, sideLimit);
|
||||
done(std::move(list));
|
||||
};
|
||||
const auto fileImage = std::make_shared<Image>(*large);
|
||||
auto editor = base::make_unique_q<Editor::PhotoEditor>(
|
||||
parent,
|
||||
&controller->window(),
|
||||
fileImage,
|
||||
Editor::PhotoModifications());
|
||||
const auto raw = editor.get();
|
||||
auto layer = std::make_unique<Editor::LayerWidget>(
|
||||
parent,
|
||||
std::move(editor));
|
||||
Editor::InitEditorLayer(layer.get(), raw, std::move(callback));
|
||||
controller->showLayer(std::move(layer), Ui::LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EditCaptionBox::EditCaptionBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<HistoryItem*> item)
|
||||
: EditCaptionBox({}, controller, item, PrepareEditText(item), {}, {}) {
|
||||
}
|
||||
|
||||
EditCaptionBox::EditCaptionBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<HistoryItem*> item,
|
||||
TextWithTags &&text,
|
||||
Ui::PreparedList &&list,
|
||||
Fn<void()> saved)
|
||||
: _controller(controller)
|
||||
, _historyItem(item)
|
||||
, _isAllowedEditMedia(item->media()
|
||||
@@ -135,7 +251,10 @@ EditCaptionBox::EditCaptionBox(
|
||||
tr::lng_photo_caption()))
|
||||
, _emojiToggle(base::make_unique_q<Ui::EmojiButton>(
|
||||
this,
|
||||
st::boxAttachEmoji)) {
|
||||
st::boxAttachEmoji))
|
||||
, _initialText(std::move(text))
|
||||
, _initialList(std::move(list))
|
||||
, _saved(std::move(saved)) {
|
||||
Expects(item->media() != nullptr);
|
||||
Expects(item->media()->allowsEditCaption());
|
||||
|
||||
@@ -148,6 +267,100 @@ EditCaptionBox::EditCaptionBox(
|
||||
|
||||
EditCaptionBox::~EditCaptionBox() = default;
|
||||
|
||||
void EditCaptionBox::StartMediaReplace(
|
||||
not_null<Window::SessionController*> controller,
|
||||
FullMsgId itemId,
|
||||
TextWithTags text,
|
||||
Fn<void()> saved) {
|
||||
const auto session = &controller->session();
|
||||
const auto item = session->data().message(itemId);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
const auto show = [=](Ui::PreparedList &&list) mutable {
|
||||
controller->show(Box<EditCaptionBox>(
|
||||
controller,
|
||||
item,
|
||||
std::move(text),
|
||||
std::move(list),
|
||||
std::move(saved)));
|
||||
};
|
||||
ChooseReplacement(
|
||||
controller,
|
||||
ComputeAlbumType(item),
|
||||
crl::guard(controller, show));
|
||||
}
|
||||
|
||||
void EditCaptionBox::StartMediaReplace(
|
||||
not_null<Window::SessionController*> controller,
|
||||
FullMsgId itemId,
|
||||
Ui::PreparedList &&list,
|
||||
TextWithTags text,
|
||||
Fn<void()> saved) {
|
||||
const auto session = &controller->session();
|
||||
const auto item = session->data().message(itemId);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
const auto type = ComputeAlbumType(item);
|
||||
const auto showError = [=](tr::phrase<> t) {
|
||||
controller->showToast(t(tr::now));
|
||||
};
|
||||
const auto checkResult = [=](const Ui::PreparedList &list) {
|
||||
if (list.files.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
const auto &file = list.files.front();
|
||||
const auto mime = file.information->filemime;
|
||||
if (Core::IsMimeSticker(mime)) {
|
||||
showError(tr::lng_edit_media_invalid_file);
|
||||
return false;
|
||||
} else if (type != Ui::AlbumType::None
|
||||
&& !file.canBeInAlbumType(type)) {
|
||||
showError(tr::lng_edit_media_album_error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (list.error != Ui::PreparedList::Error::None) {
|
||||
showError(tr::lng_send_media_invalid_files);
|
||||
} else if (checkResult(list)) {
|
||||
controller->show(Box<EditCaptionBox>(
|
||||
controller,
|
||||
item,
|
||||
std::move(text),
|
||||
std::move(list),
|
||||
std::move(saved)));
|
||||
}
|
||||
}
|
||||
|
||||
void EditCaptionBox::StartPhotoEdit(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::shared_ptr<Data::PhotoMedia> media,
|
||||
FullMsgId itemId,
|
||||
TextWithTags text,
|
||||
Fn<void()> saved) {
|
||||
const auto session = &controller->session();
|
||||
const auto item = session->data().message(itemId);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
const auto hasSpoiler = item->media() && item->media()->hasSpoiler();
|
||||
EditPhotoImage(controller, media, hasSpoiler, [=](
|
||||
Ui::PreparedList &&list) mutable {
|
||||
const auto item = session->data().message(itemId);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
controller->show(Box<EditCaptionBox>(
|
||||
controller,
|
||||
item,
|
||||
std::move(text),
|
||||
std::move(list),
|
||||
std::move(saved)));
|
||||
});
|
||||
}
|
||||
|
||||
void EditCaptionBox::prepare() {
|
||||
addButton(tr::lng_settings_save(), [=] { save(); });
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
@@ -158,7 +371,9 @@ void EditCaptionBox::prepare() {
|
||||
setupEmojiPanel();
|
||||
setInitialText();
|
||||
|
||||
rebuildPreview();
|
||||
if (!setPreparedList(std::move(_initialList))) {
|
||||
rebuildPreview();
|
||||
}
|
||||
setupEditEventHandler();
|
||||
SetupShadowsToScrollContent(this, _scroll, _contentHeight.events());
|
||||
|
||||
@@ -290,16 +505,15 @@ void EditCaptionBox::setupField() {
|
||||
}
|
||||
|
||||
void EditCaptionBox::setInitialText() {
|
||||
const auto initial = PrepareEditText(_historyItem);
|
||||
_field->setTextWithTags(
|
||||
initial,
|
||||
_initialText,
|
||||
Ui::InputField::HistoryAction::Clear);
|
||||
auto cursor = _field->textCursor();
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
_field->setTextCursor(cursor);
|
||||
|
||||
_checkChangedTimer.setCallback([=] {
|
||||
if (_field->getTextWithAppliedMarkdown() == initial) {
|
||||
if (_field->getTextWithAppliedMarkdown() == _initialText) {
|
||||
setCloseByOutsideClick(true);
|
||||
}
|
||||
});
|
||||
@@ -353,132 +567,44 @@ void EditCaptionBox::setupControls() {
|
||||
}
|
||||
|
||||
void EditCaptionBox::setupEditEventHandler() {
|
||||
const auto toastParent = Ui::BoxShow(this).toastParent();
|
||||
const auto callback = [=](FileDialog::OpenResult &&result) {
|
||||
auto showError = [toastParent](tr::phrase<> t) {
|
||||
Ui::Toast::Show(toastParent, t(tr::now));
|
||||
};
|
||||
|
||||
const auto checkResult = [=](const Ui::PreparedList &list) {
|
||||
if (list.files.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
const auto &file = list.files.front();
|
||||
const auto mime = file.information->filemime;
|
||||
if (Core::IsMimeSticker(mime)) {
|
||||
showError(tr::lng_edit_media_invalid_file);
|
||||
return false;
|
||||
} else if (_albumType != Ui::AlbumType::None
|
||||
&& !file.canBeInAlbumType(_albumType)) {
|
||||
showError(tr::lng_edit_media_album_error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const auto premium = _controller->session().premium();
|
||||
auto list = Storage::PreparedFileFromFilesDialog(
|
||||
std::move(result),
|
||||
checkResult,
|
||||
showError,
|
||||
st::sendMediaPreviewSize,
|
||||
premium);
|
||||
|
||||
if (list) {
|
||||
setPreparedList(std::move(*list));
|
||||
}
|
||||
};
|
||||
|
||||
const auto buttonCallback = [=] {
|
||||
const auto filters = (_albumType == Ui::AlbumType::PhotoVideo)
|
||||
? FileDialog::PhotoVideoFilesFilter()
|
||||
: FileDialog::AllFilesFilter();
|
||||
FileDialog::GetOpenPath(
|
||||
this,
|
||||
tr::lng_choose_file(tr::now),
|
||||
filters,
|
||||
crl::guard(this, callback));
|
||||
};
|
||||
|
||||
_editMediaClicks.events(
|
||||
) | rpl::start_with_next(
|
||||
buttonCallback,
|
||||
lifetime());
|
||||
) | rpl::start_with_next([=] {
|
||||
ChooseReplacement(_controller, _albumType, crl::guard(this, [=](
|
||||
Ui::PreparedList &&list) {
|
||||
setPreparedList(std::move(list));
|
||||
}));
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void EditCaptionBox::setupPhotoEditorEventHandler() {
|
||||
const auto openedOnce = lifetime().make_state<bool>(false);
|
||||
_photoEditorOpens.events(
|
||||
) | rpl::start_with_next([=, controller = _controller] {
|
||||
const auto increment = [=] {
|
||||
if (*openedOnce) {
|
||||
return;
|
||||
}
|
||||
if (_preparedList.files.empty()
|
||||
&& (!_photoMedia
|
||||
|| !_photoMedia->image(Data::PhotoSize::Large))) {
|
||||
return;
|
||||
} else if (!*openedOnce) {
|
||||
*openedOnce = true;
|
||||
controller->session().settings().incrementPhotoEditorHintShown();
|
||||
controller->session().saveSettings();
|
||||
};
|
||||
const auto clearError = [=] {
|
||||
}
|
||||
if (!_error.isEmpty()) {
|
||||
_error = QString();
|
||||
update();
|
||||
};
|
||||
const auto previewWidth = st::sendMediaPreviewSize;
|
||||
}
|
||||
if (!_preparedList.files.empty()) {
|
||||
increment();
|
||||
clearError();
|
||||
Editor::OpenWithPreparedFile(
|
||||
this,
|
||||
controller,
|
||||
&_preparedList.files.front(),
|
||||
previewWidth,
|
||||
st::sendMediaPreviewSize,
|
||||
[=] { rebuildPreview(); });
|
||||
} else if (_photoMedia) {
|
||||
const auto large = _photoMedia->image(Data::PhotoSize::Large);
|
||||
if (!large) {
|
||||
return;
|
||||
}
|
||||
increment();
|
||||
clearError();
|
||||
auto callback = [=](const Editor::PhotoModifications &mods) {
|
||||
if (!mods || !_photoMedia) {
|
||||
return;
|
||||
}
|
||||
const auto large = _photoMedia->image(Data::PhotoSize::Large);
|
||||
if (!large) {
|
||||
return;
|
||||
}
|
||||
auto copy = large->original();
|
||||
const auto wasSpoiler = hasSpoiler();
|
||||
|
||||
_preparedList = Storage::PrepareMediaFromImage(
|
||||
std::move(copy),
|
||||
QByteArray(),
|
||||
previewWidth);
|
||||
|
||||
using ImageInfo = Ui::PreparedFileInformation::Image;
|
||||
auto &file = _preparedList.files.front();
|
||||
file.spoiler = wasSpoiler;
|
||||
const auto image = std::get_if<ImageInfo>(
|
||||
&file.information->media);
|
||||
|
||||
image->modifications = mods;
|
||||
const auto sideLimit = PhotoSideLimit();
|
||||
Storage::UpdateImageDetails(file, previewWidth, sideLimit);
|
||||
rebuildPreview();
|
||||
};
|
||||
const auto fileImage = std::make_shared<Image>(*large);
|
||||
auto editor = base::make_unique_q<Editor::PhotoEditor>(
|
||||
this,
|
||||
&controller->window(),
|
||||
fileImage,
|
||||
Editor::PhotoModifications());
|
||||
const auto raw = editor.get();
|
||||
auto layer = std::make_unique<Editor::LayerWidget>(
|
||||
this,
|
||||
std::move(editor));
|
||||
Editor::InitEditorLayer(layer.get(), raw, std::move(callback));
|
||||
controller->showLayer(
|
||||
std::move(layer),
|
||||
Ui::LayerOption::KeepOther);
|
||||
} else {
|
||||
EditPhotoImage(_controller, _photoMedia, hasSpoiler(), [=](
|
||||
Ui::PreparedList &&list) {
|
||||
setPreparedList(std::move(list));
|
||||
});
|
||||
}
|
||||
}, lifetime());
|
||||
}
|
||||
@@ -521,7 +647,7 @@ void EditCaptionBox::setupEmojiPanel() {
|
||||
_controller,
|
||||
object_ptr<Selector>(
|
||||
nullptr,
|
||||
_controller,
|
||||
_controller->uiShow(),
|
||||
Window::GifPauseReason::Layer,
|
||||
Selector::Mode::EmojiOnly));
|
||||
_emojiPanel->setDesiredHeightValues(
|
||||
@@ -602,9 +728,7 @@ bool EditCaptionBox::setPreparedList(Ui::PreparedList &&list) {
|
||||
}
|
||||
}
|
||||
if (invalidForAlbum) {
|
||||
Ui::Toast::Show(
|
||||
Ui::BoxShow(this).toastParent(),
|
||||
tr::lng_edit_media_album_error(tr::now));
|
||||
showToast(tr::lng_edit_media_album_error(tr::now));
|
||||
return false;
|
||||
}
|
||||
const auto wasSpoiler = hasSpoiler();
|
||||
@@ -780,13 +904,13 @@ void EditCaptionBox::save() {
|
||||
: SendMediaType::File,
|
||||
_field->getTextWithAppliedMarkdown(),
|
||||
action);
|
||||
closeBox();
|
||||
closeAfterSave();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto done = crl::guard(this, [=] {
|
||||
_saveRequestId = 0;
|
||||
closeBox();
|
||||
closeAfterSave();
|
||||
});
|
||||
|
||||
const auto fail = crl::guard(this, [=](const QString &error) {
|
||||
@@ -795,7 +919,7 @@ void EditCaptionBox::save() {
|
||||
_error = tr::lng_edit_error(tr::now);
|
||||
update();
|
||||
} else if (error == u"MESSAGE_NOT_MODIFIED"_q) {
|
||||
closeBox();
|
||||
closeAfterSave();
|
||||
} else if (error == u"MESSAGE_EMPTY"_q) {
|
||||
_field->setFocus();
|
||||
_field->showError();
|
||||
@@ -816,6 +940,16 @@ void EditCaptionBox::save() {
|
||||
_saveRequestId = Api::EditCaption(item, sending, options, done, fail);
|
||||
}
|
||||
|
||||
void EditCaptionBox::closeAfterSave() {
|
||||
const auto weak = MakeWeak(this);
|
||||
if (_saved) {
|
||||
_saved();
|
||||
}
|
||||
if (weak) {
|
||||
closeBox();
|
||||
}
|
||||
}
|
||||
|
||||
void EditCaptionBox::keyPressEvent(QKeyEvent *e) {
|
||||
const auto ctrl = e->modifiers().testFlag(Qt::ControlModifier);
|
||||
if ((e->key() == Qt::Key_E) && ctrl) {
|
||||
|
||||
@@ -36,8 +36,33 @@ public:
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<HistoryItem*> item);
|
||||
EditCaptionBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<HistoryItem*> item,
|
||||
TextWithTags &&text,
|
||||
Ui::PreparedList &&list,
|
||||
Fn<void()> saved);
|
||||
~EditCaptionBox();
|
||||
|
||||
static void StartMediaReplace(
|
||||
not_null<Window::SessionController*> controller,
|
||||
FullMsgId itemId,
|
||||
TextWithTags text,
|
||||
Fn<void()> saved);
|
||||
static void StartMediaReplace(
|
||||
not_null<Window::SessionController*> controller,
|
||||
FullMsgId itemId,
|
||||
Ui::PreparedList &&list,
|
||||
TextWithTags text,
|
||||
Fn<void()> saved);
|
||||
static void StartPhotoEdit(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::shared_ptr<Data::PhotoMedia> media,
|
||||
FullMsgId itemId,
|
||||
TextWithTags text,
|
||||
Fn<void()> saved);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
void setInnerFocus() override;
|
||||
@@ -66,6 +91,7 @@ private:
|
||||
bool validateLength(const QString &text) const;
|
||||
void applyChanges();
|
||||
void save();
|
||||
void closeAfterSave();
|
||||
|
||||
bool fileFromClipboard(not_null<const QMimeData*> data);
|
||||
|
||||
@@ -89,6 +115,10 @@ private:
|
||||
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;
|
||||
base::unique_qptr<QObject> _emojiFilter;
|
||||
|
||||
const TextWithTags _initialText;
|
||||
Ui::PreparedList _initialList;
|
||||
Fn<void()> _saved;
|
||||
|
||||
std::shared_ptr<Data::PhotoMedia> _photoMedia;
|
||||
|
||||
Ui::PreparedList _preparedList;
|
||||
|
||||
@@ -167,8 +167,7 @@ void EditPrivacyBox::editExceptions(
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
};
|
||||
_window->show(
|
||||
Box<PeerListBox>(std::move(controller), std::move(initBox)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
Box<PeerListBox>(std::move(controller), std::move(initBox)));
|
||||
}
|
||||
|
||||
std::vector<not_null<PeerData*>> &EditPrivacyBox::exceptions(Exception exception) {
|
||||
@@ -217,16 +216,18 @@ Ui::FlatLabel *EditPrivacyBox::addLabel(
|
||||
if (!text) {
|
||||
return nullptr;
|
||||
}
|
||||
return container->add(
|
||||
auto label = object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
rpl::duplicate(text),
|
||||
st::boxDividerLabel);
|
||||
const auto result = label.data();
|
||||
container->add(
|
||||
object_ptr<Ui::DividerLabel>(
|
||||
container,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
rpl::duplicate(text),
|
||||
st::boxDividerLabel),
|
||||
std::move(label),
|
||||
st::settingsDividerLabelPadding),
|
||||
{ 0, topSkip, 0, 0 }
|
||||
)->entity();
|
||||
{ 0, topSkip, 0, 0 });
|
||||
return result;
|
||||
}
|
||||
|
||||
Ui::FlatLabel *EditPrivacyBox::addLabelOrDivider(
|
||||
|
||||
@@ -8,20 +8,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/filters/edit_filter_box.h"
|
||||
|
||||
#include "boxes/filters/edit_filter_chats_list.h"
|
||||
#include "boxes/filters/edit_filter_links.h"
|
||||
#include "boxes/premium_limits_box.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/effects/panel_animation.h"
|
||||
#include "ui/filter_icons.h"
|
||||
#include "ui/filter_icon_panel.h"
|
||||
#include "ui/painter.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_peer_values.h" // Data::AmPremiumValue.
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "settings/settings_common.h"
|
||||
@@ -37,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -380,10 +386,7 @@ void EditExceptions(
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
};
|
||||
window->window().show(
|
||||
Box<PeerListBox>(
|
||||
std::move(controller),
|
||||
std::move(initBox)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
Box<PeerListBox>(std::move(controller), std::move(initBox)));
|
||||
}
|
||||
|
||||
void CreateIconSelector(
|
||||
@@ -499,6 +502,24 @@ void CreateIconSelector(
|
||||
return QString();
|
||||
}
|
||||
|
||||
not_null<Ui::SettingsButton*> AddToggledButton(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<bool> shown,
|
||||
rpl::producer<QString> text,
|
||||
const style::SettingsButton &st,
|
||||
IconDescriptor &&descriptor) {
|
||||
const auto toggled = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
|
||||
container,
|
||||
CreateButton(
|
||||
container,
|
||||
std::move(text),
|
||||
st,
|
||||
std::move(descriptor)))
|
||||
)->toggleOn(std::move(shown), anim::type::instant)->setDuration(0);
|
||||
return toggled->entity();
|
||||
}
|
||||
|
||||
[[nodiscard]] QString TrimDefaultTitle(const QString &title) {
|
||||
return (title.size() <= kMaxFilterTitleLength) ? title : QString();
|
||||
}
|
||||
@@ -509,10 +530,58 @@ void EditFilterBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> window,
|
||||
const Data::ChatFilter &filter,
|
||||
Fn<void(const Data::ChatFilter &)> doneCallback) {
|
||||
const auto creating = filter.title().isEmpty();
|
||||
Fn<void(const Data::ChatFilter &)> doneCallback,
|
||||
Fn<void(
|
||||
const Data::ChatFilter &data,
|
||||
Fn<void(Data::ChatFilter)> next)> saveAnd) {
|
||||
using namespace rpl::mappers;
|
||||
|
||||
struct State {
|
||||
rpl::variable<Data::ChatFilter> rules;
|
||||
rpl::variable<std::vector<Data::ChatFilterLink>> links;
|
||||
rpl::variable<bool> hasLinks;
|
||||
rpl::variable<bool> chatlist;
|
||||
rpl::variable<bool> creating;
|
||||
};
|
||||
const auto owner = &window->session().data();
|
||||
const auto state = box->lifetime().make_state<State>(State{
|
||||
.rules = filter,
|
||||
.chatlist = filter.chatlist(),
|
||||
.creating = filter.title().isEmpty(),
|
||||
});
|
||||
state->links = owner->chatsFilters().chatlistLinks(filter.id()),
|
||||
state->hasLinks = state->links.value() | rpl::map([=](const auto &v) {
|
||||
return !v.empty();
|
||||
});
|
||||
state->hasLinks.value() | rpl::filter(
|
||||
_1
|
||||
) | rpl::start_with_next([=] {
|
||||
state->chatlist = true;
|
||||
}, box->lifetime());
|
||||
|
||||
const auto data = &state->rules;
|
||||
|
||||
owner->chatsFilters().isChatlistChanged(
|
||||
) | rpl::filter([=](FilterId id) {
|
||||
return (id == data->current().id());
|
||||
}) | rpl::start_with_next([=](FilterId id) {
|
||||
const auto filters = &owner->chatsFilters();
|
||||
const auto &list = filters->list();
|
||||
const auto i = ranges::find(list, id, &Data::ChatFilter::id);
|
||||
if (i == end(list)) {
|
||||
return;
|
||||
}
|
||||
*data = data->current().withChatlist(i->chatlist(), i->hasMyLinks());
|
||||
if (!i->chatlist() && !state->hasLinks.current()) {
|
||||
state->chatlist = false;
|
||||
}
|
||||
}, box->lifetime());
|
||||
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->setTitle(creating ? tr::lng_filters_new() : tr::lng_filters_edit());
|
||||
box->setTitle(rpl::conditional(
|
||||
state->creating.value(),
|
||||
tr::lng_filters_new(),
|
||||
tr::lng_filters_edit()));
|
||||
box->setCloseByOutsideClick(false);
|
||||
|
||||
Data::AmPremiumValue(
|
||||
@@ -521,9 +590,6 @@ void EditFilterBox(
|
||||
box->closeBox();
|
||||
}, box->lifetime());
|
||||
|
||||
using State = rpl::variable<Data::ChatFilter>;
|
||||
const auto data = box->lifetime().make_state<State>(filter);
|
||||
|
||||
const auto content = box->verticalLayout();
|
||||
const auto name = content->add(
|
||||
object_ptr<Ui::InputField>(
|
||||
@@ -543,7 +609,12 @@ void EditFilterBox(
|
||||
|
||||
const auto nameEditing = box->lifetime().make_state<NameEditing>(
|
||||
NameEditing{ name });
|
||||
nameEditing->custom = !creating;
|
||||
|
||||
state->creating.value(
|
||||
) | rpl::filter(!_1) | rpl::start_with_next([=] {
|
||||
nameEditing->custom = true;
|
||||
}, box->lifetime());
|
||||
|
||||
QObject::connect(name, &Ui::InputField::changed, [=] {
|
||||
if (!nameEditing->settingDefault) {
|
||||
nameEditing->custom = true;
|
||||
@@ -604,24 +675,141 @@ void EditFilterBox(
|
||||
AddDividerText(content, tr::lng_filters_include_about());
|
||||
AddSkip(content);
|
||||
|
||||
AddSubsectionTitle(content, tr::lng_filters_exclude());
|
||||
auto excludeWrap = content->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
content,
|
||||
object_ptr<Ui::VerticalLayout>(content))
|
||||
)->setDuration(0);
|
||||
excludeWrap->toggleOn(state->chatlist.value() | rpl::map(!_1));
|
||||
const auto excludeInner = excludeWrap->entity();
|
||||
|
||||
AddSubsectionTitle(excludeInner, tr::lng_filters_exclude());
|
||||
|
||||
const auto excludeAdd = AddButton(
|
||||
content,
|
||||
excludeInner,
|
||||
tr::lng_filters_remove_chats(),
|
||||
st::settingsButtonActive,
|
||||
{ &st::settingsIconRemove, 0, IconType::Round, &st::windowBgActive });
|
||||
|
||||
const auto exclude = SetupChatsPreview(
|
||||
content,
|
||||
excludeInner,
|
||||
data,
|
||||
updateDefaultTitle,
|
||||
kExcludeTypes,
|
||||
&Data::ChatFilter::never);
|
||||
|
||||
AddSkip(content);
|
||||
AddDividerText(content, tr::lng_filters_exclude_about());
|
||||
AddSkip(excludeInner);
|
||||
AddDividerText(excludeInner, tr::lng_filters_exclude_about());
|
||||
AddSkip(excludeInner);
|
||||
|
||||
const auto collect = [=]() -> std::optional<Data::ChatFilter> {
|
||||
const auto title = name->getLastText().trimmed();
|
||||
const auto rules = data->current();
|
||||
if (title.isEmpty()) {
|
||||
name->showError();
|
||||
box->scrollToY(0);
|
||||
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 {};
|
||||
}
|
||||
return rules.withTitle(title);
|
||||
};
|
||||
|
||||
AddSubsectionTitle(
|
||||
content,
|
||||
rpl::conditional(
|
||||
state->hasLinks.value(),
|
||||
tr::lng_filters_link_has(),
|
||||
tr::lng_filters_link()));
|
||||
|
||||
state->hasLinks.changes() | rpl::start_with_next([=] {
|
||||
content->resizeToWidth(content->widthNoMargins());
|
||||
}, content->lifetime());
|
||||
|
||||
if (filter.chatlist()) {
|
||||
window->session().data().chatsFilters().reloadChatlistLinks(
|
||||
filter.id());
|
||||
}
|
||||
|
||||
const auto createLink = AddToggledButton(
|
||||
content,
|
||||
state->hasLinks.value() | rpl::map(!rpl::mappers::_1),
|
||||
tr::lng_filters_link_create(),
|
||||
st::settingsButtonActive,
|
||||
{ &st::settingsFolderShareIcon, 0, IconType::Simple });
|
||||
const auto addLink = AddToggledButton(
|
||||
content,
|
||||
state->hasLinks.value(),
|
||||
tr::lng_group_invite_add(),
|
||||
st::settingsButtonActive,
|
||||
{ &st::settingsIconAdd, 0, IconType::Round, &st::windowBgActive });
|
||||
|
||||
SetupFilterLinks(
|
||||
content,
|
||||
window,
|
||||
state->links.value(),
|
||||
[=] { return collect().value_or(Data::ChatFilter()); });
|
||||
|
||||
rpl::merge(
|
||||
createLink->clicks(),
|
||||
addLink->clicks()
|
||||
) | rpl::filter(
|
||||
(rpl::mappers::_1 == Qt::LeftButton)
|
||||
) | rpl::start_with_next([=](Qt::MouseButton button) {
|
||||
const auto result = collect();
|
||||
if (!result || !GoodForExportFilterLink(window, *result)) {
|
||||
return;
|
||||
}
|
||||
const auto shared = CollectFilterLinkChats(*result);
|
||||
if (shared.empty()) {
|
||||
window->show(ShowLinkBox(window, *result, {}));
|
||||
return;
|
||||
}
|
||||
saveAnd(*result, crl::guard(box, [=](Data::ChatFilter updated) {
|
||||
state->creating = false;
|
||||
|
||||
// Comparison of ChatFilter-s don't take id into account!
|
||||
data->force_assign(updated);
|
||||
const auto id = updated.id();
|
||||
state->links = owner->chatsFilters().chatlistLinks(id);
|
||||
ExportFilterLink(id, shared, crl::guard(box, [=](
|
||||
Data::ChatFilterLink link) {
|
||||
Expects(link.id == id);
|
||||
|
||||
*data = data->current().withChatlist(true, true);
|
||||
window->show(ShowLinkBox(window, updated, link));
|
||||
}), crl::guard(box, [=](QString error) {
|
||||
const auto session = &window->session();
|
||||
if (error == u"CHATLISTS_TOO_MUCH"_q) {
|
||||
window->show(Box(ShareableFiltersLimitBox, session));
|
||||
} else if (error == u"INVITES_TOO_MUCH"_q) {
|
||||
window->show(Box(FilterLinksLimitBox, session));
|
||||
} else if (error == u"CHANNELS_TOO_MUCH"_q) {
|
||||
window->show(Box(ChannelsLimitBox, session));
|
||||
} else if (error == u"USER_CHANNELS_TOO_MUCH"_q) {
|
||||
window->showToast(
|
||||
{ tr::lng_filters_link_group_admin_error(tr::now) });
|
||||
} else {
|
||||
window->show(ShowLinkBox(window, updated, { .id = id }));
|
||||
}
|
||||
}));
|
||||
}));
|
||||
}, createLink->lifetime());
|
||||
AddSkip(content);
|
||||
AddDividerText(
|
||||
content,
|
||||
rpl::conditional(
|
||||
state->hasLinks.value(),
|
||||
tr::lng_filters_link_about_many(),
|
||||
tr::lng_filters_link_about()));
|
||||
|
||||
const auto show = box->uiShow();
|
||||
const auto refreshPreviews = [=] {
|
||||
include->updateData(
|
||||
data->current().flags() & kTypes,
|
||||
@@ -634,7 +822,7 @@ void EditFilterBox(
|
||||
EditExceptions(
|
||||
window,
|
||||
box,
|
||||
kTypes,
|
||||
kTypes | (state->chatlist.current() ? Flag::Chatlist : Flag()),
|
||||
data,
|
||||
updateDefaultTitle,
|
||||
refreshPreviews);
|
||||
@@ -650,35 +838,17 @@ void EditFilterBox(
|
||||
});
|
||||
|
||||
const auto save = [=] {
|
||||
const auto title = name->getLastText().trimmed();
|
||||
const auto rules = data->current();
|
||||
const auto result = Data::ChatFilter(
|
||||
rules.id(),
|
||||
title,
|
||||
rules.iconEmoji(),
|
||||
rules.flags(),
|
||||
rules.always(),
|
||||
rules.pinned(),
|
||||
rules.never());
|
||||
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;
|
||||
if (const auto result = collect()) {
|
||||
box->closeBox();
|
||||
doneCallback(*result);
|
||||
}
|
||||
box->closeBox();
|
||||
|
||||
doneCallback(result);
|
||||
};
|
||||
box->addButton(
|
||||
creating ? tr::lng_filters_create_button() : tr::lng_settings_save(),
|
||||
save);
|
||||
|
||||
box->addButton(rpl::conditional(
|
||||
state->creating.value(),
|
||||
tr::lng_filters_create_button(),
|
||||
tr::lng_settings_save()
|
||||
), save);
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}
|
||||
|
||||
@@ -707,9 +877,16 @@ void EditExistingFilter(
|
||||
tl
|
||||
)).send();
|
||||
};
|
||||
const auto saveAnd = [=](
|
||||
const Data::ChatFilter &data,
|
||||
Fn<void(Data::ChatFilter)> next) {
|
||||
doneCallback(data);
|
||||
next(data);
|
||||
};
|
||||
window->window().show(Box(
|
||||
EditFilterBox,
|
||||
window,
|
||||
*i,
|
||||
crl::guard(session, doneCallback)));
|
||||
crl::guard(session, doneCallback),
|
||||
crl::guard(session, saveAnd)));
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ void EditFilterBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> window,
|
||||
const Data::ChatFilter &filter,
|
||||
Fn<void(const Data::ChatFilter &)> doneCallback);
|
||||
Fn<void(const Data::ChatFilter &)> doneCallback,
|
||||
Fn<void(const Data::ChatFilter &, Fn<void(Data::ChatFilter)>)> saveAnd);
|
||||
|
||||
void EditExistingFilter(
|
||||
not_null<Window::SessionController*> window,
|
||||
|
||||
@@ -343,9 +343,10 @@ EditFilterChatsListController::EditFilterChatsListController(
|
||||
, _session(session)
|
||||
, _title(std::move(title))
|
||||
, _peers(peers)
|
||||
, _options(options)
|
||||
, _options(options & ~Flag::Chatlist)
|
||||
, _selected(selected)
|
||||
, _limit(Limit(session)) {
|
||||
, _limit(Limit(session))
|
||||
, _chatlist(options & Flag::Chatlist) {
|
||||
}
|
||||
|
||||
Main::Session &EditFilterChatsListController::session() const {
|
||||
@@ -353,8 +354,11 @@ Main::Session &EditFilterChatsListController::session() const {
|
||||
}
|
||||
|
||||
int EditFilterChatsListController::selectedTypesCount() const {
|
||||
Expects(_typesDelegate != nullptr);
|
||||
Expects(_chatlist || _typesDelegate != nullptr);
|
||||
|
||||
if (_chatlist) {
|
||||
return 0;
|
||||
}
|
||||
auto result = 0;
|
||||
for (auto i = 0; i != _typesDelegate->peerListFullRowsCount(); ++i) {
|
||||
if (_typesDelegate->peerListRowAt(i)->checked()) {
|
||||
@@ -396,7 +400,9 @@ bool EditFilterChatsListController::handleDeselectForeignRow(
|
||||
|
||||
void EditFilterChatsListController::prepareViewHook() {
|
||||
delegate()->peerListSetTitle(std::move(_title));
|
||||
delegate()->peerListSetAboveWidget(prepareTypesList());
|
||||
if (!_chatlist) {
|
||||
delegate()->peerListSetAboveWidget(prepareTypesList());
|
||||
}
|
||||
|
||||
const auto count = int(_peers.size());
|
||||
const auto rows = std::make_unique<std::optional<ExceptionRow>[]>(count);
|
||||
|
||||
@@ -75,6 +75,7 @@ private:
|
||||
Flags _options;
|
||||
Flags _selected;
|
||||
int _limit = 0;
|
||||
bool _chatlist = false;
|
||||
|
||||
Fn<void(PeerListRowId)> _deselectOption;
|
||||
|
||||
|
||||
1202
Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp
Normal file
63
Telegram/SourceFiles/boxes/filters/edit_filter_links.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/object_ptr.h"
|
||||
|
||||
class PeerListRow;
|
||||
|
||||
namespace Ui {
|
||||
class Show;
|
||||
class BoxContent;
|
||||
class VerticalLayout;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Data {
|
||||
class ChatFilter;
|
||||
struct ChatFilterLink;
|
||||
} // namespace Data
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
[[nodiscard]] std::vector<not_null<PeerData*>> CollectFilterLinkChats(
|
||||
const Data::ChatFilter &filter);
|
||||
[[nodiscard]] bool GoodForExportFilterLink(
|
||||
not_null<Window::SessionController*> window,
|
||||
const Data::ChatFilter &filter);
|
||||
|
||||
void ExportFilterLink(
|
||||
FilterId id,
|
||||
const std::vector<not_null<PeerData*>> &peers,
|
||||
Fn<void(Data::ChatFilterLink)> done,
|
||||
Fn<void(QString)> fail);
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::BoxContent> ShowLinkBox(
|
||||
not_null<Window::SessionController*> window,
|
||||
const Data::ChatFilter &filter,
|
||||
const Data::ChatFilterLink &link);
|
||||
[[nodiscard]] QString FilterChatStatusText(not_null<PeerData*> peer);
|
||||
|
||||
void SetupFilterLinks(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<Window::SessionController*> window,
|
||||
rpl::producer<std::vector<Data::ChatFilterLink>> value,
|
||||
Fn<Data::ChatFilter()> currentFilter);
|
||||
|
||||
void AddFilterSubtitleWithToggles(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<QString> text,
|
||||
int selectableCount,
|
||||
rpl::producer<int> selectedCount,
|
||||
Fn<void(bool select)> toggle);
|
||||
|
||||
[[nodiscard]] std::unique_ptr<PeerListRow> MakeFilterChatRow(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &status,
|
||||
bool disabled);
|
||||
@@ -1248,7 +1248,7 @@ void LanguageBox::setupTop(not_null<Ui::VerticalLayout*> container) {
|
||||
st::settingsButtonNoIcon);
|
||||
|
||||
translateSkip->setClickedCallback([=] {
|
||||
Ui::BoxShow(this).showBox(Ui::EditSkipTranslationLanguages());
|
||||
uiShow()->showBox(Ui::EditSkipTranslationLanguages());
|
||||
});
|
||||
Settings::AddSkip(container);
|
||||
Settings::AddDividerText(
|
||||
|
||||
@@ -91,9 +91,7 @@ void MaxInviteBox::mousePressEvent(QMouseEvent *e) {
|
||||
if (_linkOver) {
|
||||
if (!_channel->inviteLink().isEmpty()) {
|
||||
QGuiApplication::clipboard()->setText(_channel->inviteLink());
|
||||
Ui::Toast::Show(
|
||||
Ui::BoxShow(this).toastParent(),
|
||||
tr::lng_create_channel_link_copied(tr::now));
|
||||
showToast(tr::lng_create_channel_link_copied(tr::now));
|
||||
} else if (_channel->isFullLoaded() && !_creatingInviteLink) {
|
||||
_creatingInviteLink = true;
|
||||
_channel->session().api().inviteLinks().create(_channel);
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/peer_list_box.h"
|
||||
|
||||
#include "main/session/session_show.h"
|
||||
#include "main/main_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "ui/widgets/multi_select.h"
|
||||
@@ -74,7 +75,7 @@ PaintRoundImageCallback ForceRoundUserpicCallback(not_null<PeerData*> peer) {
|
||||
}
|
||||
|
||||
PeerListContentDelegateShow::PeerListContentDelegateShow(
|
||||
std::shared_ptr<Ui::Show> show)
|
||||
std::shared_ptr<Main::SessionShow> show)
|
||||
: _show(show) {
|
||||
}
|
||||
|
||||
@@ -88,15 +89,16 @@ void PeerListContentDelegateShow::peerListHideLayer() {
|
||||
_show->hideLayer();
|
||||
}
|
||||
|
||||
not_null<QWidget*> PeerListContentDelegateShow::peerListToastParent() {
|
||||
return _show->toastParent();
|
||||
auto PeerListContentDelegateShow::peerListUiShow()
|
||||
-> std::shared_ptr<Main::SessionShow>{
|
||||
return _show;
|
||||
}
|
||||
|
||||
PeerListBox::PeerListBox(
|
||||
QWidget*,
|
||||
std::unique_ptr<PeerListController> controller,
|
||||
Fn<void(not_null<PeerListBox*>)> init)
|
||||
: _show(this)
|
||||
: _show(Main::MakeSessionShow(uiShow(), &controller->session()))
|
||||
, _controller(std::move(controller))
|
||||
, _init(std::move(init)) {
|
||||
Expects(_controller != nullptr);
|
||||
@@ -140,9 +142,14 @@ void PeerListBox::createMultiSelect() {
|
||||
|
||||
void PeerListBox::setAddedTopScrollSkip(int skip) {
|
||||
_addedTopScrollSkip = skip;
|
||||
_scrollBottomFixed = false;
|
||||
updateScrollSkips();
|
||||
}
|
||||
|
||||
void PeerListBox::showFinished() {
|
||||
_controller->showFinished();
|
||||
}
|
||||
|
||||
int PeerListBox::getTopScrollSkip() const {
|
||||
auto result = _addedTopScrollSkip;
|
||||
if (_select && !_select->isHidden()) {
|
||||
@@ -306,18 +313,20 @@ void PeerListBox::peerListSetSearchMode(PeerListSearchMode mode) {
|
||||
void PeerListBox::peerListShowBox(
|
||||
object_ptr<Ui::BoxContent> content,
|
||||
Ui::LayerOptions options) {
|
||||
_show.showBox(std::move(content), options);
|
||||
_show->showBox(std::move(content), options);
|
||||
}
|
||||
|
||||
void PeerListBox::peerListHideLayer() {
|
||||
_show.hideLayer();
|
||||
_show->hideLayer();
|
||||
}
|
||||
|
||||
not_null<QWidget*> PeerListBox::peerListToastParent() {
|
||||
return _show.toastParent();
|
||||
std::shared_ptr<Main::SessionShow> PeerListBox::peerListUiShow() {
|
||||
return _show;
|
||||
}
|
||||
|
||||
PeerListController::PeerListController(std::unique_ptr<PeerListSearchController> searchController) : _searchController(std::move(searchController)) {
|
||||
PeerListController::PeerListController(
|
||||
std::unique_ptr<PeerListSearchController> searchController)
|
||||
: _searchController(std::move(searchController)) {
|
||||
if (_searchController) {
|
||||
_searchController->setDelegate(this);
|
||||
}
|
||||
@@ -1216,18 +1225,14 @@ void PeerListContent::setSearchNoResults(object_ptr<Ui::FlatLabel> noResults) {
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListContent::setAboveWidget(object_ptr<TWidget> widget) {
|
||||
void PeerListContent::setAboveWidget(object_ptr<Ui::RpWidget> widget) {
|
||||
_aboveWidget = std::move(widget);
|
||||
if (_aboveWidget) {
|
||||
_aboveWidget->setParent(this);
|
||||
}
|
||||
initDecorateWidget(_aboveWidget.data());
|
||||
}
|
||||
|
||||
void PeerListContent::setAboveSearchWidget(object_ptr<TWidget> widget) {
|
||||
void PeerListContent::setAboveSearchWidget(object_ptr<Ui::RpWidget> widget) {
|
||||
_aboveSearchWidget = std::move(widget);
|
||||
if (_aboveSearchWidget) {
|
||||
_aboveSearchWidget->setParent(this);
|
||||
}
|
||||
initDecorateWidget(_aboveSearchWidget.data());
|
||||
}
|
||||
|
||||
void PeerListContent::setHideEmpty(bool hide) {
|
||||
@@ -1235,10 +1240,20 @@ void PeerListContent::setHideEmpty(bool hide) {
|
||||
resizeToWidth(width());
|
||||
}
|
||||
|
||||
void PeerListContent::setBelowWidget(object_ptr<TWidget> widget) {
|
||||
void PeerListContent::setBelowWidget(object_ptr<Ui::RpWidget> widget) {
|
||||
_belowWidget = std::move(widget);
|
||||
if (_belowWidget) {
|
||||
_belowWidget->setParent(this);
|
||||
initDecorateWidget(_belowWidget.data());
|
||||
}
|
||||
|
||||
void PeerListContent::initDecorateWidget(Ui::RpWidget *widget) {
|
||||
if (widget) {
|
||||
widget->setParent(this);
|
||||
widget->events(
|
||||
) | rpl::filter([=](not_null<QEvent*> e) {
|
||||
return (e->type() == QEvent::Enter) && widget->isVisible();
|
||||
}) | rpl::start_with_next([=] {
|
||||
mouseLeftGeometry();
|
||||
}, widget->lifetime());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ struct MultiSelect;
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
class SessionShow;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
@@ -298,9 +299,9 @@ public:
|
||||
virtual void peerListSetHideEmpty(bool hide) = 0;
|
||||
virtual void peerListSetDescription(object_ptr<Ui::FlatLabel> description) = 0;
|
||||
virtual void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) = 0;
|
||||
virtual void peerListSetAboveWidget(object_ptr<TWidget> aboveWidget) = 0;
|
||||
virtual void peerListSetAboveSearchWidget(object_ptr<TWidget> aboveWidget) = 0;
|
||||
virtual void peerListSetBelowWidget(object_ptr<TWidget> belowWidget) = 0;
|
||||
virtual void peerListSetAboveWidget(object_ptr<Ui::RpWidget> aboveWidget) = 0;
|
||||
virtual void peerListSetAboveSearchWidget(object_ptr<Ui::RpWidget> aboveWidget) = 0;
|
||||
virtual void peerListSetBelowWidget(object_ptr<Ui::RpWidget> belowWidget) = 0;
|
||||
virtual void peerListMouseLeftGeometry() = 0;
|
||||
virtual void peerListSetSearchMode(PeerListSearchMode mode) = 0;
|
||||
virtual void peerListAppendRow(std::unique_ptr<PeerListRow> row) = 0;
|
||||
@@ -329,7 +330,7 @@ public:
|
||||
object_ptr<Ui::BoxContent> content,
|
||||
Ui::LayerOptions options = Ui::LayerOption::KeepOther) = 0;
|
||||
virtual void peerListHideLayer() = 0;
|
||||
virtual not_null<QWidget*> peerListToastParent() = 0;
|
||||
virtual std::shared_ptr<Main::SessionShow> peerListUiShow() = 0;
|
||||
|
||||
template <typename PeerDataRange>
|
||||
void peerListAddSelectedPeers(PeerDataRange &&range) {
|
||||
@@ -447,6 +448,9 @@ public:
|
||||
|
||||
virtual void prepare() = 0;
|
||||
|
||||
virtual void showFinished() {
|
||||
}
|
||||
|
||||
virtual void rowClicked(not_null<PeerListRow*> row) = 0;
|
||||
virtual void rowRightActionClicked(not_null<PeerListRow*> row) {
|
||||
}
|
||||
@@ -615,9 +619,9 @@ public:
|
||||
void setDescription(object_ptr<Ui::FlatLabel> description);
|
||||
void setSearchLoading(object_ptr<Ui::FlatLabel> loading);
|
||||
void setSearchNoResults(object_ptr<Ui::FlatLabel> noResults);
|
||||
void setAboveWidget(object_ptr<TWidget> widget);
|
||||
void setAboveSearchWidget(object_ptr<TWidget> widget);
|
||||
void setBelowWidget(object_ptr<TWidget> width);
|
||||
void setAboveWidget(object_ptr<Ui::RpWidget> widget);
|
||||
void setAboveSearchWidget(object_ptr<Ui::RpWidget> widget);
|
||||
void setBelowWidget(object_ptr<Ui::RpWidget> width);
|
||||
void setHideEmpty(bool hide);
|
||||
void refreshRows();
|
||||
|
||||
@@ -778,6 +782,7 @@ private:
|
||||
void clearAllContent();
|
||||
void handleMouseMove(QPoint globalPosition);
|
||||
void mousePressReleased(Qt::MouseButton button);
|
||||
void initDecorateWidget(Ui::RpWidget *widget);
|
||||
|
||||
const style::PeerList &_st;
|
||||
not_null<PeerListController*> _controller;
|
||||
@@ -812,9 +817,9 @@ private:
|
||||
int _aboveHeight = 0;
|
||||
int _belowHeight = 0;
|
||||
bool _hideEmpty = false;
|
||||
object_ptr<TWidget> _aboveWidget = { nullptr };
|
||||
object_ptr<TWidget> _aboveSearchWidget = { nullptr };
|
||||
object_ptr<TWidget> _belowWidget = { nullptr };
|
||||
object_ptr<Ui::RpWidget> _aboveWidget = { nullptr };
|
||||
object_ptr<Ui::RpWidget> _aboveSearchWidget = { nullptr };
|
||||
object_ptr<Ui::RpWidget> _belowWidget = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _description = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _searchNoResults = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _searchLoading = { nullptr };
|
||||
@@ -898,13 +903,13 @@ public:
|
||||
void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) override {
|
||||
_content->setSearchNoResults(std::move(noResults));
|
||||
}
|
||||
void peerListSetAboveWidget(object_ptr<TWidget> aboveWidget) override {
|
||||
void peerListSetAboveWidget(object_ptr<Ui::RpWidget> aboveWidget) override {
|
||||
_content->setAboveWidget(std::move(aboveWidget));
|
||||
}
|
||||
void peerListSetAboveSearchWidget(object_ptr<TWidget> aboveWidget) override {
|
||||
void peerListSetAboveSearchWidget(object_ptr<Ui::RpWidget> aboveWidget) override {
|
||||
_content->setAboveSearchWidget(std::move(aboveWidget));
|
||||
}
|
||||
void peerListSetBelowWidget(object_ptr<TWidget> belowWidget) override {
|
||||
void peerListSetBelowWidget(object_ptr<Ui::RpWidget> belowWidget) override {
|
||||
_content->setBelowWidget(std::move(belowWidget));
|
||||
}
|
||||
void peerListSetSearchMode(PeerListSearchMode mode) override {
|
||||
@@ -995,22 +1000,24 @@ public:
|
||||
void peerListHideLayer() override {
|
||||
Unexpected("...DelegateSimple::peerListHideLayer");
|
||||
}
|
||||
not_null<QWidget*> peerListToastParent() override {
|
||||
Unexpected("...DelegateSimple::peerListToastParent");
|
||||
std::shared_ptr<Main::SessionShow> peerListUiShow() override {
|
||||
Unexpected("...DelegateSimple::peerListUiShow");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class PeerListContentDelegateShow : public PeerListContentDelegateSimple {
|
||||
public:
|
||||
PeerListContentDelegateShow(std::shared_ptr<Ui::Show> show);
|
||||
explicit PeerListContentDelegateShow(
|
||||
std::shared_ptr<Main::SessionShow> show);
|
||||
void peerListShowBox(
|
||||
object_ptr<Ui::BoxContent> content,
|
||||
Ui::LayerOptions options = Ui::LayerOption::KeepOther) override;
|
||||
void peerListHideLayer() override;
|
||||
not_null<QWidget*> peerListToastParent() override;
|
||||
std::shared_ptr<Main::SessionShow> peerListUiShow() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Ui::Show> _show;
|
||||
std::shared_ptr<Main::SessionShow> _show;
|
||||
|
||||
};
|
||||
|
||||
@@ -1046,10 +1053,12 @@ public:
|
||||
object_ptr<Ui::BoxContent> content,
|
||||
Ui::LayerOptions options = Ui::LayerOption::KeepOther) override;
|
||||
void peerListHideLayer() override;
|
||||
not_null<QWidget*> peerListToastParent() override;
|
||||
std::shared_ptr<Main::SessionShow> peerListUiShow() override;
|
||||
|
||||
void setAddedTopScrollSkip(int skip);
|
||||
|
||||
void showFinished() override;
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
void setInnerFocus() override;
|
||||
@@ -1086,7 +1095,7 @@ private:
|
||||
|
||||
object_ptr<Ui::SlideWrap<Ui::MultiSelect>> _select = { nullptr };
|
||||
|
||||
const Ui::BoxShow _show;
|
||||
const std::shared_ptr<Main::SessionShow> _show;
|
||||
std::unique_ptr<PeerListController> _controller;
|
||||
Fn<void(PeerListBox*)> _init;
|
||||
bool _scrollBottomFixed = false;
|
||||
|
||||
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/widgets/multi_select.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "main/session/session_show.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_peer.h"
|
||||
@@ -301,7 +302,7 @@ PeerListsBox::Delegate::Delegate(
|
||||
not_null<PeerListController*> controller)
|
||||
: _box(box)
|
||||
, _controller(controller)
|
||||
, _show(_box) {
|
||||
, _show(Main::MakeSessionShow(_box->uiShow(), &_controller->session())) {
|
||||
}
|
||||
|
||||
void PeerListsBox::Delegate::peerListSetTitle(rpl::producer<QString> title) {
|
||||
@@ -374,15 +375,16 @@ void PeerListsBox::Delegate::peerListFinishSelectedRowsBunch() {
|
||||
void PeerListsBox::Delegate::peerListShowBox(
|
||||
object_ptr<Ui::BoxContent> content,
|
||||
Ui::LayerOptions options) {
|
||||
_show.showBox(std::move(content), options);
|
||||
_show->showBox(std::move(content), options);
|
||||
}
|
||||
|
||||
void PeerListsBox::Delegate::peerListHideLayer() {
|
||||
_show.hideLayer();
|
||||
_show->hideLayer();
|
||||
}
|
||||
|
||||
not_null<QWidget*> PeerListsBox::Delegate::peerListToastParent() {
|
||||
return _show.toastParent();
|
||||
auto PeerListsBox::Delegate::peerListUiShow()
|
||||
-> std::shared_ptr<Main::SessionShow> {
|
||||
return _show;
|
||||
}
|
||||
|
||||
bool PeerListsBox::Delegate::peerListIsRowChecked(
|
||||
|
||||
@@ -58,12 +58,12 @@ private:
|
||||
object_ptr<Ui::BoxContent> content,
|
||||
Ui::LayerOptions options = Ui::LayerOption::KeepOther) override;
|
||||
void peerListHideLayer() override;
|
||||
not_null<QWidget*> peerListToastParent() override;
|
||||
std::shared_ptr<Main::SessionShow> peerListUiShow() override;
|
||||
|
||||
private:
|
||||
const not_null<PeerListsBox*> _box;
|
||||
const not_null<PeerListController*> _controller;
|
||||
const Ui::BoxShow _show;
|
||||
const std::shared_ptr<Main::SessionShow> _show;
|
||||
|
||||
};
|
||||
struct List {
|
||||
|
||||
@@ -182,8 +182,7 @@ void AddBotToGroupBoxController::addBotToGroup(not_null<PeerData*> chat) {
|
||||
if (const auto megagroup = chat->asMegagroup()) {
|
||||
if (!megagroup->canAddMembers()) {
|
||||
_controller->show(
|
||||
Ui::MakeInformBox(tr::lng_error_cant_add_member()),
|
||||
Ui::LayerOption::KeepOther);
|
||||
Ui::MakeInformBox(tr::lng_error_cant_add_member()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -243,18 +242,16 @@ void AddBotToGroupBoxController::addBotToGroup(not_null<PeerData*> chat) {
|
||||
_token,
|
||||
_existingRights.value_or(ChatAdminRights()) });
|
||||
box->setSaveCallback(saveCallback);
|
||||
controller->show(std::move(box), Ui::LayerOption::KeepOther);
|
||||
controller->show(std::move(box));
|
||||
} else {
|
||||
auto callback = crl::guard(this, [=] {
|
||||
AddBotToGroup(bot, chat, _token);
|
||||
controller->hideLayer();
|
||||
});
|
||||
controller->show(
|
||||
Ui::MakeConfirmBox({
|
||||
tr::lng_bot_sure_invite(tr::now, lt_group, chat->name()),
|
||||
std::move(callback),
|
||||
}),
|
||||
Ui::LayerOption::KeepOther);
|
||||
controller->show(Ui::MakeConfirmBox({
|
||||
tr::lng_bot_sure_invite(tr::now, lt_group, chat->name()),
|
||||
std::move(callback),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -202,9 +202,7 @@ void InviteForbiddenController::send(
|
||||
int(list.size()),
|
||||
Ui::Text::RichLangValue);
|
||||
close();
|
||||
Ui::Toast::Show(
|
||||
show->toastParent(),
|
||||
{ .text = std::move(text), .st = &st::defaultToast });
|
||||
show->showToast(std::move(text));
|
||||
return true;
|
||||
};
|
||||
const auto sendForFull = [=] {
|
||||
@@ -368,7 +366,7 @@ bool AddParticipantsBoxController::needsInviteLinkButton() {
|
||||
QPointer<Ui::BoxContent> AddParticipantsBoxController::showBox(
|
||||
object_ptr<Ui::BoxContent> box) const {
|
||||
const auto weak = Ui::MakeWeak(box.data());
|
||||
delegate()->peerListShowBox(std::move(box), Ui::LayerOption::KeepOther);
|
||||
delegate()->peerListShowBox(std::move(box));
|
||||
return weak;
|
||||
}
|
||||
|
||||
@@ -425,7 +423,7 @@ void AddParticipantsBoxController::inviteSelectedUsers(
|
||||
if (users.empty()) {
|
||||
return;
|
||||
}
|
||||
const auto show = std::make_shared<Ui::BoxShow>(box);
|
||||
const auto show = box->uiShow();
|
||||
const auto request = [=](bool checked) {
|
||||
_peer->session().api().chatParticipants().add(
|
||||
_peer,
|
||||
@@ -493,9 +491,8 @@ void AddParticipantsBoxController::Start(
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
};
|
||||
Window::Show(navigation).showBox(
|
||||
Box<PeerListBox>(std::move(controller), std::move(initBox)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
parent->show(
|
||||
Box<PeerListBox>(std::move(controller), std::move(initBox)));
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::Start(
|
||||
@@ -538,9 +535,8 @@ void AddParticipantsBoxController::Start(
|
||||
}, box->lifetime());
|
||||
}
|
||||
};
|
||||
Window::Show(navigation).showBox(
|
||||
Box<PeerListBox>(std::move(controller), std::move(initBox)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
parent->show(
|
||||
Box<PeerListBox>(std::move(controller), std::move(initBox)));
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::Start(
|
||||
@@ -616,7 +612,7 @@ bool ChatInviteForbidden(
|
||||
box->addButton(tr::lng_via_link_send(), [=] {
|
||||
weak->send(
|
||||
box->collectSelectedRows(),
|
||||
std::make_shared<Ui::BoxShow>(box),
|
||||
box->uiShow(),
|
||||
crl::guard(box, [=] { box->closeBox(); }));
|
||||
});
|
||||
}
|
||||
@@ -626,8 +622,7 @@ bool ChatInviteForbidden(
|
||||
}, box->lifetime());
|
||||
};
|
||||
show->showBox(
|
||||
Box<PeerListBox>(std::move(controller), std::move(initBox)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
Box<PeerListBox>(std::move(controller), std::move(initBox)));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -673,7 +668,7 @@ void AddSpecialBoxController::migrate(
|
||||
QPointer<Ui::BoxContent> AddSpecialBoxController::showBox(
|
||||
object_ptr<Ui::BoxContent> box) const {
|
||||
const auto weak = Ui::MakeWeak(box.data());
|
||||
delegate()->peerListShowBox(std::move(box), Ui::LayerOption::KeepOther);
|
||||
delegate()->peerListShowBox(std::move(box));
|
||||
return weak;
|
||||
}
|
||||
|
||||
|
||||
@@ -505,5 +505,5 @@ void ShowChoosePeerBox(
|
||||
bot,
|
||||
query,
|
||||
std::move(callback)),
|
||||
std::move(initBox)), Ui::LayerOption::KeepOther);
|
||||
std::move(initBox)));
|
||||
}
|
||||
|
||||
@@ -69,8 +69,7 @@ void SendRequest(
|
||||
}
|
||||
if (box) {
|
||||
if (!wasContact) {
|
||||
Ui::Toast::Show(
|
||||
Ui::BoxShow(box.data()).toastParent(),
|
||||
box->showToast(
|
||||
tr::lng_new_contact_add_done(tr::now, lt_user, first));
|
||||
}
|
||||
box->closeBox();
|
||||
|
||||
@@ -274,12 +274,9 @@ struct IconSelector {
|
||||
};
|
||||
const auto selector = body->add(
|
||||
object_ptr<EmojiListWidget>(body, EmojiListDescriptor{
|
||||
.session = &controller->session(),
|
||||
.show = controller->uiShow(),
|
||||
.mode = EmojiListWidget::Mode::TopicIcon,
|
||||
.controller = controller,
|
||||
.paused = Window::PausedIn(
|
||||
controller,
|
||||
Window::GifPauseReason::Layer),
|
||||
.paused = Window::PausedIn(controller, PauseReason::Layer),
|
||||
.customRecentList = recent(),
|
||||
.customRecentFactory = std::move(factory),
|
||||
.st = &st::reactPanelEmojiPan,
|
||||
|
||||
@@ -18,7 +18,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "apiwrap.h"
|
||||
#include "main/main_session.h"
|
||||
@@ -168,13 +167,11 @@ void Controller::choose(not_null<ChannelData*> chat) {
|
||||
const auto onstack = _callback;
|
||||
onstack(chat);
|
||||
};
|
||||
delegate()->peerListShowBox(
|
||||
Ui::MakeConfirmBox({
|
||||
.text = text,
|
||||
.confirmed = sure,
|
||||
.confirmText = tr::lng_manage_discussion_group_link(tr::now),
|
||||
}),
|
||||
Ui::LayerOption::KeepOther);
|
||||
delegate()->peerListShowBox(Ui::MakeConfirmBox({
|
||||
.text = text,
|
||||
.confirmed = sure,
|
||||
.confirmText = tr::lng_manage_discussion_group_link(tr::now),
|
||||
}));
|
||||
}
|
||||
|
||||
void Controller::choose(not_null<ChatData*> chat) {
|
||||
@@ -201,13 +198,11 @@ void Controller::choose(not_null<ChatData*> chat) {
|
||||
};
|
||||
chat->session().api().migrateChat(chat, crl::guard(this, done));
|
||||
};
|
||||
delegate()->peerListShowBox(
|
||||
Ui::MakeConfirmBox({
|
||||
.text = text,
|
||||
.confirmed = sure,
|
||||
.confirmText = tr::lng_manage_discussion_group_link(tr::now),
|
||||
}),
|
||||
Ui::LayerOption::KeepOther);
|
||||
delegate()->peerListShowBox(Ui::MakeConfirmBox({
|
||||
.text = text,
|
||||
.confirmed = sure,
|
||||
.confirmText = tr::lng_manage_discussion_group_link(tr::now),
|
||||
}));
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<TextWithEntities> About(
|
||||
@@ -279,13 +274,11 @@ void Controller::choose(not_null<ChatData*> chat) {
|
||||
{ &st::settingsIconChat, Settings::kIconLightBlue }
|
||||
)->addClickHandler([=, parent = above.data()] {
|
||||
const auto guarded = crl::guard(parent, callback);
|
||||
Window::Show(navigation).showBox(
|
||||
Box<GroupInfoBox>(
|
||||
navigation,
|
||||
GroupInfoBox::Type::Megagroup,
|
||||
channel->name() + " Chat",
|
||||
guarded),
|
||||
Ui::LayerOption::KeepOther);
|
||||
navigation->uiShow()->showBox(Box<GroupInfoBox>(
|
||||
navigation,
|
||||
GroupInfoBox::Type::Megagroup,
|
||||
channel->name() + " Chat",
|
||||
guarded));
|
||||
});
|
||||
}
|
||||
box->peerListSetAboveWidget(std::move(above));
|
||||
@@ -363,10 +356,8 @@ object_ptr<Ui::BoxContent> EditLinkedChatBox(
|
||||
|
||||
void ShowForumForDiscussionError(
|
||||
not_null<Window::SessionNavigation*> navigation) {
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = Window::Show(navigation).toastParent(),
|
||||
.text = tr::lng_forum_topics_no_discussion(
|
||||
navigation->showToast(
|
||||
tr::lng_forum_topics_no_discussion(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue),
|
||||
});
|
||||
Ui::Text::RichLangValue));
|
||||
}
|
||||
|
||||
@@ -203,7 +203,6 @@ EditAdminBox::EditAdminBox(
|
||||
peer,
|
||||
user,
|
||||
(rights.flags != 0))
|
||||
, _show(this)
|
||||
, _oldRights(rights)
|
||||
, _oldRank(rank)
|
||||
, _addingBot(std::move(addingBot)) {
|
||||
@@ -399,7 +398,7 @@ void EditAdminBox::prepare() {
|
||||
Ui::Text::Bold(peer()->name()),
|
||||
Ui::Text::WithEntities),
|
||||
crl::guard(this, [=] { finishAddAdmin(); })
|
||||
}), Ui::LayerOption::KeepOther);
|
||||
}));
|
||||
} else {
|
||||
_finishSave();
|
||||
}
|
||||
@@ -623,16 +622,15 @@ void EditAdminBox::sendTransferRequestFrom(
|
||||
if (!box && !weak) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ui::Toast::Show(
|
||||
(box ? Ui::BoxShow(box) : weak->_show).toastParent(),
|
||||
const auto show = box ? box->uiShow() : weak->uiShow();
|
||||
show->showToast(
|
||||
(channel->isBroadcast()
|
||||
? tr::lng_rights_transfer_done_channel
|
||||
: tr::lng_rights_transfer_done_group)(
|
||||
tr::now,
|
||||
lt_user,
|
||||
user->shortName()));
|
||||
(box ? Ui::BoxShow(box) : weak->_show).hideLayer();
|
||||
show->hideLayer();
|
||||
}).fail(crl::guard(this, [=](const MTP::Error &error) {
|
||||
if (weak) {
|
||||
_transferRequestId = 0;
|
||||
@@ -694,7 +692,6 @@ EditRestrictedBox::EditRestrictedBox(
|
||||
bool hasAdminRights,
|
||||
ChatRestrictionsInfo rights)
|
||||
: EditParticipantBox(nullptr, peer, user, hasAdminRights)
|
||||
, _show(this)
|
||||
, _oldRights(rights) {
|
||||
}
|
||||
|
||||
@@ -788,7 +785,7 @@ ChatRestrictionsInfo EditRestrictedBox::defaultRights() const {
|
||||
}
|
||||
|
||||
void EditRestrictedBox::showRestrictUntil() {
|
||||
_show.showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
uiShow()->showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
const auto save = [=](TimeId result) {
|
||||
if (!result) {
|
||||
return;
|
||||
|
||||
@@ -114,7 +114,6 @@ private:
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
bool isGroup);
|
||||
|
||||
const Ui::BoxShow _show;
|
||||
const ChatAdminRightsInfo _oldRights;
|
||||
const QString _oldRank;
|
||||
Fn<void(
|
||||
@@ -168,7 +167,6 @@ private:
|
||||
void createUntilVariants();
|
||||
TimeId getRealUntilValue() const;
|
||||
|
||||
const Ui::BoxShow _show;
|
||||
const ChatRestrictionsInfo _oldRights;
|
||||
TimeId _until = 0;
|
||||
Fn<void(ChatRestrictionsInfo, ChatRestrictionsInfo)> _saveCallback;
|
||||
|
||||
@@ -1254,7 +1254,7 @@ void ParticipantsBoxController::rebuild() {
|
||||
QPointer<Ui::BoxContent> ParticipantsBoxController::showBox(
|
||||
object_ptr<Ui::BoxContent> box) const {
|
||||
const auto weak = Ui::MakeWeak(box.data());
|
||||
delegate()->peerListShowBox(std::move(box), Ui::LayerOption::KeepOther);
|
||||
delegate()->peerListShowBox(std::move(box));
|
||||
return weak;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/peers/edit_peer_requests_box.h"
|
||||
#include "boxes/peers/edit_peer_reactions.h"
|
||||
#include "boxes/stickers_box.h"
|
||||
#include "boxes/username_box.h"
|
||||
#include "ui/boxes/single_choice_box.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "core/application.h"
|
||||
@@ -35,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_message_reactions.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "data/data_user.h"
|
||||
#include "history/admin_log/history_admin_log_section.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "lang/lang_keys.h"
|
||||
@@ -46,7 +48,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
@@ -66,6 +67,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kBotManagerUsername = "BotFather"_cs;
|
||||
|
||||
[[nodiscard]] auto ToPositiveNumberString() {
|
||||
return rpl::map([](int count) {
|
||||
return count ? QString::number(count) : QString();
|
||||
@@ -237,9 +240,7 @@ void ShowEditPermissions(
|
||||
};
|
||||
ShowEditPeerPermissionsBox(box, navigation, peer, std::move(done));
|
||||
};
|
||||
navigation->parentController()->show(
|
||||
Box(std::move(createBox)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
navigation->parentController()->show(Box(std::move(createBox)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -305,6 +306,13 @@ private:
|
||||
void fillManageSection();
|
||||
void fillPendingRequestsButton();
|
||||
|
||||
void fillBotUsernamesButton();
|
||||
#if 0 // Enable after design improvements.
|
||||
void fillBotEditIntroButton();
|
||||
void fillBotEditCommandsButton();
|
||||
void fillBotEditSettingsButton();
|
||||
#endif
|
||||
|
||||
void submitTitle();
|
||||
void submitDescription();
|
||||
void deleteWithConfirmation();
|
||||
@@ -340,6 +348,10 @@ private:
|
||||
void continueSave();
|
||||
void cancelSave();
|
||||
|
||||
#if 0 // Enable after design improvements.
|
||||
void toggleBotManager(const QString &command);
|
||||
#endif
|
||||
|
||||
void togglePreHistoryHidden(
|
||||
not_null<ChannelData*> channel,
|
||||
bool hidden,
|
||||
@@ -362,6 +374,7 @@ private:
|
||||
not_null<PeerData*> _peer;
|
||||
MTP::Sender _api;
|
||||
const bool _isGroup = false;
|
||||
const bool _isBot = false;
|
||||
|
||||
base::unique_qptr<Ui::VerticalLayout> _wrap;
|
||||
Controls _controls;
|
||||
@@ -385,8 +398,11 @@ Controller::Controller(
|
||||
, _box(box)
|
||||
, _peer(peer)
|
||||
, _api(&_peer->session().mtp())
|
||||
, _isGroup(_peer->isChat() || _peer->isMegagroup()) {
|
||||
_box->setTitle(_isGroup
|
||||
, _isGroup(_peer->isChat() || _peer->isMegagroup())
|
||||
, _isBot(_peer->isUser() && _peer->asUser()->botInfo) {
|
||||
_box->setTitle(_isBot
|
||||
? tr::lng_edit_bot_title()
|
||||
: _isGroup
|
||||
? tr::lng_edit_group()
|
||||
: tr::lng_edit_channel_title());
|
||||
_box->addButton(tr::lng_settings_save(), [=] {
|
||||
@@ -490,7 +506,9 @@ object_ptr<Ui::RpWidget> Controller::createTitleEdit() {
|
||||
object_ptr<Ui::InputField>(
|
||||
_wrap,
|
||||
st::defaultInputField,
|
||||
(_isGroup
|
||||
(_isBot
|
||||
? tr::lng_dlg_new_bot_name
|
||||
: _isGroup
|
||||
? tr::lng_dlg_new_group_name
|
||||
: tr::lng_dlg_new_channel_name)(),
|
||||
_peer->name()),
|
||||
@@ -585,8 +603,7 @@ object_ptr<Ui::RpWidget> Controller::createStickersEdit() {
|
||||
rpl::single(QString()), //Empty count.
|
||||
[=, controller = _navigation->parentController()] {
|
||||
controller->show(
|
||||
Box<StickersBox>(controller, channel),
|
||||
Ui::LayerOption::KeepOther);
|
||||
Box<StickersBox>(controller->uiShow(), channel));
|
||||
},
|
||||
{ &st::settingsIconStickers, Settings::kIconLightOrange });
|
||||
|
||||
@@ -602,7 +619,9 @@ object_ptr<Ui::RpWidget> Controller::createStickersEdit() {
|
||||
}
|
||||
|
||||
bool Controller::canEditInformation() const {
|
||||
if (const auto channel = _peer->asChannel()) {
|
||||
if (_isBot) {
|
||||
return _peer->asUser()->botInfo->canEditInformation;
|
||||
} else if (const auto channel = _peer->asChannel()) {
|
||||
return channel->canEditInformation();
|
||||
} else if (const auto chat = _peer->asChat()) {
|
||||
return chat->canEditInformation();
|
||||
@@ -651,8 +670,7 @@ void Controller::showEditPeerTypeBox(
|
||||
_channelHasLocationOriginalValue,
|
||||
boxCallback,
|
||||
_typeDataSavedValue,
|
||||
error),
|
||||
Ui::LayerOption::KeepOther);
|
||||
error));
|
||||
box->boxClosing(
|
||||
) | rpl::start_with_next([peer = _peer] {
|
||||
peer->session().api().usernames().requestToCache(peer);
|
||||
@@ -686,14 +704,12 @@ void Controller::showEditLinkedChatBox() {
|
||||
|| channel->canEditPreHistoryHidden()));
|
||||
|
||||
if (const auto chat = *_linkedChatSavedValue) {
|
||||
*box = _navigation->parentController()->show(
|
||||
EditLinkedChatBox(
|
||||
_navigation,
|
||||
channel,
|
||||
chat,
|
||||
canEdit,
|
||||
callback),
|
||||
Ui::LayerOption::KeepOther);
|
||||
*box = _navigation->parentController()->show(EditLinkedChatBox(
|
||||
_navigation,
|
||||
channel,
|
||||
chat,
|
||||
canEdit,
|
||||
callback));
|
||||
return;
|
||||
} else if (!canEdit || _linkedChatsRequestId) {
|
||||
return;
|
||||
@@ -720,13 +736,11 @@ void Controller::showEditLinkedChatBox() {
|
||||
for (const auto &item : list) {
|
||||
chats.emplace_back(_peer->owner().processChat(item));
|
||||
}
|
||||
*box = _navigation->parentController()->show(
|
||||
EditLinkedChatBox(
|
||||
_navigation,
|
||||
channel,
|
||||
std::move(chats),
|
||||
callback),
|
||||
Ui::LayerOption::KeepOther);
|
||||
*box = _navigation->parentController()->show(EditLinkedChatBox(
|
||||
_navigation,
|
||||
channel,
|
||||
std::move(chats),
|
||||
callback));
|
||||
}).fail([=] {
|
||||
_linkedChatsRequestId = 0;
|
||||
}).send();
|
||||
@@ -864,14 +878,12 @@ void Controller::fillForumButton() {
|
||||
if (_linkedChatSavedValue && *_linkedChatSavedValue) {
|
||||
ShowForumForDiscussionError(_navigation);
|
||||
} else {
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = Window::Show(_navigation).toastParent(),
|
||||
.text = tr::lng_forum_topics_not_enough(
|
||||
_navigation->showToast(
|
||||
tr::lng_forum_topics_not_enough(
|
||||
tr::now,
|
||||
lt_count,
|
||||
EnableForumMinMembers(_peer),
|
||||
Ui::Text::RichLangValue),
|
||||
});
|
||||
Ui::Text::RichLangValue));
|
||||
}
|
||||
} else {
|
||||
_forumSavedValue = toggled;
|
||||
@@ -984,12 +996,41 @@ void Controller::fillHistoryVisibilityButton() {
|
||||
void Controller::fillManageSection() {
|
||||
Expects(_controls.buttonsLayout != nullptr);
|
||||
|
||||
using namespace rpl::mappers;
|
||||
if (_isBot) {
|
||||
const auto &container = _controls.buttonsLayout;
|
||||
|
||||
AddSkip(container, 0);
|
||||
fillBotUsernamesButton();
|
||||
#if 0 // Enable after design improvements.
|
||||
fillBotEditIntroButton();
|
||||
fillBotEditCommandsButton();
|
||||
fillBotEditSettingsButton();
|
||||
#endif
|
||||
Settings::AddSkip(
|
||||
container,
|
||||
st::editPeerTopButtonsLayoutSkipCustomBottom);
|
||||
container->add(object_ptr<Ui::DividerLabel>(
|
||||
container,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
tr::lng_manage_peer_bot_about(
|
||||
lt_bot,
|
||||
rpl::single(Ui::Text::Link(
|
||||
'@' + kBotManagerUsername.utf16(),
|
||||
_peer->session().createInternalLinkFull(
|
||||
kBotManagerUsername.utf16()))),
|
||||
Ui::Text::RichLangValue),
|
||||
st::boxDividerLabel),
|
||||
st::settingsDividerLabelPadding));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto chat = _peer->asChat();
|
||||
const auto channel = _peer->asChannel();
|
||||
const auto isChannel = (!chat);
|
||||
if (!chat && !channel) return;
|
||||
if (!chat && !channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto canEditType = [&] {
|
||||
return isChannel
|
||||
@@ -1042,7 +1083,6 @@ void Controller::fillManageSection() {
|
||||
}();
|
||||
|
||||
const auto canEditStickers = [&] {
|
||||
// return true;
|
||||
return isChannel
|
||||
? channel->canEditStickers()
|
||||
: false;
|
||||
@@ -1180,14 +1220,12 @@ void Controller::fillManageSection() {
|
||||
tr::lng_manage_peer_invite_links(),
|
||||
rpl::duplicate(count) | ToPositiveNumberString(),
|
||||
[=] {
|
||||
_navigation->parentController()->show(
|
||||
Box(
|
||||
ManageInviteLinksBox,
|
||||
_peer,
|
||||
_peer->session().user(),
|
||||
0,
|
||||
0),
|
||||
Ui::LayerOption::KeepOther);
|
||||
_navigation->parentController()->show(Box(
|
||||
ManageInviteLinksBox,
|
||||
_peer,
|
||||
_peer->session().user(),
|
||||
0,
|
||||
0));
|
||||
},
|
||||
{ &st::infoRoundedIconInviteLinks, Settings::kIconLightOrange });
|
||||
wrap->toggle(true, anim::type::instant);
|
||||
@@ -1307,6 +1345,98 @@ void Controller::fillPendingRequestsButton() {
|
||||
}, wrap->lifetime());
|
||||
}
|
||||
|
||||
void Controller::fillBotUsernamesButton() {
|
||||
Expects(_isBot);
|
||||
|
||||
const auto user = _peer->asUser();
|
||||
|
||||
auto localUsernames = rpl::single(
|
||||
user->usernames()
|
||||
) | rpl::map([](const std::vector<QString> &usernames) {
|
||||
return ranges::views::all(
|
||||
usernames
|
||||
) | ranges::views::transform([](const QString &u) {
|
||||
return Data::Username{ u };
|
||||
}) | ranges::to_vector;
|
||||
});
|
||||
auto usernamesValue = std::move(
|
||||
localUsernames
|
||||
) | rpl::then(
|
||||
_peer->session().api().usernames().loadUsernames(_peer)
|
||||
);
|
||||
auto rightLabel = rpl::duplicate(
|
||||
usernamesValue
|
||||
) | rpl::map([=](const Data::Usernames &usernames) {
|
||||
if (usernames.size() <= 1) {
|
||||
return user->session().createInternalLink(user->username());
|
||||
} else {
|
||||
const auto active = ranges::count_if(
|
||||
usernames,
|
||||
[](const Data::Username &u) { return u.active; });
|
||||
return u"%1/%2"_q.arg(active).arg(usernames.size());
|
||||
}
|
||||
});
|
||||
auto leftLabel = std::move(
|
||||
usernamesValue
|
||||
) | rpl::map([=](const Data::Usernames &usernames) {
|
||||
return (usernames.size() <= 1)
|
||||
? tr::lng_manage_peer_bot_public_link()
|
||||
: tr::lng_manage_peer_bot_public_links();
|
||||
}) | rpl::flatten_latest();
|
||||
|
||||
_controls.buttonsLayout->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
_controls.buttonsLayout,
|
||||
object_ptr<Ui::VerticalLayout>(
|
||||
_controls.buttonsLayout)));
|
||||
AddButtonWithCount(
|
||||
_controls.buttonsLayout,
|
||||
std::move(leftLabel),
|
||||
std::move(rightLabel),
|
||||
[=] {
|
||||
_navigation->uiShow()->showBox(Box(UsernamesBox, user));
|
||||
},
|
||||
{ &st::infoRoundedIconInviteLinks, Settings::kIconLightOrange });
|
||||
}
|
||||
|
||||
#if 0 // Enable after design improvements.
|
||||
void Controller::fillBotEditIntroButton() {
|
||||
Expects(_isBot);
|
||||
|
||||
const auto user = _peer->asUser();
|
||||
AddButtonWithCount(
|
||||
_controls.buttonsLayout,
|
||||
tr::lng_manage_peer_bot_edit_intro(),
|
||||
rpl::never<QString>(),
|
||||
[=] { toggleBotManager(u"%1-intro"_q.arg(user->username())); },
|
||||
{ &st::settingsIconChat, Settings::kIconLightBlue });
|
||||
}
|
||||
|
||||
void Controller::fillBotEditCommandsButton() {
|
||||
Expects(_isBot);
|
||||
|
||||
const auto user = _peer->asUser();
|
||||
AddButtonWithCount(
|
||||
_controls.buttonsLayout,
|
||||
tr::lng_manage_peer_bot_edit_commands(),
|
||||
rpl::never<QString>(),
|
||||
[=] { toggleBotManager(u"%1-commands"_q.arg(user->username())); },
|
||||
{ &st::settingsIconChat, Settings::kIconLightBlue });
|
||||
}
|
||||
|
||||
void Controller::fillBotEditSettingsButton() {
|
||||
Expects(_isBot);
|
||||
|
||||
const auto user = _peer->asUser();
|
||||
AddButtonWithCount(
|
||||
_controls.buttonsLayout,
|
||||
tr::lng_manage_peer_bot_edit_settings(),
|
||||
rpl::never<QString>(),
|
||||
[=] { toggleBotManager(user->username()); },
|
||||
{ &st::settingsIconChat, Settings::kIconLightBlue });
|
||||
}
|
||||
#endif
|
||||
|
||||
void Controller::submitTitle() {
|
||||
Expects(_controls.title != nullptr);
|
||||
|
||||
@@ -1669,6 +1799,31 @@ void Controller::saveTitle() {
|
||||
)).done(std::move(onDone)
|
||||
).fail(std::move(onFail)
|
||||
).send();
|
||||
} else if (_isBot) {
|
||||
_api.request(MTPbots_GetBotInfo(
|
||||
MTP_flags(MTPbots_GetBotInfo::Flag::f_bot),
|
||||
_peer->asUser()->inputUser,
|
||||
MTPstring() // Lang code.
|
||||
)).done([=](const MTPbots_BotInfo &result) {
|
||||
const auto was = qs(result.data().vname());
|
||||
const auto now = *_savingData.title;
|
||||
if (was == now) {
|
||||
return continueSave();
|
||||
}
|
||||
using Flag = MTPbots_SetBotInfo::Flag;
|
||||
_api.request(MTPbots_SetBotInfo(
|
||||
MTP_flags(Flag::f_bot | Flag::f_name),
|
||||
_peer->asUser()->inputUser,
|
||||
MTPstring(), // Lang code.
|
||||
MTP_string(now), // Name.
|
||||
MTPstring(), // About.
|
||||
MTPstring() // Description.
|
||||
)).done([=] {
|
||||
continueSave();
|
||||
}).fail(std::move(onFail)
|
||||
).send();
|
||||
}).fail(std::move(onFail)
|
||||
).send();
|
||||
} else {
|
||||
continueSave();
|
||||
}
|
||||
@@ -1683,6 +1838,36 @@ void Controller::saveDescription() {
|
||||
_peer->setAbout(*_savingData.description);
|
||||
continueSave();
|
||||
};
|
||||
if (_isBot) {
|
||||
_api.request(MTPbots_GetBotInfo(
|
||||
MTP_flags(MTPbots_GetBotInfo::Flag::f_bot),
|
||||
_peer->asUser()->inputUser,
|
||||
MTPstring() // Lang code.
|
||||
)).done([=](const MTPbots_BotInfo &result) {
|
||||
const auto was = qs(result.data().vabout());
|
||||
const auto now = *_savingData.description;
|
||||
if (was == now) {
|
||||
return continueSave();
|
||||
}
|
||||
using Flag = MTPbots_SetBotInfo::Flag;
|
||||
_api.request(MTPbots_SetBotInfo(
|
||||
MTP_flags(Flag::f_bot | Flag::f_about),
|
||||
_peer->asUser()->inputUser,
|
||||
MTPstring(), // Lang code.
|
||||
MTPstring(), // Name.
|
||||
MTP_string(now), // About.
|
||||
MTPstring() // Description.
|
||||
)).done([=] {
|
||||
successCallback();
|
||||
}).fail([=] {
|
||||
_controls.description->showError();
|
||||
cancelSave();
|
||||
}).send();
|
||||
}).fail([=] {
|
||||
continueSave();
|
||||
}).send();
|
||||
return;
|
||||
}
|
||||
_api.request(MTPmessages_EditChatAbout(
|
||||
_peer->input,
|
||||
MTP_string(*_savingData.description)
|
||||
@@ -1725,6 +1910,24 @@ void Controller::saveHistoryVisibility() {
|
||||
[=] { cancelSave(); });
|
||||
}
|
||||
|
||||
#if 0 // Enable after design improvements.
|
||||
void Controller::toggleBotManager(const QString &command) {
|
||||
const auto controller = _navigation->parentController();
|
||||
_api.request(MTPcontacts_ResolveUsername(
|
||||
MTP_string(kBotManagerUsername.utf16())
|
||||
)).done([=](const MTPcontacts_ResolvedPeer &result) {
|
||||
_peer->owner().processUsers(result.data().vusers());
|
||||
_peer->owner().processChats(result.data().vchats());
|
||||
const auto botPeer = _peer->owner().peerLoaded(
|
||||
peerFromMTP(result.data().vpeer()));
|
||||
if (const auto bot = botPeer ? botPeer->asUser() : nullptr) {
|
||||
_peer->session().api().sendBotStart(bot, bot, command);
|
||||
controller->showPeerHistory(bot);
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Controller::togglePreHistoryHidden(
|
||||
not_null<ChannelData*> channel,
|
||||
bool hidden,
|
||||
@@ -1902,8 +2105,7 @@ void Controller::deleteWithConfirmation() {
|
||||
.confirmed = deleteCallback,
|
||||
.confirmText = tr::lng_box_delete(),
|
||||
.confirmStyle = &st::attentionBoxButton,
|
||||
}),
|
||||
Ui::LayerOption::KeepOther);
|
||||
}));
|
||||
}
|
||||
|
||||
void Controller::deleteChannel() {
|
||||
@@ -2014,7 +2216,9 @@ object_ptr<Ui::SettingsButton> EditPeerInfoBox::CreateButton(
|
||||
}
|
||||
|
||||
bool EditPeerInfoBox::Available(not_null<PeerData*> peer) {
|
||||
if (const auto chat = peer->asChat()) {
|
||||
if (const auto bot = peer->asUser()) {
|
||||
return bot->botInfo && bot->botInfo->canEditInformation;
|
||||
} else if (const auto chat = peer->asChat()) {
|
||||
return false
|
||||
|| chat->canEditInformation()
|
||||
|| chat->canEditPermissions();
|
||||
|
||||
@@ -26,7 +26,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/abstract_button.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/boxes/edit_invite_link.h"
|
||||
#include "ui/painter.h"
|
||||
@@ -274,12 +273,13 @@ QImage QrForShare(const QString &text) {
|
||||
void QrBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
const QString &link,
|
||||
Fn<void(QImage, std::shared_ptr<Ui::BoxShow>)> share) {
|
||||
rpl::producer<QString> about,
|
||||
Fn<void(QImage, std::shared_ptr<Ui::Show>)> share) {
|
||||
box->setTitle(tr::lng_group_invite_qr_title());
|
||||
|
||||
box->addButton(tr::lng_about_done(), [=] { box->closeBox(); });
|
||||
|
||||
const auto copyCallback = [=, show = std::make_shared<Ui::BoxShow>(box)] {
|
||||
const auto copyCallback = [=, show = box->uiShow()] {
|
||||
share(QrForShare(link), show);
|
||||
};
|
||||
|
||||
@@ -307,7 +307,7 @@ void QrBox(
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_group_invite_qr_about(),
|
||||
std::move(about),
|
||||
st::boxLabel),
|
||||
st::inviteLinkQrValuePadding);
|
||||
|
||||
@@ -345,32 +345,23 @@ void Controller::addHeaderBlock(not_null<Ui::VerticalLayout*> container) {
|
||||
const auto admin = current.admin;
|
||||
const auto weak = Ui::MakeWeak(container);
|
||||
const auto copyLink = crl::guard(weak, [=] {
|
||||
CopyInviteLink(delegate()->peerListToastParent(), link);
|
||||
CopyInviteLink(delegate()->peerListUiShow(), link);
|
||||
});
|
||||
const auto shareLink = crl::guard(weak, [=] {
|
||||
delegate()->peerListShowBox(
|
||||
ShareInviteLinkBox(_peer, link),
|
||||
Ui::LayerOption::KeepOther);
|
||||
delegate()->peerListShowBox(ShareInviteLinkBox(_peer, link));
|
||||
});
|
||||
const auto getLinkQr = crl::guard(weak, [=] {
|
||||
delegate()->peerListShowBox(
|
||||
InviteLinkQrBox(link),
|
||||
Ui::LayerOption::KeepOther);
|
||||
InviteLinkQrBox(link, tr::lng_group_invite_qr_about()));
|
||||
});
|
||||
const auto revokeLink = crl::guard(weak, [=] {
|
||||
delegate()->peerListShowBox(
|
||||
RevokeLinkBox(_peer, admin, link),
|
||||
Ui::LayerOption::KeepOther);
|
||||
delegate()->peerListShowBox(RevokeLinkBox(_peer, admin, link));
|
||||
});
|
||||
const auto editLink = crl::guard(weak, [=] {
|
||||
delegate()->peerListShowBox(
|
||||
EditLinkBox(_peer, _data.current()),
|
||||
Ui::LayerOption::KeepOther);
|
||||
delegate()->peerListShowBox(EditLinkBox(_peer, _data.current()));
|
||||
});
|
||||
const auto deleteLink = crl::guard(weak, [=] {
|
||||
delegate()->peerListShowBox(
|
||||
DeleteLinkBox(_peer, admin, link),
|
||||
Ui::LayerOption::KeepOther);
|
||||
delegate()->peerListShowBox(DeleteLinkBox(_peer, admin, link));
|
||||
});
|
||||
|
||||
const auto createMenu = [=] {
|
||||
@@ -731,9 +722,14 @@ void Controller::loadMoreRows() {
|
||||
void Controller::appendSlice(const Api::JoinedByLinkSlice &slice) {
|
||||
for (const auto &user : slice.users) {
|
||||
_lastUser = user;
|
||||
delegate()->peerListAppendRow((_role == Role::Requested)
|
||||
auto row = (_role == Role::Requested)
|
||||
? std::make_unique<RequestedRow>(user.user, user.date)
|
||||
: std::make_unique<PeerListRow>(user.user));
|
||||
: std::make_unique<PeerListRow>(user.user);
|
||||
if (_role != Role::Requested && user.viaFilterLink) {
|
||||
row->setCustomStatus(
|
||||
tr::lng_group_invite_joined_via_filter(tr::now));
|
||||
}
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
}
|
||||
delegate()->peerListRefreshRows();
|
||||
if (delegate()->peerListFullRowsCount() > 0) {
|
||||
@@ -803,16 +799,13 @@ void Controller::processRequest(
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
if (approved) {
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = delegate()->peerListToastParent(),
|
||||
.text = (_peer->isBroadcast()
|
||||
? tr::lng_group_requests_was_added_channel
|
||||
: tr::lng_group_requests_was_added)(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Bold(user->name()),
|
||||
Ui::Text::WithEntities)
|
||||
});
|
||||
delegate()->peerListUiShow()->showToast((_peer->isBroadcast()
|
||||
? tr::lng_group_requests_was_added_channel
|
||||
: tr::lng_group_requests_was_added)(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Bold(user->name()),
|
||||
Ui::Text::WithEntities));
|
||||
}
|
||||
});
|
||||
const auto fail = crl::guard(this, [=] {
|
||||
@@ -955,28 +948,24 @@ void AddPermanentLinkBlock(
|
||||
const auto weak = Ui::MakeWeak(container);
|
||||
const auto copyLink = crl::guard(weak, [=] {
|
||||
if (const auto current = value->current(); !current.link.isEmpty()) {
|
||||
CopyInviteLink(show->toastParent(), current.link);
|
||||
CopyInviteLink(show, current.link);
|
||||
}
|
||||
});
|
||||
const auto shareLink = crl::guard(weak, [=] {
|
||||
if (const auto current = value->current(); !current.link.isEmpty()) {
|
||||
show->showBox(
|
||||
ShareInviteLinkBox(peer, current.link),
|
||||
Ui::LayerOption::KeepOther);
|
||||
show->showBox(ShareInviteLinkBox(peer, current.link));
|
||||
}
|
||||
});
|
||||
const auto getLinkQr = crl::guard(weak, [=] {
|
||||
if (const auto current = value->current(); !current.link.isEmpty()) {
|
||||
show->showBox(
|
||||
InviteLinkQrBox(current.link),
|
||||
Ui::LayerOption::KeepOther);
|
||||
show->showBox(InviteLinkQrBox(
|
||||
current.link,
|
||||
tr::lng_group_invite_qr_about()));
|
||||
}
|
||||
});
|
||||
const auto revokeLink = crl::guard(weak, [=] {
|
||||
if (const auto current = value->current(); !current.link.isEmpty()) {
|
||||
show->showBox(
|
||||
RevokeLinkBox(peer, admin, current.link, true),
|
||||
Ui::LayerOption::KeepOther);
|
||||
show->showBox(RevokeLinkBox(peer, admin, current.link, true));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1103,9 +1092,7 @@ void AddPermanentLinkBlock(
|
||||
st::inviteLinkJoinedRowPadding
|
||||
)->setClickedCallback([=] {
|
||||
if (!currentLinkFields->link.isEmpty()) {
|
||||
show->showBox(
|
||||
ShowInviteLinkBox(peer, *currentLinkFields),
|
||||
Ui::LayerOption::KeepOther);
|
||||
show->showBox(ShowInviteLinkBox(peer, *currentLinkFields));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1120,20 +1107,26 @@ void AddPermanentLinkBlock(
|
||||
}));
|
||||
}
|
||||
|
||||
void CopyInviteLink(not_null<QWidget*> toastParent, const QString &link) {
|
||||
void CopyInviteLink(std::shared_ptr<Ui::Show> show, const QString &link) {
|
||||
QGuiApplication::clipboard()->setText(link);
|
||||
Ui::Toast::Show(toastParent, tr::lng_group_invite_copied(tr::now));
|
||||
show->showToast(tr::lng_group_invite_copied(tr::now));
|
||||
}
|
||||
|
||||
object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &link) {
|
||||
return ShareInviteLinkBox(&peer->session(), link);
|
||||
}
|
||||
|
||||
object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||
not_null<Main::Session*> session,
|
||||
const QString &link) {
|
||||
const auto sending = std::make_shared<bool>();
|
||||
const auto box = std::make_shared<QPointer<ShareBox>>();
|
||||
|
||||
const auto showToast = [=](const QString &text) {
|
||||
if (*box) {
|
||||
Ui::Toast::Show(Ui::BoxShow(*box).toastParent(), text);
|
||||
(*box)->showToast(text);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1170,9 +1163,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||
}
|
||||
text.append(error.first);
|
||||
if (*box) {
|
||||
Ui::BoxShow(*box).showBox(
|
||||
Ui::MakeInformBox(text),
|
||||
Ui::LayerOption::KeepOther);
|
||||
(*box)->uiShow()->showBox(Ui::MakeInformBox(text));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1187,7 +1178,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||
} else {
|
||||
comment.text = link;
|
||||
}
|
||||
auto &api = peer->session().api();
|
||||
auto &api = session->api();
|
||||
for (const auto thread : result) {
|
||||
auto message = Api::MessageToSend(
|
||||
Api::SendAction(thread, options));
|
||||
@@ -1204,7 +1195,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||
return Data::CanSendTexts(thread);
|
||||
};
|
||||
auto object = Box<ShareBox>(ShareBox::Descriptor{
|
||||
.session = &peer->session(),
|
||||
.session = session,
|
||||
.copyCallback = std::move(copyCallback),
|
||||
.submitCallback = std::move(submitCallback),
|
||||
.filterCallback = std::move(filterCallback),
|
||||
@@ -1213,17 +1204,16 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||
return object;
|
||||
}
|
||||
|
||||
object_ptr<Ui::BoxContent> InviteLinkQrBox(const QString &link) {
|
||||
return Box(QrBox, link, [=](
|
||||
object_ptr<Ui::BoxContent> InviteLinkQrBox(
|
||||
const QString &link,
|
||||
rpl::producer<QString> about) {
|
||||
return Box(QrBox, link, std::move(about), [=](
|
||||
const QImage &image,
|
||||
std::shared_ptr<Ui::BoxShow> show) {
|
||||
std::shared_ptr<Ui::Show> show) {
|
||||
auto mime = std::make_unique<QMimeData>();
|
||||
mime->setImageData(image);
|
||||
QGuiApplication::clipboard()->setMimeData(mime.release());
|
||||
|
||||
Ui::Toast::Show(
|
||||
show->toastParent(),
|
||||
tr::lng_group_invite_qr_copied(tr::now));
|
||||
show->showToast(tr::lng_group_invite_qr_copied(tr::now));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@ namespace Api {
|
||||
struct InviteLink;
|
||||
} // namespace Api
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
class VerticalLayout;
|
||||
class Show;
|
||||
@@ -34,11 +38,16 @@ void AddPermanentLinkBlock(
|
||||
not_null<UserData*> admin,
|
||||
rpl::producer<Api::InviteLink> fromList);
|
||||
|
||||
void CopyInviteLink(not_null<QWidget*> toastParent, const QString &link);
|
||||
void CopyInviteLink(std::shared_ptr<Ui::Show> show, const QString &link);
|
||||
[[nodiscard]] object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &link);
|
||||
[[nodiscard]] object_ptr<Ui::BoxContent> InviteLinkQrBox(const QString &link);
|
||||
[[nodiscard]] object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||
not_null<Main::Session*> session,
|
||||
const QString &link);
|
||||
[[nodiscard]] object_ptr<Ui::BoxContent> InviteLinkQrBox(
|
||||
const QString &link,
|
||||
rpl::producer<QString> about);
|
||||
[[nodiscard]] object_ptr<Ui::BoxContent> RevokeLinkBox(
|
||||
not_null<PeerData*> peer,
|
||||
not_null<UserData*> admin,
|
||||
|
||||
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_session.h"
|
||||
#include "main/session/session_show.h"
|
||||
#include "main/main_session.h"
|
||||
#include "api/api_invite_links.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
@@ -543,8 +544,7 @@ void LinksController::appendSlice(const InviteLinksSlice &slice) {
|
||||
|
||||
void LinksController::rowClicked(not_null<PeerListRow*> row) {
|
||||
delegate()->peerListShowBox(
|
||||
ShowInviteLinkBox(_peer, static_cast<Row*>(row.get())->data()),
|
||||
Ui::LayerOption::KeepOther);
|
||||
ShowInviteLinkBox(_peer, static_cast<Row*>(row.get())->data()));
|
||||
}
|
||||
|
||||
void LinksController::rowRightActionClicked(not_null<PeerListRow*> row) {
|
||||
@@ -579,33 +579,25 @@ base::unique_qptr<Ui::PopupMenu> LinksController::createRowContextMenu(
|
||||
st::popupMenuWithIcons);
|
||||
if (data.revoked) {
|
||||
result->addAction(tr::lng_group_invite_context_delete(tr::now), [=] {
|
||||
delegate()->peerListShowBox(
|
||||
DeleteLinkBox(_peer, _admin, link),
|
||||
Ui::LayerOption::KeepOther);
|
||||
delegate()->peerListShowBox(DeleteLinkBox(_peer, _admin, link));
|
||||
}, &st::menuIconDelete);
|
||||
} else {
|
||||
result->addAction(tr::lng_group_invite_context_copy(tr::now), [=] {
|
||||
CopyInviteLink(delegate()->peerListToastParent(), link);
|
||||
CopyInviteLink(delegate()->peerListUiShow(), link);
|
||||
}, &st::menuIconCopy);
|
||||
result->addAction(tr::lng_group_invite_context_share(tr::now), [=] {
|
||||
delegate()->peerListShowBox(
|
||||
ShareInviteLinkBox(_peer, link),
|
||||
Ui::LayerOption::KeepOther);
|
||||
ShareInviteLinkBox(_peer, link));
|
||||
}, &st::menuIconShare);
|
||||
result->addAction(tr::lng_group_invite_context_qr(tr::now), [=] {
|
||||
delegate()->peerListShowBox(
|
||||
InviteLinkQrBox(link),
|
||||
Ui::LayerOption::KeepOther);
|
||||
InviteLinkQrBox(link, tr::lng_group_invite_qr_about()));
|
||||
}, &st::menuIconQrCode);
|
||||
result->addAction(tr::lng_group_invite_context_edit(tr::now), [=] {
|
||||
delegate()->peerListShowBox(
|
||||
EditLinkBox(_peer, data),
|
||||
Ui::LayerOption::KeepOther);
|
||||
delegate()->peerListShowBox(EditLinkBox(_peer, data));
|
||||
}, &st::menuIconEdit);
|
||||
result->addAction(tr::lng_group_invite_context_revoke(tr::now), [=] {
|
||||
delegate()->peerListShowBox(
|
||||
RevokeLinkBox(_peer, _admin, link),
|
||||
Ui::LayerOption::KeepOther);
|
||||
delegate()->peerListShowBox(RevokeLinkBox(_peer, _admin, link));
|
||||
}, &st::menuIconRemove);
|
||||
}
|
||||
return result;
|
||||
@@ -813,8 +805,7 @@ void AdminsController::loadMoreRows() {
|
||||
|
||||
void AdminsController::rowClicked(not_null<PeerListRow*> row) {
|
||||
delegate()->peerListShowBox(
|
||||
Box(ManageInviteLinksBox, _peer, row->peer()->asUser(), 0, 0),
|
||||
Ui::LayerOption::KeepOther);
|
||||
Box(ManageInviteLinksBox, _peer, row->peer()->asUser(), 0, 0));
|
||||
}
|
||||
|
||||
Main::Session &AdminsController::session() const {
|
||||
@@ -836,7 +827,7 @@ struct LinksList {
|
||||
};
|
||||
|
||||
LinksList AddLinksList(
|
||||
std::shared_ptr<Ui::BoxShow> show,
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<PeerData*> peer,
|
||||
not_null<UserData*> admin,
|
||||
@@ -861,7 +852,7 @@ LinksList AddLinksList(
|
||||
}
|
||||
|
||||
not_null<Ui::RpWidget*> AddAdminsList(
|
||||
std::shared_ptr<Ui::BoxShow> show,
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<PeerData*> peer,
|
||||
not_null<UserData*> admin) {
|
||||
@@ -889,7 +880,9 @@ void ManageInviteLinksBox(
|
||||
int revokedCount) {
|
||||
using namespace Settings;
|
||||
|
||||
const auto show = std::make_shared<Ui::BoxShow>(box);
|
||||
const auto show = Main::MakeSessionShow(
|
||||
box->uiShow(),
|
||||
&peer->session());
|
||||
|
||||
box->setTitle(tr::lng_group_invite_title());
|
||||
box->setWidth(st::boxWideWidth);
|
||||
@@ -925,8 +918,7 @@ void ManageInviteLinksBox(
|
||||
const auto add = AddCreateLinkButton(container);
|
||||
add->setClickedCallback([=] {
|
||||
show->showBox(
|
||||
EditLinkBox(peer, InviteLinkData{ .admin = admin }),
|
||||
Ui::LayerOption::KeepOther);
|
||||
EditLinkBox(peer, InviteLinkData{ .admin = admin }));
|
||||
});
|
||||
} else {
|
||||
otherHeader = container->add(object_ptr<Ui::SlideWrap<>>(
|
||||
@@ -1006,8 +998,8 @@ void ManageInviteLinksBox(
|
||||
top + st::inviteLinkRevokedTitlePadding.top(),
|
||||
outerWidth);
|
||||
}, deleteAll->lifetime());
|
||||
deleteAll->setClickedCallback([=, show = Ui::BoxShow(box)] {
|
||||
show.showBox(DeleteAllRevokedBox(peer, admin));
|
||||
deleteAll->setClickedCallback([=, show = box->uiShow()] {
|
||||
show->showBox(DeleteAllRevokedBox(peer, admin));
|
||||
});
|
||||
|
||||
rpl::combine(
|
||||
|
||||
@@ -21,7 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/widgets/box_content_divider.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "boxes/peers/edit_participants_box.h"
|
||||
@@ -417,10 +417,7 @@ not_null<Ui::RpWidget*> AddInnerToggle(
|
||||
|
||||
const auto handleLocked = [=] {
|
||||
if (locked.has_value()) {
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = container,
|
||||
.text = { *locked },
|
||||
});
|
||||
Ui::Toast::Show(container, *locked);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -597,8 +594,7 @@ template <typename Flags>
|
||||
) | rpl::start_with_next([=](bool checked) {
|
||||
if (checked && state->forceDisabled.current()) {
|
||||
if (!state->toast) {
|
||||
state->toast = Ui::ShowMultilineToast({
|
||||
.parentOverride = container,
|
||||
state->toast = Ui::Toast::Show(container, {
|
||||
.text = { state->forceDisabledMessage.current() },
|
||||
.duration = kForceDisableTooltipDuration,
|
||||
});
|
||||
@@ -607,8 +603,7 @@ template <typename Flags>
|
||||
} else if (locked.has_value()) {
|
||||
if (checked != toggled) {
|
||||
if (!state->toast) {
|
||||
state->toast = Ui::ShowMultilineToast({
|
||||
.parentOverride = container,
|
||||
state->toast = Ui::Toast::Show(container, {
|
||||
.text = { *locked },
|
||||
.duration = kForceDisableTooltipDuration,
|
||||
});
|
||||
@@ -970,23 +965,20 @@ Fn<void()> AboutGigagroupCallback(
|
||||
channel->inputChannel
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
channel->session().api().applyUpdates(result);
|
||||
if (const auto strongController = weak.get()) {
|
||||
strongController->window().hideSettingsAndLayer();
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = strongController->widget(),
|
||||
.text = { tr::lng_gigagroup_done(tr::now) },
|
||||
});
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->window().hideSettingsAndLayer();
|
||||
strong->showToast(tr::lng_gigagroup_done(tr::now));
|
||||
}
|
||||
}).fail([=] {
|
||||
*converting = false;
|
||||
}).send();
|
||||
};
|
||||
const auto convertWarn = [=] {
|
||||
const auto strongController = weak.get();
|
||||
if (*converting || !strongController) {
|
||||
const auto strong = weak.get();
|
||||
if (*converting || !strong) {
|
||||
return;
|
||||
}
|
||||
strongController->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
strong->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
box->setTitle(tr::lng_gigagroup_warning_title());
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
@@ -996,14 +988,14 @@ Fn<void()> AboutGigagroupCallback(
|
||||
st::infoAboutGigagroup));
|
||||
box->addButton(tr::lng_gigagroup_convert_sure(), convertSure);
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}), Ui::LayerOption::KeepOther);
|
||||
}));
|
||||
};
|
||||
return [=] {
|
||||
const auto strongController = weak.get();
|
||||
if (*converting || !strongController) {
|
||||
const auto strong = weak.get();
|
||||
if (*converting || !strong) {
|
||||
return;
|
||||
}
|
||||
strongController->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
strong->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
box->setTitle(tr::lng_gigagroup_convert_title());
|
||||
const auto addFeature = [&](rpl::producer<QString> text) {
|
||||
using namespace rpl::mappers;
|
||||
@@ -1024,7 +1016,7 @@ Fn<void()> AboutGigagroupCallback(
|
||||
addFeature(tr::lng_gigagroup_convert_feature3());
|
||||
box->addButton(tr::lng_gigagroup_convert_sure(), convertWarn);
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}), Ui::LayerOption::KeepOther);
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "ui/painter.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "window/window_session_controller.h"
|
||||
@@ -382,16 +381,13 @@ void RequestsBoxController::processRequest(
|
||||
const auto done = crl::guard(this, [=] {
|
||||
remove();
|
||||
if (approved) {
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = delegate()->peerListToastParent(),
|
||||
.text = (_peer->isBroadcast()
|
||||
? tr::lng_group_requests_was_added_channel
|
||||
: tr::lng_group_requests_was_added)(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Bold(user->name()),
|
||||
Ui::Text::WithEntities)
|
||||
});
|
||||
delegate()->peerListUiShow()->showToast((_peer->isBroadcast()
|
||||
? tr::lng_group_requests_was_added_channel
|
||||
: tr::lng_group_requests_was_added)(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Bold(user->name()),
|
||||
Ui::Text::WithEntities));
|
||||
}
|
||||
});
|
||||
const auto fail = crl::guard(this, remove);
|
||||
|
||||
@@ -52,7 +52,7 @@ class Controller : public base::has_weak_ptr {
|
||||
public:
|
||||
Controller(
|
||||
Window::SessionNavigation *navigation,
|
||||
std::shared_ptr<Ui::BoxShow> show,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<PeerData*> peer,
|
||||
bool useLocationPhrases,
|
||||
@@ -140,7 +140,7 @@ private:
|
||||
rpl::producer<QString> about);
|
||||
|
||||
Window::SessionNavigation *_navigation = nullptr;
|
||||
std::shared_ptr<Ui::BoxShow> _show;
|
||||
std::shared_ptr<Ui::Show> _show;
|
||||
|
||||
not_null<PeerData*> _peer;
|
||||
bool _linkOnly = false;
|
||||
@@ -168,7 +168,7 @@ private:
|
||||
|
||||
Controller::Controller(
|
||||
Window::SessionNavigation *navigation,
|
||||
std::shared_ptr<Ui::BoxShow> show,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<PeerData*> peer,
|
||||
bool useLocationPhrases,
|
||||
@@ -601,9 +601,7 @@ void Controller::askUsernameRevoke() {
|
||||
_controls.privacy->setValue(Privacy::HasUsername);
|
||||
checkUsernameAvailability();
|
||||
});
|
||||
_show->showBox(
|
||||
Box(PublicLinksLimitBox, _navigation, revokeCallback),
|
||||
Ui::LayerOption::KeepOther);
|
||||
_show->showBox(Box(PublicLinksLimitBox, _navigation, revokeCallback));
|
||||
}
|
||||
|
||||
void Controller::usernameChanged() {
|
||||
@@ -735,7 +733,7 @@ void EditPeerTypeBox::prepare() {
|
||||
const auto controller = Ui::CreateChild<Controller>(
|
||||
this,
|
||||
_navigation,
|
||||
std::make_shared<Ui::BoxShow>(this),
|
||||
uiShow(),
|
||||
content.data(),
|
||||
_peer,
|
||||
_useLocationPhrases,
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "apiwrap.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_user.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings/settings_common.h"
|
||||
@@ -64,6 +65,7 @@ public:
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const Data::Username &data,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
QString status,
|
||||
QString link);
|
||||
|
||||
[[nodiscard]] const Data::Username &username() const;
|
||||
@@ -90,15 +92,12 @@ UsernamesList::Row::Row(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const Data::Username &data,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
QString status,
|
||||
QString link)
|
||||
: Ui::SettingsButton(parent, rpl::never<QString>())
|
||||
, _st(st::inviteLinkListItem)
|
||||
, _data(data)
|
||||
, _status(data.editable
|
||||
? tr::lng_usernames_edit(tr::now)
|
||||
: data.active
|
||||
? tr::lng_usernames_active(tr::now)
|
||||
: tr::lng_usernames_non_active(tr::now))
|
||||
, _status(std::move(status))
|
||||
, _rightAction(Ui::CreateChild<RightAction>(this))
|
||||
, _iconRect(
|
||||
_st.photoPosition.x() + st::inviteLinkIconSkip,
|
||||
@@ -118,8 +117,7 @@ UsernamesList::Row::Row(
|
||||
tr::lng_group_invite_context_copy(tr::now),
|
||||
[=] {
|
||||
QGuiApplication::clipboard()->setText(link);
|
||||
Ui::Toast::Show(
|
||||
show->toastParent(),
|
||||
show->showToast(
|
||||
tr::lng_create_channel_link_copied(tr::now));
|
||||
},
|
||||
&st::menuIconCopy);
|
||||
@@ -197,6 +195,9 @@ UsernamesList::UsernamesList(
|
||||
: RpWidget(parent)
|
||||
, _show(show)
|
||||
, _peer(peer)
|
||||
, _isBot(peer->isUser()
|
||||
&& peer->asUser()->botInfo
|
||||
&& peer->asUser()->botInfo->canEditInformation)
|
||||
, _focusCallback(std::move(focusCallback)) {
|
||||
{
|
||||
auto &api = _peer->session().api();
|
||||
@@ -246,16 +247,24 @@ void UsernamesList::rebuild(const Data::Usernames &usernames) {
|
||||
for (const auto &username : usernames) {
|
||||
const auto link = _peer->session().createInternalLinkFull(
|
||||
username.username);
|
||||
const auto status = (username.editable && _focusCallback)
|
||||
? tr::lng_usernames_edit(tr::now)
|
||||
: username.active
|
||||
? tr::lng_usernames_active(tr::now)
|
||||
: tr::lng_usernames_non_active(tr::now);
|
||||
const auto row = content->add(
|
||||
object_ptr<Row>(content, username, _show, link));
|
||||
object_ptr<Row>(content, username, _show, status, link));
|
||||
_rows.push_back(row);
|
||||
row->addClickHandler([=] {
|
||||
if (_reordering || (!_peer->isSelf() && !_peer->isChannel())) {
|
||||
if (_reordering
|
||||
|| (!_peer->isSelf() && !_peer->isChannel() && !_isBot)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.editable) {
|
||||
_focusCallback();
|
||||
if (_focusCallback) {
|
||||
_focusCallback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -263,6 +272,10 @@ void UsernamesList::rebuild(const Data::Usernames &usernames) {
|
||||
? (username.active
|
||||
? tr::lng_usernames_deactivate_description()
|
||||
: tr::lng_usernames_activate_description())
|
||||
: _isBot
|
||||
? (username.active
|
||||
? tr::lng_bot_usernames_deactivate_description()
|
||||
: tr::lng_bot_usernames_activate_description())
|
||||
: (username.active
|
||||
? tr::lng_channel_usernames_deactivate_description()
|
||||
: tr::lng_channel_usernames_activate_description());
|
||||
@@ -293,8 +306,7 @@ void UsernamesList::rebuild(const Data::Usernames &usernames) {
|
||||
tr::lng_usernames_activate_error(
|
||||
lt_count,
|
||||
rpl::single(kMaxUsernames),
|
||||
Ui::Text::RichLangValue)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
Ui::Text::RichLangValue)));
|
||||
}
|
||||
load();
|
||||
_toggleLifetime.destroy();
|
||||
@@ -307,9 +319,7 @@ void UsernamesList::rebuild(const Data::Usernames &usernames) {
|
||||
}),
|
||||
.confirmText = std::move(confirmText),
|
||||
};
|
||||
_show->showBox(
|
||||
Ui::MakeConfirmBox(std::move(args)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
_show->showBox(Ui::MakeConfirmBox(std::move(args)));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -359,6 +369,8 @@ void UsernamesList::rebuild(const Data::Usernames &usernames) {
|
||||
_container,
|
||||
_peer->isSelf()
|
||||
? tr::lng_usernames_description()
|
||||
: _isBot
|
||||
? tr::lng_bot_usernames_description()
|
||||
: tr::lng_channel_usernames_description());
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ private:
|
||||
|
||||
const std::shared_ptr<Ui::Show> _show;
|
||||
const not_null<PeerData*> _peer;
|
||||
const bool _isBot = false;
|
||||
Fn<void()> _focusCallback;
|
||||
|
||||
base::unique_qptr<Ui::VerticalLayout> _container;
|
||||
|
||||
@@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
@@ -41,15 +43,17 @@ namespace {
|
||||
|
||||
void PinMessageBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<PeerData*> peer,
|
||||
MsgId msgId) {
|
||||
not_null<HistoryItem*> item) {
|
||||
struct State {
|
||||
QPointer<Ui::Checkbox> pinForPeer;
|
||||
QPointer<Ui::Checkbox> notify;
|
||||
mtpRequestId requestId = 0;
|
||||
};
|
||||
|
||||
const auto pinningOld = IsOldForPin(msgId, peer, MsgId(0));
|
||||
const auto peer = item->history()->peer;
|
||||
const auto msgId = item->id;
|
||||
const auto topicRootId = item->topic() ? item->topicRootId() : MsgId();
|
||||
const auto pinningOld = IsOldForPin(msgId, peer, topicRootId);
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
const auto api = box->lifetime().make_state<MTP::Sender>(
|
||||
&peer->session().mtp());
|
||||
|
||||
@@ -13,5 +13,4 @@ class GenericBox;
|
||||
|
||||
void PinMessageBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<PeerData*> peer,
|
||||
MsgId msgId);
|
||||
not_null<HistoryItem*> item);
|
||||
|
||||
@@ -13,7 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_domain.h"
|
||||
@@ -46,6 +45,7 @@ struct InfographicDescriptor {
|
||||
float64 premiumLimit = 0;
|
||||
const style::icon *icon;
|
||||
std::optional<tr::phrase<lngtag_count>> phrase;
|
||||
bool complexRatio = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] rpl::producer<> BoxShowFinishes(not_null<Ui::GenericBox*> box) {
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
object_ptr<Ui::BoxContent> content,
|
||||
Ui::LayerOptions options = Ui::LayerOption::KeepOther) override;
|
||||
void peerListHideLayer() override;
|
||||
not_null<QWidget*> peerListToastParent() override;
|
||||
std::shared_ptr<Main::SessionShow> peerListUiShow() override;
|
||||
void peerListSetRowChecked(
|
||||
not_null<PeerListRow*> row,
|
||||
bool checked) override;
|
||||
@@ -204,8 +204,8 @@ void InactiveDelegate::peerListShowBox(
|
||||
void InactiveDelegate::peerListHideLayer() {
|
||||
}
|
||||
|
||||
not_null<QWidget*> InactiveDelegate::peerListToastParent() {
|
||||
Unexpected("...InactiveDelegate::peerListToastParent");
|
||||
std::shared_ptr<Main::SessionShow> InactiveDelegate::peerListUiShow() {
|
||||
Unexpected("...InactiveDelegate::peerListUiShow");
|
||||
}
|
||||
|
||||
rpl::producer<int> InactiveDelegate::selectedCountChanges() const {
|
||||
@@ -384,8 +384,7 @@ void PublicsController::rowRightActionClicked(not_null<PeerListRow*> row) {
|
||||
.text = text,
|
||||
.confirmed = std::move(callback),
|
||||
.confirmText = confirmText,
|
||||
}),
|
||||
Ui::LayerOption::KeepOther);
|
||||
}));
|
||||
}
|
||||
|
||||
void PublicsController::appendRow(not_null<PeerData*> participant) {
|
||||
@@ -433,7 +432,11 @@ void SimpleLimitBox(
|
||||
Ui::Premium::AddLimitRow(
|
||||
top,
|
||||
descriptor.premiumLimit,
|
||||
descriptor.phrase);
|
||||
descriptor.phrase,
|
||||
0,
|
||||
(descriptor.complexRatio
|
||||
? (float64(descriptor.current) / descriptor.premiumLimit)
|
||||
: Ui::Premium::kLimitRowRatio));
|
||||
Settings::AddSkip(top, st::premiumInfographicPadding.bottom());
|
||||
}
|
||||
|
||||
@@ -597,10 +600,7 @@ void ChannelsLimitBox(
|
||||
session->api().leaveChannel(channel);
|
||||
}
|
||||
}
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = Ui::BoxShow(box).toastParent(),
|
||||
.text = { tr::lng_channels_leave_done(tr::now) },
|
||||
});
|
||||
box->showToast(tr::lng_channels_leave_done(tr::now));
|
||||
box->closeBox();
|
||||
};
|
||||
box->clearButtons();
|
||||
@@ -723,6 +723,50 @@ void FilterChatsLimitBox(
|
||||
{ defaultLimit, current, premiumLimit, &st::premiumIconChats });
|
||||
}
|
||||
|
||||
void FilterLinksLimitBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session) {
|
||||
const auto premium = session->premium();
|
||||
const auto premiumPossible = session->premiumPossible();
|
||||
|
||||
const auto limits = Data::PremiumLimits(session);
|
||||
const auto defaultLimit = float64(limits.dialogFiltersLinksDefault());
|
||||
const auto premiumLimit = float64(limits.dialogFiltersLinksPremium());
|
||||
const auto current = (premium ? premiumLimit : defaultLimit);
|
||||
|
||||
auto text = rpl::combine(
|
||||
tr::lng_filter_links_limit1(
|
||||
lt_count,
|
||||
rpl::single(premium ? premiumLimit : defaultLimit),
|
||||
Ui::Text::RichLangValue),
|
||||
((premium || !premiumPossible)
|
||||
? rpl::single(TextWithEntities())
|
||||
: tr::lng_filter_links_limit2(
|
||||
lt_count,
|
||||
rpl::single(premiumLimit),
|
||||
Ui::Text::RichLangValue))
|
||||
) | rpl::map([](TextWithEntities &&a, TextWithEntities &&b) {
|
||||
return b.text.isEmpty()
|
||||
? a
|
||||
: a.append(QChar(' ')).append(std::move(b));
|
||||
});
|
||||
|
||||
SimpleLimitBox(
|
||||
box,
|
||||
session,
|
||||
tr::lng_filter_links_limit_title(),
|
||||
std::move(text),
|
||||
"chatlist_invites",
|
||||
{
|
||||
defaultLimit,
|
||||
current,
|
||||
premiumLimit,
|
||||
&st::premiumIconChats,
|
||||
std::nullopt,
|
||||
true });
|
||||
}
|
||||
|
||||
|
||||
void FiltersLimitBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session) {
|
||||
@@ -761,6 +805,50 @@ void FiltersLimitBox(
|
||||
{ defaultLimit, current, premiumLimit, &st::premiumIconFolders });
|
||||
}
|
||||
|
||||
void ShareableFiltersLimitBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session) {
|
||||
const auto premium = session->premium();
|
||||
const auto premiumPossible = session->premiumPossible();
|
||||
|
||||
const auto limits = Data::PremiumLimits(session);
|
||||
const auto defaultLimit = float64(limits.dialogShareableFiltersDefault());
|
||||
const auto premiumLimit = float64(limits.dialogShareableFiltersPremium());
|
||||
const auto current = float64(ranges::count_if(
|
||||
session->data().chatsFilters().list(),
|
||||
[](const Data::ChatFilter &f) { return f.chatlist(); }));
|
||||
|
||||
auto text = rpl::combine(
|
||||
tr::lng_filter_shared_limit1(
|
||||
lt_count,
|
||||
rpl::single(premium ? premiumLimit : defaultLimit),
|
||||
Ui::Text::RichLangValue),
|
||||
((premium || !premiumPossible)
|
||||
? rpl::single(TextWithEntities())
|
||||
: tr::lng_filter_shared_limit2(
|
||||
lt_count,
|
||||
rpl::single(premiumLimit),
|
||||
Ui::Text::RichLangValue))
|
||||
) | rpl::map([](TextWithEntities &&a, TextWithEntities &&b) {
|
||||
return b.text.isEmpty()
|
||||
? a
|
||||
: a.append(QChar(' ')).append(std::move(b));
|
||||
});
|
||||
SimpleLimitBox(
|
||||
box,
|
||||
session,
|
||||
tr::lng_filter_shared_limit_title(),
|
||||
std::move(text),
|
||||
"chatlists_joined",
|
||||
{
|
||||
defaultLimit,
|
||||
current,
|
||||
premiumLimit,
|
||||
&st::premiumIconFolders,
|
||||
std::nullopt,
|
||||
true });
|
||||
}
|
||||
|
||||
void FilterPinsLimitBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session,
|
||||
|
||||
@@ -32,9 +32,15 @@ void FilterChatsLimitBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session,
|
||||
int currentCount);
|
||||
void FilterLinksLimitBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session);
|
||||
void FiltersLimitBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session);
|
||||
void ShareableFiltersLimitBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session);
|
||||
void FilterPinsLimitBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session,
|
||||
|
||||
@@ -27,7 +27,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/effects/scroll_content_shadow.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
@@ -384,7 +383,7 @@ void AddReactionAnimatedIcon(
|
||||
|
||||
const auto paintCallback = [=](not_null<QWidget*> widget, QPainter &p) {
|
||||
const auto paintFrame = [&](not_null<Ui::AnimatedIcon*> animation) {
|
||||
const auto frame = animation->frame();
|
||||
const auto frame = animation->frame(st::windowFg->c);
|
||||
p.drawImage(
|
||||
QRect(
|
||||
(widget->width() - iconSize) / 2,
|
||||
@@ -404,7 +403,6 @@ void AddReactionAnimatedIcon(
|
||||
} else if (const auto select = state->select.icon.get()) {
|
||||
paintFrame(select);
|
||||
}
|
||||
|
||||
};
|
||||
const auto widget = AddReactionIconWrap(
|
||||
parent,
|
||||
|
||||
@@ -39,13 +39,12 @@ namespace {
|
||||
return Ui::ReportSource::Bot;
|
||||
});
|
||||
return Box([=](not_null<Ui::GenericBox*> box) {
|
||||
const auto show = box->uiShow();
|
||||
Ui::ReportReasonBox(box, source, [=](Ui::ReportReason reason) {
|
||||
Ui::BoxShow(box).showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
const auto show = Ui::BoxShow(box);
|
||||
show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
Ui::ReportDetailsBox(box, [=](const QString &text) {
|
||||
const auto toastParent = show.toastParent();
|
||||
Api::SendReport(toastParent, peer, reason, text, data);
|
||||
show.hideLayer();
|
||||
Api::SendReport(show, peer, reason, text, data);
|
||||
show->hideLayer();
|
||||
});
|
||||
}));
|
||||
});
|
||||
@@ -79,7 +78,7 @@ void ShowReportPeerBox(
|
||||
const auto send = [=](const QString &text) {
|
||||
window->clearChooseReportMessages();
|
||||
Api::SendReport(
|
||||
Window::Show(window).toastParent(),
|
||||
window->uiShow(),
|
||||
peer,
|
||||
reason,
|
||||
text,
|
||||
|
||||