Compare commits

...

216 Commits

Author SHA1 Message Date
John Preston
bcf91e2f2c Version 4.8.3: Fix statup crash with passcode. 2023-06-01 11:47:36 +04:00
GitHub Action
1e9087db4a Update User-Agent for DNS to Chrome 113.0.5672.63. 2023-06-01 11:36:47 +04:00
John Preston
c5cb928724 Version 4.8.3.
- Fix main window focus from notifications with disabled animations.
- Some minor fixes and improvements.
2023-05-31 21:50:53 +04:00
John Preston
e1350d6813 Fix webview links opening from webview. 2023-05-31 21:46:32 +04:00
Ilya Fedin
898581badd Update submodules 2023-05-30 20:55:15 +04:00
Ilya Fedin
71570f5be1 Store managed objects inside managers 2023-05-30 20:55:15 +04:00
Ilya Fedin
8c38d31950 C++ify wl_registry 2023-05-30 20:55:15 +04:00
Ilya Fedin
c8407e5bde Update lib_base 2023-05-29 11:07:19 +04:00
Ilya Fedin
2ce9e610fa Update icons when icon theme on Linux changes 2023-05-29 11:07:19 +04:00
Ilya Fedin
6aef6d7f4e Integrate GApplication with QFileOpenEvent and Core::Application::activate 2023-05-29 10:51:40 +04:00
Ilya Fedin
54841de991 Use new base_linux_wayland_utilities 2023-05-25 22:05:13 +04:00
Ilya Fedin
fc66a0eea8 Destroy Wayland integration before Wayland disconnection 2023-05-25 22:05:13 +04:00
Ilya Fedin
e9787170d5 Update Qt to 6.5.1 on Linux 2023-05-25 14:41:02 +04:00
John Preston
d57aa2a1f6 Fix possible crash on MTP::Instance shutdown. 2023-05-25 11:56:09 +04:00
John Preston
2fb7bdc803 Skip refocus InputField::Inner if field unfocused.
I hope this fixes #26223.
2023-05-24 21:22:26 +04:00
John Preston
f671897a4d Fix a possible crash in Go to Message on macOS.
Fixes #26273.

This crash happens with a very deep stack trace that is caused by
destroying shared media layer on Go to Message click, which starts
destroying Media::ListWidget, which destroys Ui::PopupMenu, which is
a window, so when it destructs it triggers a sync repaint of the main
window (by a system event), which tries to render the new messages slice,
which starts a photo download, which calls an item repaint in Data::Session,
and the semi-destroyed Media::ListWidget is still subscribed to the
item repaint requests and tries to repaint and crashes there.

The fix is to destroy all session-related subscriptions before the PopupMenu.
2023-05-24 20:22:41 +04:00
John Preston
7877463468 Respect peer_types in the switch inline button.
Fixes #26274.
2023-05-24 19:36:58 +04:00
John Preston
8a99de16f6 Allow editing messages even if sending is not allowed. 2023-05-24 18:48:48 +04:00
John Preston
f46f655a0e Revert "Disable xcb_xlib"
This reverts commit d1ff6e583d.

Otherwise HIME input is broken:
https://github.com/telegramdesktop/tdesktop/issues/26228
2023-05-24 18:11:36 +04:00
John Preston
04a3a50e74 Show error when trying change number from TDesktop. 2023-05-24 18:11:17 +04:00
John Preston
d349763460 Fix "Remain Anonymous" change in recent actions. 2023-05-24 17:17:38 +04:00
Ilya Fedin
c4b95b40e5 Update lib_webview 2023-05-24 17:10:39 +04:00
Ilya Fedin
0fa2f83cf8 Fix gobject-introspection base in Dockerfile
It doesn't use patches
2023-05-24 17:10:39 +04:00
John Preston
abc32c63b0 Force-hide media viewer on passcode lock. 2023-05-24 16:58:34 +04:00
John Preston
8ae373b654 Update patches with a commit revert. 2023-05-24 13:23:01 +04:00
John Preston
b6395d08d8 Remove streaming channel from live stream "participants". 2023-05-24 13:20:13 +04:00
John Preston
ad573ecc84 Fix a possible crash in Large Emoji toggling. 2023-05-24 12:50:54 +04:00
Vedant
1d2a50c407 Fix installer selection for winget-releaser 2023-05-21 20:10:22 +04:00
Ilya Fedin
082ffb6cd1 Update submodules 2023-05-19 15:33:43 +04:00
Ilya Fedin
9514b0d1f1 Get rid of unneeded gi::result wrap 2023-05-19 15:33:43 +04:00
Ilya Fedin
2ea50f5c85 Constify options in Linux screen locker monitor
That's possible now thanks to cppgir update
2023-05-19 15:33:43 +04:00
GitHub Action
3eca5d206b Update User-Agent for DNS to Chrome 112.0.5615.165. 2023-05-19 15:31:10 +04:00
Ilya Fedin
862f4822f2 Don't drop default LDFLAGS in snap 2023-05-18 18:15:40 +04:00
Ilya Fedin
6dff1f11ba Fix .gir file generation in snap 2023-05-18 15:49:15 +04:00
John Preston
90b5c6a582 Version 4.8.2: Hardened Runtime in Mac App Store. 2023-05-18 11:54:56 +04:00
Ilya Fedin
e05b813b6e Fix gobject-inrospection dependency in snap
Debian's weird package naming goes on
2023-05-17 17:59:47 +04:00
John Preston
e8a4a7b754 Some build fixes. 2023-05-17 16:29:31 +04:00
John Preston
d909248e25 Update lib_ui. 2023-05-17 15:55:26 +04:00
John Preston
65afa2c402 Detach ComposeControls from SessionController. 2023-05-17 15:51:52 +04:00
Ilya Fedin
665467b02d Add new dependencies to snap build 2023-05-17 15:38:49 +04:00
Ilya Fedin
787cab7417 Update submodules 2023-05-17 15:38:34 +04:00
Ilya Fedin
b98b44e638 Downgrade to the working revision 2023-05-15 12:34:56 +04:00
Ilya Fedin
d1455f5117 Switch Wayland integration to the new type safe Qt API
This also makes object lifetime tracking better thanks to the new API
2023-05-15 12:34:56 +04:00
Ilya Fedin
0ffaff2d8b Support Qt 6.5 dark mode API 2023-05-15 12:33:30 +04:00
Ilya Fedin
2675b5df3b Use QGuiApplication::setBadgeNumber API since Qt 6.6 on Linux 2023-05-15 10:20:18 +04:00
Ilya Fedin
627c870dd5 Get rid of qt5compat dependency 2023-05-15 10:14:52 +04:00
Ilya Fedin
db60bee7dc Implement screen locker monitor on Linux 2023-05-15 09:51:13 +04:00
Ilya Fedin
2ff341b7d3 Update submodules 2023-05-15 09:50:28 +04:00
Ilya Fedin
bb008911b0 Get rid of SystemMediaControls::setServiceName usage 2023-05-15 09:50:28 +04:00
Ilya Fedin
0ca9bbafad Update cmake on Linux 2023-05-15 09:50:28 +04:00
Ilya Fedin
0ca983ed71 Add cppgir dependencies to Dockerfile 2023-05-15 09:50:28 +04:00
Ilya Fedin
0b4ebcbae4 Warn if incompatible event loop is detected 2023-05-15 08:08:04 +04:00
Ilya Fedin
0a011db483 Get rid of DESKTOP_APP_DISABLE_DBUS_INTEGRATION
Desktop App Toolkit uses GLib as the D-Bus library for quite long time, but GLib is not only a D-Bus library, it's more a basic library providing native Linux APIs implementing various specs. The situation right now is that DESKTOP_APP_DISABLE_DBUS_INTEGRATION disables not only D-Bus code but all the native API integration such as MIME handling or .desktop file parsing. In other words, the option disables native Linux APIs on Linux what is absurd and doesn't have any sense.
2023-05-15 08:08:04 +04:00
Ilya Fedin
37f5576c38 Get rid of -Ofast on Linux
This seem to make some miscompilation that lead to hangs
2023-05-03 23:49:38 +04:00
John Preston
b881d24a5a Update libyuv in tg_owt. 2023-05-01 11:08:24 +04:00
Ilya Fedin
4628d4fece Update submodules 2023-04-30 12:31:14 +04:00
Ilya Fedin
68e229640b Revert "Build OpenSSL 1.1.1 in snap"
This reverts commit 0e97ef992e.
2023-04-30 12:31:14 +04:00
Ilya Fedin
1ac0c4142d Update tg_owt in snap 2023-04-30 12:31:14 +04:00
John Preston
61a61669b6 Fix build with Xcode. 2023-04-28 22:03:42 +04:00
John Preston
9b576a13bc Fix calls with OpenSSL 3.
For that libsrtp was updated to v2.5.0 in tg_owt.
2023-04-28 14:40:53 +04:00
John Preston
60fe961c21 Implement new client hello generation. 2023-04-27 21:11:03 +04:00
John Preston
c890281258 Fix emoji in macOS title bar text. 2023-04-27 15:23:37 +04:00
John Preston
9615347634 This should allow calling another authed account. 2023-04-27 15:23:36 +04:00
John Preston
864959aee0 Hide login code with a spoiler in chats list. 2023-04-27 15:23:36 +04:00
Klemens Nanni
11906297d8 Explicitly specify QVector element type to fix build with clang13+range-v3+qt6
Same story as 2b383a4 for #24014.
Fixes 4.8.0 build on OpenBSD/amd64 7.3 with clang 13.0.0, range-v3 0.12.0 and Qt 6.5.0.
2023-04-27 09:59:32 +04:00
Ilya Fedin
3825586715 Update lib_base 2023-04-25 08:29:07 +04:00
Ilya Fedin
cc6f63edf4 Downgrade wl_seat version in snap
To avoid high-resolution scrolling events
2023-04-25 08:29:07 +04:00
John Preston
3da787791f Version 4.8.1.
- Fix sending an album of ten scheduled messages.
- Convert folder back to non-shareable on last link deletion.
- Several fixes of focus control in discussions / channel comments.
- Drop all formatting on paste in non-message input fields.
- Clear search field on archive opening.
- Show video upload / download progress over the spoiler.
- Better support for text-colored emoji in reactions.
- Close archive / topics group when clicking on currently active folder.
- Allow replace media by paste in discussions / comments / scheduled messages.
- Fix stuck Replace Media button after editing media in some chats.
- Quick reply by double click only with the left mouse button.
- Fix applying the same custom chat background with different dimming value.
- Fix "who reacted" list display for media album parts.
- Show full t.me/+ invite link prefix in Recent Actions.
- Don't try to suggest userpic photos to bots.
- Add "Send when online" to the send button context menu.
- Fix crash in empty topics message sending.
2023-04-24 22:52:26 +04:00
John Preston
ad238108bd Fix background dimming slider initial visibility. 2023-04-24 22:50:36 +04:00
John Preston
81f40586a3 Don't re-apply exactly the same custom chat wallpaper. 2023-04-24 22:47:31 +04:00
John Preston
40deda1e9b Divide groupId of scheduled / normal messages. 2023-04-24 22:31:38 +04:00
John Preston
2d50e893b5 Update lib_base with wl_display_roundtrip. 2023-04-24 21:48:24 +04:00
John Preston
5ad4719c08 Correctly handle shareable->non-shareable conversion. 2023-04-24 21:39:21 +04:00
John Preston
f0acc9526e Fix compose controls focus checks. 2023-04-24 21:32:44 +04:00
John Preston
d938d91366 Drop all formatting by default in InputField. 2023-04-24 21:25:27 +04:00
John Preston
0b60985966 Clear search field on archive opening. 2023-04-24 21:15:40 +04:00
John Preston
b6fbdd25a0 Show video radial above the spoiler. 2023-04-24 21:12:52 +04:00
John Preston
faf6352a11 Support colorized emoji in reactions inline dropdown. 2023-04-24 20:59:19 +04:00
John Preston
4cfa486d91 Support colorized emoji in reactions fly animation. 2023-04-24 14:56:10 +04:00
John Preston
2dea8941a5 Return focus to input field on ListWidget selection clear. 2023-04-23 20:40:59 +04:00
John Preston
9c7fee0bfe Add .m4v as a video file format. 2023-04-23 20:32:00 +04:00
John Preston
3394094dd3 Close archive / forum on repeated click on current filter. 2023-04-23 20:08:53 +04:00
John Preston
58f735e19b Allow replace media by paste in replies/scheduled. 2023-04-23 19:24:03 +04:00
John Preston
3a8237f03a Fix staying _replaceMedia button after media edit. 2023-04-23 19:23:41 +04:00
John Preston
95174a5f36 Quick reply only by left mouse button. 2023-04-23 19:23:16 +04:00
John Preston
b569078e96 Make dimming intensity part of the cache key. 2023-04-23 18:51:47 +04:00
John Preston
33e66d21bd Force dst path creation on Save Selected. 2023-04-23 18:44:52 +04:00
John Preston
480c109b09 Request WhoRead/Reacted only for group leader. 2023-04-23 18:41:33 +04:00
John Preston
4eefebc96c Fix possible assertion violation in non-self reaction. 2023-04-23 18:41:14 +04:00
John Preston
1697e9e791 Fix a regression in accounts switching. 2023-04-23 18:21:31 +04:00
John Preston
8fe2ec63b7 Show t.me/+ invite link prefix in Recent Actions. 2023-04-23 18:09:10 +04:00
John Preston
c1a63164c0 Make count-depended button texts in filter links. 2023-04-23 18:09:10 +04:00
John Preston
cb1041a289 Fix item view refresh in comments / discussions. 2023-04-22 23:04:47 +04:00
John Preston
67fa4372aa Don't suggest userpic photos to bots. 2023-04-22 22:38:43 +04:00
John Preston
a25005483a Fix sending-when-online for already-online user. 2023-04-22 22:33:51 +04:00
John Preston
e285b22398 Add "Send when online" to the send button context menu. 2023-04-22 22:26:09 +04:00
John Preston
4201a0193c Add additional assertion for OOM detection. 2023-04-22 21:35:06 +04:00
John Preston
dffe79fea8 Fix possible crash in nested section show. 2023-04-22 21:20:10 +04:00
John Preston
454c8db00d Fix crash in empty topics message sending. 2023-04-22 21:09:46 +04:00
John Preston
27aa678f6a Version 4.8: Fix build with GCC. 2023-04-22 16:09:52 +04:00
John Preston
f361dd0df2 Fix initial device selection in calls. 2023-04-21 16:50:22 +04:00
John Preston
f4f5139eb8 Version 4.8.
- Share folders that include dozens of chats with friends or colleagues.
- Anyone you invite can add the folder and join all its chats in one tap.
- You can create multiple invite links to give different people access
to different chats in the folder.
- Set a custom wallpaper for any 1-on-1 chat.
- Your chat partner can apply the same wallpaper - or choose their own.
2023-04-21 16:39:01 +04:00
John Preston
976c32e5af Fill isTcp field in calls reflector descriptors. 2023-04-21 15:49:37 +04:00
23rd
2e85f7e5fc Fixed bottom padding below description field in bot management box. 2023-04-21 14:39:43 +03:00
John Preston
3e8b1d9663 Fix focus loosing in RepliesWidget with JoinGroup button. 2023-04-21 15:14:50 +04:00
John Preston
05b8b9f22e Show pin confirmation correctly in topics. 2023-04-21 15:09:06 +04:00
John Preston
b782569faf Handle escape before messages are loaded.
Fixes #4111.
2023-04-21 14:38:21 +04:00
John Preston
7dfed2a012 Disable some bot editing items while there is no design. 2023-04-21 14:23:54 +04:00
John Preston
64125f0cc8 Update patches revision in docker. 2023-04-21 14:16:19 +04:00
John Preston
8a64a9b2ad Close forward options box by Enter. 2023-04-21 14:15:19 +04:00
John Preston
0b5038aaa2 Fix t.me channel comment links opening.
Regression was introduced in af51307aa6.
2023-04-21 14:10:49 +04:00
John Preston
d37666e91a Fix sharing system audio in video chat on Windows.
Fixes #26107.
2023-04-21 13:42:12 +04:00
John Preston
458082c738 Add correct icons for dark/light mode switch. 2023-04-21 13:42:06 +04:00
23rd
383b100fc7 Fixed changing speed of video in windowed media viewer. 2023-04-21 01:34:05 +03:00
23rd
f4a02b126d Improved format of ttl phrases. 2023-04-21 01:21:08 +03:00
23rd
47756fb8c3 Removed focus from disabled editor in UsernamesBox for bots. 2023-04-21 00:23:49 +03:00
23rd
efa19d5782 Added check of bot info with correct lang code to bot management box. 2023-04-21 00:13:11 +03:00
John Preston
66e7f1d490 Fix dimming edition for new wallpapers. 2023-04-20 23:05:54 +04:00
John Preston
2b2d190d2b Closed alpha version 4.7.1.3. 2023-04-20 23:03:34 +04:00
John Preston
4617ba5fb9 Add couple of phrases to the langpack. 2023-04-20 23:02:33 +04:00
John Preston
65f54d937f Toggle wallpaper dark mode / edit dimming. 2023-04-20 22:55:42 +04:00
John Preston
ac57d46f30 Try allowing correct   handling. 2023-04-20 13:36:48 +04:00
John Preston
0d6e5eda0c Allow larger PiP window size. 2023-04-20 12:55:01 +04:00
John Preston
32c7964e8c Fix video thumbnail in PiP raster renderer. 2023-04-20 12:49:44 +04:00
John Preston
a27ef55ff8 Improve chat theme selector design. 2023-04-20 11:55:23 +04:00
John Preston
1b6b0c1732 Show blur / dimming in wallpaper preview. 2023-04-19 12:14:39 +04:00
John Preston
ae6d703a44 Always show current custom wallpaper. 2023-04-19 11:15:50 +04:00
John Preston
0b25d19e3b Allow uploading chat wallpapers from file. 2023-04-19 10:42:01 +04:00
John Preston
9130735ed6 Add a 'Choose from file' button in wallpapers gallery. 2023-04-18 22:07:12 +04:00
John Preston
da7d4687ca Drop custom wallpaper on theme change. 2023-04-18 22:06:53 +04:00
John Preston
352ae5100a Support chat wallpaper set from gallery. 2023-04-18 16:47:31 +04:00
John Preston
9b25973b49 Update chat wallpaper from service message. 2023-04-17 16:36:13 +04:00
John Preston
bf27185feb Show custom chat wallpapers in chats. 2023-04-17 16:35:50 +04:00
John Preston
5cbf9a2dc4 Show background preview in service messages. 2023-04-14 13:52:49 +04:00
John Preston
d0e8802b9d Allow who viewed lists in topic groups. 2023-04-11 20:54:56 +04:00
John Preston
ad3ec244e9 Fix pasting file for media replacement. 2023-04-11 20:06:38 +04:00
John Preston
22ca4e25d9 Fix replace media button non-disappearance. 2023-04-11 19:18:54 +04:00
23rd
a02ded6fd8 Disabled editor in UsernamesBox for bots. 2023-04-11 19:17:42 +04:00
23rd
ca2a0d41c9 Moved some app config values to separated file. 2023-04-11 19:17:42 +04:00
23rd
30a7893afe Added initial implementation of bot management box. 2023-04-11 19:17:42 +04:00
23rd
cfc8820bad Added support of non-self users to UsernamesBox. 2023-04-11 19:17:42 +04:00
23rd
c53b26dec8 Added ability to provide empty callback to list of usernames management. 2023-04-11 19:17:42 +04:00
23rd
973dd5c50f Added support of bots to list of usernames management. 2023-04-11 19:17:42 +04:00
23rd
896bbb7c56 Added api support to toggle and reorder bot usernames. 2023-04-11 19:17:42 +04:00
23rd
bbeefaed9c Added api support to upload bot photo. 2023-04-11 19:17:42 +04:00
23rd
2276a4c9af Added menu item to bot info for bot management. 2023-04-11 19:17:42 +04:00
23rd
a0ca3beef8 Added phrases for bot management. 2023-04-11 19:17:42 +04:00
23rd
ec2299a7e2 Updated Qt to 5.15.9 on Windows. 2023-04-11 19:17:42 +04:00
John Preston
6eb904acb1 Closed alpha version 4.7.1.2. 2023-04-11 19:17:42 +04:00
John Preston
7ed020ecc5 Use new caption edit in ComposeControls. 2023-04-11 19:17:42 +04:00
John Preston
42c96b4c7f Edit media captions in message field. 2023-04-11 19:17:42 +04:00
John Preston
e3f2dcec22 Fix userpic glitch in folder chats lists. 2023-04-11 19:17:42 +04:00
John Preston
c3f20c59b5 Update API scheme on layer 158. 2023-04-11 19:17:39 +04:00
John Preston
2a06182d1a Closed alpha version 4007001001: Fix build. 2023-04-11 19:17:39 +04:00
John Preston
004a60ded5 Fix a possible crash in folder link export. 2023-04-11 19:17:39 +04:00
John Preston
b4a588a676 Update API scheme on layer 158. 2023-04-11 19:17:39 +04:00
John Preston
2f261e6f7b A bit more error handling. 2023-04-11 19:17:39 +04:00
23rd
cda4bca190 Added support of left edge to corner tail of bubble in premium box. 2023-04-11 19:17:39 +04:00
23rd
127bafa254 Fixed closing of main menu on activate account in separated window. 2023-04-11 19:17:39 +04:00
John Preston
90cea58d28 Closed alpha version 4.7.1.1. 2023-04-11 19:17:39 +04:00
John Preston
7c11fd58cf Make whole same-background message clickable. 2023-04-11 19:17:39 +04:00
John Preston
9600cc0ed5 Don't suggest joining forbidden chats by link. 2023-04-11 19:17:39 +04:00
John Preston
959348f8cd Improve error messages for shareable folders. 2023-04-11 19:17:39 +04:00
John Preston
1a1fa5db3e Implement complex filter delete from context menu. 2023-04-11 19:17:39 +04:00
John Preston
6f4eef035d Don't jump in filters list on list change. 2023-04-11 19:17:39 +04:00
John Preston
e351ad1f3d Show only channels in suggest-leave box. 2023-04-11 19:17:39 +04:00
John Preston
292e5bc3f7 Improve filters save-on-demand. 2023-04-11 19:17:39 +04:00
John Preston
06cf2b562f Improve layout of shareable folder limit boxes. 2023-04-11 19:17:38 +04:00
John Preston
09de881036 Improve peer list decorate widgets. 2023-04-11 19:17:38 +04:00
John Preston
1ffbc122e1 Allow select/deselect all in filter link boxes. 2023-04-11 19:17:38 +04:00
John Preston
f05f1f4359 Change the format for shared filters link. 2023-04-11 19:17:38 +04:00
John Preston
7ff0659e91 Improve scrolling limits in chat select boxes. 2023-04-11 19:17:38 +04:00
John Preston
9b1c5b1050 Support new limit boxes for shareable folders. 2023-04-11 19:17:38 +04:00
John Preston
77939ae9bd Add more-chats-added bar to cloud folders. 2023-04-11 19:17:38 +04:00
John Preston
63960c647b Don't allow sharing public groups with requests in folders. 2023-04-11 19:17:38 +04:00
John Preston
312aa4b130 Update API scheme on layer 158. 2023-04-11 19:17:38 +04:00
John Preston
6be2fb9790 Support nice leaving of shareable folder. 2023-04-11 19:17:38 +04:00
John Preston
4444844443 Update API scheme on layer 158. 2023-04-11 19:17:38 +04:00
John Preston
7a9961b0e9 Improve folder link chats list edit design. 2023-04-11 19:17:38 +04:00
John Preston
0faadc8fa0 Implement folder link add / join design. 2023-04-11 19:17:38 +04:00
John Preston
7684dbc701 Allow "Nobody" in group/channel invites privacy. 2023-04-11 19:17:38 +04:00
John Preston
86f9875662 Add most of the phrases to the langpack. 2023-04-11 19:17:38 +04:00
John Preston
fb16375a19 Delete filter links, edit filter link names. 2023-04-11 19:17:38 +04:00
John Preston
8a9d13c6e4 Initial add filter / chats / edit filter link. 2023-04-11 19:17:37 +04:00
John Preston
b7d9d549ff Start filter share links management. 2023-04-11 19:17:37 +04:00
John Preston
725c22e776 Update API scheme to layer 158. 2023-04-11 19:17:37 +04:00
Ilya Fedin
b2c5c8ae2c Update kcoreaddons 2023-04-11 14:57:57 +04:00
Ilya Fedin
a6e5e7ab84 Tell QLocalSocket the socket is abstract 2023-04-07 12:21:35 +04:00
Ilya Fedin
b2faed6b96 Switch Dockerfile and snap to https for Qt clone 2023-04-06 09:47:41 +04:00
GitHub Action
ec10306ccb Update User-Agent for DNS to Chrome 111.0.5563.110. 2023-04-05 15:41:18 +04:00
Ilya Fedin
590be6d6e7 Fix CounterLayerArgs devicePixelRatio order 2023-04-05 10:17:22 +04:00
Ilya Fedin
8dee2a1c8b Set device pixel ratio for counter layer 2023-04-04 10:07:51 +04:00
Ilya Fedin
c9308d04b8 Don't set no more needed variables for portal support in snap
Both glib 2.76 and Qt 6.5 detect snap automatically now
2023-04-04 08:31:28 +04:00
Ilya Fedin
07791aa2eb Update submodules 2023-04-03 19:44:52 +04:00
Ilya Fedin
c78a15410d Try to remove Wayland blacklist again 2023-04-03 19:44:52 +04:00
Ilya Fedin
dc459d454f Update OpenAL to 1.23.0 on Linux 2023-04-03 13:33:37 +04:00
Ilya Fedin
db04e33128 Update patches on Linux 2023-04-03 13:33:37 +04:00
Ilya Fedin
d1ff6e583d Disable xcb_xlib 2023-04-03 13:33:37 +04:00
Ilya Fedin
a529932556 Update Qt to 6.5.0 on Linux 2023-04-03 13:33:37 +04:00
Ilya Fedin
d52cabb386 Update to the new WebKitGTK 6.0 API 2023-04-02 17:19:15 +04:00
Ilya Fedin
e632ac631e Add version check for abstract sockets 2023-04-02 00:06:04 +04:00
Ilya Fedin
3064a41014 Try to restore updater-dependent binary path logic
Now that the file generating logic is simplier, it may be not that hard to maintain
2023-04-02 00:06:04 +04:00
Ilya Fedin
40e65eb1e6 Fix snap action 2023-04-01 17:41:03 +04:00
Ilya Fedin
30f057fff5 Use abstract socket for single instance on Linux 2023-04-01 00:17:58 +04:00
Ilya Fedin
bebf8e4a03 Update glibmm in snap to 2.76.0 2023-04-01 00:17:58 +04:00
Ilya Fedin
463b628f99 Remove no longer needed hack to build libjxl in snap 2023-04-01 00:17:58 +04:00
Ilya Fedin
e9a92c311b Fix non-first attempts to build snap 2023-04-01 00:17:58 +04:00
Ilya Fedin
4c05da604a Add crash ID field to the bug report template 2023-03-22 13:21:22 +04:00
Ilya Fedin
5da26ffdf8 Explain where to find log.txt 2023-03-22 13:21:22 +04:00
John Preston
66baeb86a1 Specify minimal required glibmm version. 2023-03-22 08:46:34 +04:00
John Preston
e368de008d Version 4.7.1.
- Fix media viewer with some system scale settings on Windows.
- Fix calls on Linux.
2023-03-21 20:28:59 +04:00
John Preston
58c5bb7247 Update online after the window active status.
I hope this fixes #26059.
2023-03-21 16:23:38 +04:00
John Preston
f0a96d0aad Update tgcalls with one more fix. 2023-03-21 16:23:37 +04:00
John Preston
ed3e37b06a Fix calls on Linux.
I hope it fixes #26066.
2023-03-21 12:05:59 +04:00
John Preston
83052ec056 Fix translucent windows in some screen scales.
I hope this fixes #25958, problems with the viewer on Windows.
2023-03-21 09:33:45 +04:00
Ilya Fedin
398237b80e Remove pre-defined title from feature request template
There are labels for this purpose, pre-defined title lets people to not to think about the title what leads to feature requests not having title
2023-03-20 21:54:47 +04:00
375 changed files with 11106 additions and 4219 deletions

View File

@@ -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

View File

@@ -2,7 +2,6 @@
name: Feature request
description: Suggest an idea.
labels: [enhancement]
title: "[Feature Request] "
body:
- type: textarea
attributes:

View File

@@ -57,7 +57,6 @@ jobs:
matrix:
defines:
- ""
- "DESKTOP_APP_DISABLE_DBUS_INTEGRATION"
- "DESKTOP_APP_DISABLE_X11_INTEGRATION"
- "DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION"

View File

@@ -40,7 +40,7 @@ jobs:
snap:
name: Ubuntu
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
env:
UPLOAD_ARTIFACT: "false"

View File

@@ -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
View File

@@ -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

View File

@@ -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()

View File

@@ -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()

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -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 cant 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";

View File

@@ -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>

View 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

View File

@@ -111,6 +111,7 @@ tlsBlockDomain = TlsBlock;
tlsBlockGrease seed:int = TlsBlock;
tlsBlockPublicKey = TlsBlock;
tlsBlockScope entries:Vector<TlsBlock> = TlsBlock;
tlsBlockPermutation entries:Vector<Vector<TlsBlock>> = TlsBlock;
---functions---

View File

@@ -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>

View File

@@ -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"

View File

@@ -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"

View File

@@ -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()) {

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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,

View File

@@ -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(),
});
});
}

View File

@@ -34,6 +34,7 @@ struct PeerInviteLinks {
struct JoinedByLinkUser {
not_null<UserData*> user;
TimeId date = 0;
bool viaFilterLink = false;
};
struct JoinedByLinkSlice {

View File

@@ -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

View File

@@ -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,

View File

@@ -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,

View File

@@ -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(

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -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;

View File

@@ -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()) {

View File

@@ -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;

View File

@@ -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, [=] {

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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() {

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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(

View File

@@ -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)));
}

View File

@@ -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,

View File

@@ -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);

View File

@@ -75,6 +75,7 @@ private:
Flags _options;
Flags _selected;
int _limit = 0;
bool _chatlist = false;
Fn<void(PeerListRowId)> _deselectOption;

File diff suppressed because it is too large Load Diff

View 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);

View File

@@ -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(

View File

@@ -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);

View File

@@ -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());
}
}

View File

@@ -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;

View File

@@ -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(

View File

@@ -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 {

View File

@@ -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),
}));
}
}

View File

@@ -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;
}

View File

@@ -505,5 +505,5 @@ void ShowChoosePeerBox(
bot,
query,
std::move(callback)),
std::move(initBox)), Ui::LayerOption::KeepOther);
std::move(initBox)));
}

View File

@@ -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();

View File

@@ -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,

View File

@@ -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));
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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));
});
}

View File

@@ -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,

View File

@@ -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(

View File

@@ -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);
}));
};
}

View File

@@ -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);

View File

@@ -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,

View File

@@ -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());
}

View File

@@ -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;

View File

@@ -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());

View File

@@ -13,5 +13,4 @@ class GenericBox;
void PinMessageBox(
not_null<Ui::GenericBox*> box,
not_null<PeerData*> peer,
MsgId msgId);
not_null<HistoryItem*> item);

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

Some files were not shown because too many files have changed in this diff Show More