Compare commits

...

490 Commits

Author SHA1 Message Date
John Preston
c04f68f25c Version 5.8.2.
- Improve bottom label color in mini apps.
- Fix some fullscreen issues in mini apps.
- Fix miss-order of archived chats in forward box.
- Fix file dialog for Windows on ARM.
- Fix emoji status pack view from profile regression.
2024-11-19 14:32:15 +04:00
John Preston
e4902efefc Improve fullscreen miniapps on Linux. 2024-11-19 14:24:25 +04:00
John Preston
1267bcd255 Use separate window buttons only with QWindowContainer. 2024-11-19 13:16:54 +04:00
John Preston
0659ccc3f0 Fix user-driven fullscreen change on macOS. 2024-11-19 12:25:27 +04:00
23rd
bd2ae03ab4 Fixed display of long text for open button in web page views. 2024-11-19 10:39:40 +04:00
23rd
244696ae24 Fixed miss-order of archived dialogs in forward box. 2024-11-19 10:39:39 +04:00
John Preston
1438046dd4 Improve bottom label color in mini apps. 2024-11-19 10:37:47 +04:00
John Preston
5c62ba0835 Don't reopen forum on opening a link. 2024-11-18 16:37:22 +04:00
John Preston
20fadfef7f Fix file dialog for Windows on ARM. 2024-11-18 16:01:15 +04:00
John Preston
eed9541f9f Fix Withdraw button text. 2024-11-18 14:25:24 +04:00
John Preston
1594afa389 Fix emoji status pack view regression. 2024-11-18 14:17:01 +04:00
John Preston
9d74d93ed7 Fix some warnings from PVS-Studio.
Fixes #28667.

Some warnings fixed detailed in this post:
https://pvs-studio.com/en/blog/posts/cpp/1186/
2024-11-18 12:50:36 +04:00
John Preston
e4e2f47f8e Adjust build script for Windows Store ARM version. 2024-11-18 01:28:36 +04:00
John Preston
be53bec9b7 Version 5.8.1.
- Fix several possible crashes.
2024-11-17 23:33:45 +04:00
John Preston
bb32c546d4 Fix possible crash in swipe to reply. 2024-11-17 23:24:08 +04:00
John Preston
ecb4ceec7b Fix possible crash in phone click handler. 2024-11-17 23:21:16 +04:00
John Preston
c080bd4c4d Don't resubscribe on folder strip rebuild. 2024-11-17 23:09:42 +04:00
John Preston
39780f49bf Fix folder context menu Remove text color. 2024-11-17 23:00:46 +04:00
John Preston
73349c3c89 Fix crash in chats strip reorder. 2024-11-17 22:52:52 +04:00
John Preston
42a70ff7d0 Fix crash with inline results sending. 2024-11-17 22:41:52 +04:00
John Preston
fa8262cbe9 Version 5.8.
- Updates in Mini Apps platform.
- Chat folders in forward and share boxes.
- Horizontal strip mode for chat folders.
2024-11-17 16:05:22 +04:00
John Preston
7552328cdd Multi-select in mini app send prepared. 2024-11-17 15:08:17 +04:00
John Preston
60f4587d95 Sort birthday gifts to the front. 2024-11-17 15:08:17 +04:00
John Preston
572c074c42 Add gifts-to-profile privacy. 2024-11-17 15:08:17 +04:00
John Preston
3cfbd6a93b Support non-convertible star gifts from bots. 2024-11-17 15:08:16 +04:00
John Preston
d0911b6a45 Send to buy premium from miniapps emoji status. 2024-11-17 15:08:16 +04:00
John Preston
06b85442f8 Fix miniapp downloads in fullscreen. 2024-11-17 15:08:16 +04:00
John Preston
762592daff Allow RTMP for manage call admins. 2024-11-17 15:08:16 +04:00
John Preston
338122793c Implement bot downloads list UI. 2024-11-17 15:08:16 +04:00
John Preston
ef521624a0 Check file by server before downloading. 2024-11-17 15:08:16 +04:00
John Preston
341ab781b2 Implement download of files in miniapps. 2024-11-17 15:08:16 +04:00
John Preston
2fed657940 Make nice share message from miniapp. 2024-11-17 15:08:16 +04:00
John Preston
7bf78b3317 Use initial bot web app header/body colors. 2024-11-17 15:08:16 +04:00
John Preston
2d1fb0562d Prepared messages sharing from miniapp. 2024-11-17 15:08:16 +04:00
John Preston
3d77bff0c9 Support bot emoji status access. 2024-11-17 15:08:15 +04:00
John Preston
4198203a7f Implement emoji status set from miniapps. 2024-11-17 15:08:15 +04:00
John Preston
21487641c1 Support miniapp fullscreen API. 2024-11-17 15:08:15 +04:00
John Preston
c987872be8 Start fullscreen support in SeparatePanel. 2024-11-17 15:08:15 +04:00
John Preston
07e367e1a0 Update API scheme to layer 193. 2024-11-17 15:08:15 +04:00
23rd
db2e45c56e Added ability to go to top of chats list by clicking on selected filter. 2024-11-17 13:43:19 +03:00
23rd
67bbdbfc70 Added ability to change userpic with image from clipboard. 2024-11-17 13:27:57 +03:00
23rd
83df3cba66 Added debug logs for changing chats filters from context menu. 2024-11-17 13:27:57 +03:00
John Preston
f4e2b4bcbd Don't drop the old state of "systemDarkModeEnabled". 2024-11-17 14:07:22 +04:00
Ilya Fedin
6c64c22f83 Don't expand minimum window size for folders
Turn them into horizontal automatically instead
2024-11-17 14:04:27 +04:00
John Preston
702aa944dd Move read/reacted list to a layer. 2024-11-17 14:03:29 +04:00
John Preston
df45edd816 Move channel requests to a layer. 2024-11-17 14:03:29 +04:00
23rd
3f3143514e Added attention style to menu item to delete chats filter. 2024-11-17 11:20:02 +03:00
23rd
3e89910749 Replaced user info with short box for admins in section of kicked users. 2024-11-17 10:46:12 +03:00
23rd
721a642a2f Added date to info who promoted or restricted participants in menu. 2024-11-17 10:40:35 +03:00
23rd
d16ccc9dc5 Fixed display of chats filters strip on removing last chats filter. 2024-11-17 01:52:12 +03:00
23rd
77e7796b3f Fit transactions id from credits history entries to table cell. 2024-11-16 14:43:34 +03:00
23rd
983c949e8c Added ability to provide preloaded credits topup options to list. 2024-11-16 11:39:47 +03:00
23rd
e3f4f60e2d Replaced list of credits topup options with big label in settings. 2024-11-16 11:39:47 +03:00
23rd
33aa904cb7 Added shortcuts to flip photos in media viewer. 2024-11-16 11:39:40 +03:00
23rd
0248be5543 Removed from display extended credits topup options by default. 2024-11-16 07:34:08 +03:00
23rd
f7ca8212aa Moved out down arrow icon of "Show More" button to single place. 2024-11-16 07:34:02 +03:00
23rd
5eb59a1a43 Improved style of tabs for boost gifts in boosts section. 2024-11-16 06:27:07 +03:00
John Preston
7dd1e9bfbe Beta version 5.7.4: Revert accident submodules. 2024-11-16 00:04:20 +04:00
John Preston
067fd25a34 Beta version 5.7.4.
- Enable auto night mode by default.
- Use muted chats folder badge in horizontal strip.
- Fix custom userpic context menu in comments and topics.
- Fix scrolling of chats folders together with the list.
- Fix chats folders list disappearing in some cases.
- Fix topic groups in share box with chats folders.
- Fix bot commands list in some groups.
- Fix crash in channel short info box display.
- Fix crash in bot monetization section opening.
- Fix crash in bot sponsored messages destruction.
2024-11-15 23:59:52 +04:00
John Preston
7cb26ba104 Fix crash in possible ads teardown. 2024-11-15 23:53:59 +04:00
John Preston
a4212cc865 Fix point state detection on media messages. 2024-11-15 23:53:59 +04:00
John Preston
0445f7d6e8 Enable auto-night-mode by default. 2024-11-15 23:53:59 +04:00
John Preston
efe99b3f62 Fix special userpic menu in topics. 2024-11-15 23:53:59 +04:00
23rd
7f2c98f17a Fixed mouse events in chats filters strip with inner padding. 2024-11-15 22:47:39 +03:00
23rd
8f1d215851 Fixed rebuild of chats filters in strip when premium status is changed. 2024-11-15 22:47:39 +03:00
23rd
013b58f6f6 Fixed display of forums in share box with applied chats filter. 2024-11-15 20:04:42 +03:00
23rd
9d3b3476c2 Fixed display of chats filters strip in new windows. 2024-11-15 11:00:35 +03:00
23rd
715874a98f Slightly improved phrase for no results in forward box. 2024-11-15 11:00:30 +03:00
23rd
d2109dd2cb Replaced outer padding for chats filters strip with inner padding. 2024-11-15 11:00:26 +03:00
23rd
ddaf11ed6a Improved style of sponsored messages with media with max width. 2024-11-15 11:00:14 +03:00
23rd
2b5f68003d Fixed pagination of subscriptions in list. 2024-11-15 11:00:07 +03:00
23rd
1a759cc4e7 Fixed crash from short info box for non-users. 2024-11-15 10:59:40 +03:00
23rd
9e83562bf4 Fixed display of pinned chats from main list in forward box. 2024-11-15 10:57:08 +03:00
23rd
c03c19d26f Slightly improved style of chats lists strip in boxes. 2024-11-15 10:57:08 +03:00
23rd
ad9ebdf8e6 Fixed propagation scroll from chats filters strip to chats list. 2024-11-15 10:57:08 +03:00
23rd
9c1701c62a Added support of muted chats filters to chats filters strip. 2024-11-15 10:57:08 +03:00
23rd
edf6c42e9d Added short variant of unfiltered chats list to chats filters strip. 2024-11-15 10:57:08 +03:00
23rd
f0f2a71a87 Fixed disappearing of chats filters strip in forward box after search. 2024-11-15 10:57:08 +03:00
23rd
cf270bd9ce Removed chats filters strip from forum layout. 2024-11-15 10:57:08 +03:00
23rd
49223a4688 Fixed possible crash for bot owners in earn section for bots. 2024-11-15 10:57:08 +03:00
ziplantil
074bb1e66e Fix bug in ChatBotCommands::update when commands empty (#28631) 2024-11-14 11:35:57 +04:00
John Preston
7e2e510d8a Beta version 5.7.3: Fix build with Xcode. 2024-11-13 00:51:35 +04:00
John Preston
1ed34c6fa0 Beta version 5.7.3.
- Option to show folders above the chats list.
- Folders on top of recipients list box.
2024-11-13 00:19:58 +04:00
John Preston
78a0fa55b5 Send correct round video upload actions. 2024-11-12 22:58:05 +04:00
23rd
d37c040b36 Improved style of button for earn out when it is disabled. 2024-11-11 19:32:21 +03:00
23rd
e56bbf557d Added ability to open filters list in context menu from share boxes. 2024-11-11 10:18:00 +03:00
23rd
5abecec478 Fixed display of sponsored messages at bottom for bots. 2024-11-11 10:18:00 +03:00
23rd
ccb41f778e Removed selection marks from unselectable messages. 2024-11-11 10:18:00 +03:00
23rd
059a4cf0d8 Fixed ripple animation for sponsored messages.
Regression was introduced in 3f6d184435.
2024-11-11 10:18:00 +03:00
23rd
7a535a4554 Fixed display of sponsored messages with long descriptions and media. 2024-11-11 10:18:00 +03:00
23rd
f89167ef94 Fixed display of chats filters strip from dialogs widget in search mode. 2024-11-11 10:18:00 +03:00
23rd
a77777f509 Fixed display of chats filters strip from share box in search mode. 2024-11-11 10:18:00 +03:00
23rd
4a327ba584 Fixed display of chats filters strip from forward box in search mode. 2024-11-11 10:18:00 +03:00
23rd
a41e9bf67e Added preview of chats filters strip to filter link box. 2024-11-11 10:18:00 +03:00
23rd
6716973ce0 Added ability to reorder tabs of chats filters strip. 2024-11-11 10:18:00 +03:00
23rd
7cc81393d6 Adapted chats filters tabs slider for reorder feature. 2024-11-11 10:18:00 +03:00
23rd
3e413a036f Added initial implementation of class for reorder of tabs slider. 2024-11-11 10:18:00 +03:00
23rd
63a8fe7ee8 Added direct access to sections of discrete slider to derived classes. 2024-11-11 10:18:00 +03:00
23rd
146409844d Simplified geometry management of chats filters strip. 2024-11-11 10:18:00 +03:00
23rd
ba0da9f59e Slightly improved process of switching between chats filters view types. 2024-11-11 10:18:00 +03:00
23rd
81aef519d4 Added support of setting for chats filters view type to filters menu. 2024-11-11 10:18:00 +03:00
23rd
bcd84518d1 Added tracking of active chats filter to chats filters strip. 2024-11-11 10:18:00 +03:00
23rd
f205952ff2 Added chats filters strip to dialogs widget. 2024-11-11 10:18:00 +03:00
23rd
1d7622e0b5 Added condition to hide chats filters strip. 2024-11-11 10:18:00 +03:00
23rd
4d9112283d Added support of locked sections to chats filters tabs slider. 2024-11-11 10:18:00 +03:00
23rd
dc49c788a8 Added setting for chat filters view type to filters settings. 2024-11-11 10:18:00 +03:00
23rd
36741ab780 Reused setting for legacy iv zoom for chat filters view type. 2024-11-11 10:18:00 +03:00
23rd
53dffc5e88 Added context menu to chats filters strip. 2024-11-11 10:18:00 +03:00
23rd
607c7e7777 Added context menu process to chats filters tabs slider. 2024-11-11 10:18:00 +03:00
23rd
6f09e1699f Moved out display of remove filter box from chats filters menu. 2024-11-11 10:18:00 +03:00
23rd
8c35de48f3 Added process of unread states to chats filters strip. 2024-11-11 10:18:00 +03:00
23rd
b83d943841 Moved out value notification settings for filters to separated file. 2024-11-11 10:18:00 +03:00
23rd
b11b5caeb3 Moved out unread state values of main list to separated file. 2024-11-11 10:18:00 +03:00
23rd
36924da59a Replaced inner slider in chats filters strip. 2024-11-11 10:18:00 +03:00
23rd
f0a2c47613 Added initial implementation of chats filters tabs slider to td_ui. 2024-11-11 10:18:00 +03:00
23rd
5a4449f1a2 Added ability to set custom width of section to DiscreteSlider. 2024-11-11 10:18:00 +03:00
23rd
de3d7a7774 Removed redundant templates from DiscreteSlider. 2024-11-11 10:18:00 +03:00
23rd
b06dbd1c00 Added support of pinned chats from filters to forward box. 2024-11-11 10:18:00 +03:00
23rd
1fa5e424e9 Added chats filters tabs strip to share box. 2024-11-11 10:18:00 +03:00
23rd
d81c7abf1a Added chats filters strip to forward box. 2024-11-11 10:18:00 +03:00
23rd
932215c91d Added initial implementation of tabs strip for chats filters. 2024-11-11 10:18:00 +03:00
23rd
7aa1141ba5 Added ability to ignore hidden rows while searching to PeerListContent. 2024-11-11 10:18:00 +03:00
23rd
3699439506 Added ability to count sections widths of slider from derived class. 2024-11-11 10:18:00 +03:00
23rd
76b1288f77 Added access to height of multi select from peer list box. 2024-11-11 10:18:00 +03:00
xmdn
8fd9ae4e59 Replace deprecated x64 architecture identifier 2024-11-11 11:16:08 +04:00
John Preston
53abd2fe38 Follow show-panel-on-click for attach menu. 2024-11-06 17:53:09 +04:00
xiota
da8a4ba8ab Fix some ffmpeg 7.x related errors 2024-11-06 16:42:18 +04:00
John Preston
9c3990c0c1 Fix deploy scripts for Windows on ARM. 2024-11-06 16:41:56 +04:00
John Preston
1eeb46d5fc Fix build on VS 17.12 Preview 4 on ARM. 2024-11-06 12:26:12 +04:00
John Preston
02c01e258c Version 5.7.2.
- Fix recompressed video playback cutoff.
- Fix video message recroding on macOS.
2024-11-05 12:45:43 +04:00
John Preston
b2f912868d Fix record availability check on macOS. 2024-11-05 11:38:04 +04:00
John Preston
ecf0eba0a5 Open forum with topic by topic message link. 2024-11-05 11:37:15 +04:00
John Preston
8e5fec2fa8 Open forum from common groups. 2024-11-05 11:37:00 +04:00
John Preston
89c35e8512 Show copy-to-clipboard button tooltip. 2024-11-05 11:36:10 +04:00
John Preston
3fa88fad79 Correctly highlight label links in RTL. 2024-11-05 11:32:59 +04:00
John Preston
5f037462ed Implement a special context menu for sender userpic. 2024-11-04 16:54:01 +04:00
John Preston
93c01e5f1e Show "Hide My View" only for user stories. 2024-11-04 16:53:37 +04:00
John Preston
52953626a7 Allow ctrl/cmd+click copy private post link. 2024-11-04 15:22:13 +04:00
John Preston
f77fdc799d Show special phone number context number. 2024-11-04 14:52:58 +04:00
John Preston
7bf1f9bd71 Put manage chat above stories archive. 2024-11-04 14:41:25 +04:00
John Preston
363ffc1c04 Remove unused null option in SendReport(). 2024-11-04 14:05:37 +04:00
John Preston
4001899bdf Add personal channel to ShortInfoBox. 2024-11-04 14:00:51 +04:00
John Preston
fabdd89c4a Use original duration for video qualities. 2024-11-04 13:11:10 +04:00
John Preston
70f1675085 Improve diff/patch/git highlighting. 2024-11-04 12:18:00 +04:00
John Preston
07e1e2d9d6 Allow opening username in specific indexed account. 2024-11-04 12:03:37 +04:00
John Preston
ebff6c6370 Show exact reactions count in channels. 2024-11-04 11:20:29 +04:00
John Preston
233eb6d916 Version 5.7.1.
- Fix occasional redundant newlines before message timestamp.
- Remove playback speed change on settings button click.
- Fix video playback settings button on hidpi screens.
- Fix return from Picture-in-Picture to media viewer.
- Fix video quality auto-switching in Picture-in-Picture.
- Add message link to channel stars transaction information.
- Fix GIF playback in media viewer.
- Fix several possible crashes.
2024-11-01 22:25:07 +04:00
John Preston
d568cab2fc Fix initial IV zoom level.
Fixes #28600.
2024-11-01 22:25:07 +04:00
John Preston
8447b95c50 Fix GIF playback in media viewer. 2024-11-01 20:00:04 +04:00
John Preston
e959d1e1b0 Improve playback speed toggle logic. 2024-11-01 18:25:40 +04:00
John Preston
ead5dbe368 Strict validity check for local lastseen. 2024-11-01 18:22:57 +04:00
John Preston
f091f2b344 Fix album sending with video processing. 2024-11-01 17:52:05 +04:00
John Preston
267a51e800 Fix possible crash in scheduled sent toast. 2024-11-01 17:27:06 +04:00
John Preston
939f6095ba Fix possible crash in Recent Actions. 2024-11-01 17:18:59 +04:00
John Preston
a333615e53 Add message link to channel star transaction info. 2024-11-01 16:43:36 +04:00
John Preston
e1b33fbc40 Remove unneeded separator from context menu. 2024-11-01 14:35:33 +04:00
John Preston
99c5d994b5 Add special string for bot revenue. 2024-11-01 14:16:43 +04:00
John Preston
0971485367 Support quality change in PiP. 2024-11-01 14:15:28 +04:00
23rd
3cfa963f69 Added start date to subscription entry in credits transactions history. 2024-11-01 11:26:40 +03:00
John Preston
7096a4231f Fix PiP -> viewer streaming jump. 2024-11-01 11:26:03 +04:00
John Preston
07f0c182e6 Don't change speed on settings click. 2024-11-01 10:16:09 +04:00
John Preston
535b223333 Fix video playback settings button dpr. 2024-11-01 10:10:16 +04:00
John Preston
ffa8c2be79 Fix some of newlines before skip blocks. 2024-11-01 09:56:20 +04:00
John Preston
2873e64ca2 Update cmake_helpers. 2024-11-01 09:00:59 +04:00
Ilya Fedin
8bd28bce69 Update GSL to 4.1.0 2024-11-01 08:59:44 +04:00
Ilya Fedin
b97ce43fca Revert "Use GSL from a desktop-app fork."
This reverts commit bbe4312017.
2024-11-01 08:59:44 +04:00
GitHub Action
d34c4cc2f2 Update User-Agent for DNS to Chrome 130.0.0.0. 2024-11-01 08:39:37 +04:00
John Preston
1ca1f0fa7d Revert "Always rely on __has_include(<winres.h>)"
This reverts commit e0e4a7bec6.

Didn't work for me, gave errors when building.
2024-10-31 23:19:19 +04:00
John Preston
8c3c8f888d Version 5.7: Fix build with Xcode. 2024-10-31 21:35:51 +04:00
John Preston
a75d7f0381 Version 5.7.
- Sending video messages.
- New video quality selection in channels.
- Adding media to sent text messages via Edit.
- Ads in bots with revenue sharing with bot developers.
- Chat-specific hashtags.
2024-10-31 21:02:22 +04:00
John Preston
7684466acf Fix crash in quality auto-toggle. 2024-10-31 20:33:16 +04:00
John Preston
0067245739 Send more viewport_changed, just in case. 2024-10-31 20:33:15 +04:00
John Preston
4a5d8aa217 Fix build with MSVC. 2024-10-31 20:33:15 +04:00
John Preston
2d786aa02c Fix a typo in phrases. 2024-10-31 20:33:15 +04:00
Ilya Fedin
b0933b96ef Add error message for webview unsupported display server 2024-10-31 19:22:37 +04:00
Ilya Fedin
20fb73b626 Add error message for webview without OpenGL 2024-10-31 19:22:37 +04:00
Ilya Fedin
ae7bd7112b Stop recommending webkitgtk-6.0 2024-10-31 19:22:37 +04:00
Ilya Fedin
2365363dcc webkitgtk-6.0 -> webkit2gtk-4.1 in snap 2024-10-31 19:22:37 +04:00
Ilya Fedin
e0e4a7bec6 Always rely on __has_include(<winres.h>) 2024-10-31 19:22:37 +04:00
Ilya Fedin
ebd0c3696a Remove #ifndef __MINGW32__ from Windows notifications
It uses C++/WinRT nowadays just like all other code so there's no point in #ifndef'ing only it
2024-10-31 19:22:37 +04:00
Ilya Fedin
e0a0d9c039 Remove outdated ifdef
The file doesn't have __in anymore
2024-10-31 19:22:37 +04:00
Ilya Fedin
dae9f2ab2b Replace CMAKE_CXX_COMPILER_FRONTEND_VARIANT check with MSVC variable 2024-10-31 19:22:37 +04:00
Ilya Fedin
7168a00ee4 Update submodules 2024-10-31 19:22:37 +04:00
John Preston
1e2d0ced20 Update submodules. 2024-10-31 19:22:23 +04:00
John Preston
d6ac883efa Implement adaptive quality selection. 2024-10-31 18:04:17 +04:00
John Preston
a386d70ae4 Track qualities availability correctly. 2024-10-31 18:04:17 +04:00
John Preston
bc8bf672b4 Don't use shared video cover in viewer. 2024-10-31 18:04:17 +04:00
John Preston
e38998214f Disable reschedule/edit-media for processing. 2024-10-31 18:04:17 +04:00
John Preston
472a2fe802 Position ttl button correctly on record finish. 2024-10-31 18:04:17 +04:00
John Preston
2d6d89b1cf Fix recording single-listen tooltip text. 2024-10-31 18:04:17 +04:00
John Preston
66be2ac6ca Show toast/tooltip info on video processing. 2024-10-31 18:04:17 +04:00
John Preston
3137c9f3f7 Jump-to-scheduled on video processing. 2024-10-31 18:04:17 +04:00
John Preston
b9ebb02e72 Update API scheme on layer 192. 2024-10-31 18:04:17 +04:00
John Preston
1e02c475d6 Show speed / quality badges. 2024-10-31 18:04:16 +04:00
John Preston
4d2cda0692 Fix menu in case speed isn't controlled. 2024-10-31 18:04:16 +04:00
John Preston
cbd2b8f428 Query recording availability in the background.
Fixes #28576.
2024-10-31 18:04:16 +04:00
John Preston
93605db690 Use custom toast for video-not-available state. 2024-10-31 18:04:16 +04:00
John Preston
2567096de0 Send correct action on round recording. 2024-10-31 18:04:16 +04:00
John Preston
6ed25d012f Use new settings icon in the player. 2024-10-31 18:04:16 +04:00
John Preston
c2afef2bde Improve changing of video playback quality. 2024-10-31 18:04:16 +04:00
John Preston
0991e7d8a4 Mirror recorded round message. 2024-10-31 18:04:16 +04:00
John Preston
cf52f2a743 Improve multi-line menu item items. 2024-10-31 18:04:16 +04:00
John Preston
37dddda1a0 Save preferred video quality to settings. 2024-10-31 18:04:16 +04:00
John Preston
3f2f3ebd51 Allow switching video quality. 2024-10-31 18:04:16 +04:00
John Preston
7ba78540ac Keep alternative video qualities list. 2024-10-31 18:04:16 +04:00
23rd
19afb49fce Fixed display of empty context menu in section of history widget. 2024-10-31 16:25:41 +03:00
23rd
46ab553fa5 Rounded earn values from overview to two decimal digits. 2024-10-31 16:12:31 +03:00
23rd
68cc42047e Unified context menu creation for different types of sponsored messages. 2024-10-30 11:59:11 +03:00
23rd
e25cf27ba5 Replaced dropdown menu with popup in box for revenue sponsored messages. 2024-10-30 11:58:33 +03:00
23rd
3895e6d958 Fixed empty context menu from space of sponsored messages. 2024-10-30 11:58:33 +03:00
23rd
24cf3984c8 Implemented operator<< for FullMsgId to enable qDebug output. 2024-10-30 06:47:13 +03:00
23rd
6237675744 Moved out ui callbacks of bar from data class from sponsored messages. 2024-10-30 06:47:13 +03:00
23rd
30dae049ff Fixed non-animated display of top bar for sponsored messages. 2024-10-30 06:39:33 +03:00
23rd
1dc30caee9 Added hide button to bar for sponsored messages without photo. 2024-10-29 15:07:28 +03:00
23rd
b2d340cbfb Added top button to box about sponsored messages for bots. 2024-10-29 15:07:28 +03:00
23rd
7d52787e54 Added support of sponsored messages with revenue to bar. 2024-10-29 14:55:14 +03:00
23rd
ae3f16ccbd Changed behavior to show earlier button in dialog list to jump to top. 2024-10-28 19:34:27 +03:00
23rd
057222757b Added decimal separators to count of credits in balances of owned bot. 2024-10-28 17:47:17 +03:00
23rd
119f109904 Added api support of paid credits transactions from bots. 2024-10-28 17:47:17 +03:00
23rd
23a77b1ba4 Fixed width of menu item for IV zoom. 2024-10-28 15:55:28 +03:00
23rd
c076daa91f Added ability to implement platform-dependent zoom controller for IV. 2024-10-28 15:55:28 +03:00
23rd
b7ef5325ac Improved display of sponsored message bar for preloaded messages. 2024-10-28 13:26:10 +03:00
23rd
8b535c58fa Slightly improved sponsored message bar for instant display. 2024-10-28 13:25:27 +03:00
23rd
6bc8daaeda Added earn button to section of bot settings. 2024-10-27 14:17:51 +03:00
23rd
7a2562e5bb Added icons to earn buttons in profile sections of owned bots. 2024-10-27 13:42:17 +03:00
23rd
9e0c731b32 Moved out generation of credits menu icon from svg to td_ui. 2024-10-27 13:42:01 +03:00
23rd
2e0e4006a1 Moved out generation of currency icon from svg to td_ui. 2024-10-27 13:41:14 +03:00
23rd
187139473d Added subsection title to earn buttons in profile of owned bots. 2024-10-27 13:40:37 +03:00
23rd
dbb0a5ad28 Added ability to open currency earn section for owned bots. 2024-10-27 10:04:06 +03:00
23rd
24aaed44b9 Slightly simplified constructor of Info::BotEarn::InnerWidget. 2024-10-27 09:56:41 +03:00
23rd
32b8d83c04 Moved out Info::Statistics::Tag to separated file. 2024-10-27 09:56:41 +03:00
23rd
bf55c325ce Fixed hotkey to reset IV zoom. 2024-10-27 05:01:25 +03:00
23rd
3af288c74e Added currency balance to profile section of owned bots. 2024-10-26 19:20:56 +03:00
23rd
e306d9ba35 Added loading of balances on receiving full user for owned bots. 2024-10-26 19:20:50 +03:00
23rd
b23a877d7e Added ability to cache currency balance by peer. 2024-10-26 19:20:50 +03:00
23rd
08fda055fc Renamed Api::ChannelEarnStatistics with Api::EarnStatistics. 2024-10-26 19:20:50 +03:00
23rd
84055ed74e Added context menu to top bar of sponsored messages. 2024-10-26 17:55:07 +03:00
23rd
2db30690ce Added ability to open menu for sponsored messages without history item. 2024-10-26 17:55:07 +03:00
23rd
304bcfd343 Added initial button to top bar of sponsored messages to hide it. 2024-10-26 17:55:07 +03:00
23rd
8a1cf2bb3a Added initial implementation of top bar for sponsored messages. 2024-10-26 17:55:07 +03:00
23rd
c857c24a64 Added util functions to process photo and document as dynamic images. 2024-10-26 17:55:07 +03:00
23rd
bbdcb047d0 Added ability to request sponsored messages not only for channels. 2024-10-26 17:55:07 +03:00
John Preston
78f2e70956 Update API scheme to layer 192. 2024-10-26 16:36:17 +04:00
John Preston
75a75626ce Show info about popular apps section. 2024-10-26 16:34:44 +04:00
John Preston
cec9688d58 Implement public posts hashtag search preview. 2024-10-26 16:34:44 +04:00
John Preston
81492b7d3a Add "when edited" context menu information. 2024-10-26 16:34:43 +04:00
John Preston
9166acbbb9 Improve gift convert confirmation. 2024-10-26 16:34:43 +04:00
John Preston
36de2b6ca6 Improved received star gift box. 2024-10-26 16:34:43 +04:00
John Preston
21f909dd4b Improve star gift value table row. 2024-10-26 16:34:43 +04:00
John Preston
f2a92c9122 Update API scheme to layer 191. Sold out gifts. 2024-10-26 16:34:43 +04:00
John Preston
7ee2e3d8bc Support hashtags with mentions. 2024-10-25 18:14:44 +04:00
John Preston
f89aeb6ad4 Implement text -> media message editing. 2024-10-25 18:14:44 +04:00
John Preston
0397006894 Add more logging on failed top peers reading. 2024-10-25 18:14:44 +04:00
23rd
d6863074b2 Fixed error handler of wrong cloud password for withdrawal button. 2024-10-24 19:46:35 +03:00
23rd
9c185a30e0 Slightly improved position of button for QR of username. 2024-10-24 19:15:19 +03:00
23rd
a8f492a027 Improved colors of ministars in buttons from section of peer gifts. 2024-10-24 18:34:11 +03:00
23rd
0a92b1dc68 Slightly improved subtext in overview from statistics section. 2024-10-24 18:15:33 +03:00
23rd
e6d661f8ee Fixed statistics section of stories from megagroups. 2024-10-24 17:43:04 +03:00
23rd
f48dfb5d81 Added ability to add to contacts phone from text phone entity. 2024-10-24 17:04:22 +03:00
23rd
cd041e8366 Replaced empty settings button with simple ripple button. 2024-10-24 17:04:22 +03:00
23rd
6787ea883e Replaced empty icon buttons with simple circle ripple buttons. 2024-10-24 17:04:22 +03:00
John Preston
78937d716f Beta version 5.6.4.
- Add recording of video messages in case a camera is available.
- Add "Respect the Focus settings" for custom notifications on Windows.
2024-10-24 13:40:52 +04:00
John Preston
9713abc002 Fix build with GCC. 2024-10-24 13:36:22 +04:00
John Preston
b44b45cca0 Fix docker build with openh264 in ffmpeg. 2024-10-24 13:26:46 +04:00
John Preston
9e2cf0ed73 Nice faded show/hide of round recorder. 2024-10-24 13:26:46 +04:00
John Preston
b01d7ea5b9 Show recording init errors. 2024-10-24 13:26:46 +04:00
John Preston
ae89b65a98 Fix message selection checks position. 2024-10-24 13:26:46 +04:00
John Preston
9b9c3d788d Skip some first frames. 2024-10-24 13:26:46 +04:00
John Preston
ccc6c6daa5 Save last camera image as a placeholder. 2024-10-24 13:26:46 +04:00
John Preston
9ce6636c6a Create nice camera init effect. 2024-10-24 13:26:46 +04:00
John Preston
6287d306c2 Add better discard confirmations. 2024-10-24 13:26:46 +04:00
John Preston
6cfa053328 Add recorded round video preview. 2024-10-24 13:26:45 +04:00
John Preston
9514b6eecd Show mini-thumbnails when pausing recording. 2024-10-24 13:26:45 +04:00
John Preston
c8d4818d22 Blurred frame while paused. 2024-10-24 13:26:45 +04:00
John Preston
4142ada729 Concatenate two recordings. 2024-10-24 13:26:45 +04:00
John Preston
d7ffdbd78d Prepare for round record pause/resume. 2024-10-24 13:26:45 +04:00
John Preston
e8d87d37bb Use a shorter phrase for bot policy. 2024-10-24 13:26:45 +04:00
John Preston
343ffc23eb Add some dir="auto" to IV page.
I hope this fixes #28551.
2024-10-24 13:26:45 +04:00
John Preston
95e0086eed Remov unused variable. 2024-10-24 13:26:45 +04:00
John Preston
c010ecfe38 Allow sending one-time round videos. 2024-10-24 13:26:45 +04:00
John Preston
302e9371c8 Show record init errors. 2024-10-24 13:26:45 +04:00
John Preston
7060c0e6d7 Fix crash in context menu without an item.
Fixes #28552.
2024-10-24 13:26:42 +04:00
John Preston
20a4c7f9f4 Wait for both audio and video to start. 2024-10-24 13:24:44 +04:00
John Preston
e59e4afd3e Show recording progress. 2024-10-24 13:24:44 +04:00
John Preston
f74dd3ca1e Add author to the top of Reply in Another Chat. 2024-10-24 13:24:44 +04:00
John Preston
511cfc524f Adjust bitrates for video messages. 2024-10-24 13:24:44 +04:00
John Preston
4cf6173d25 Cut video messages round with white bg. 2024-10-24 13:24:44 +04:00
John Preston
17996757fd Show round video preview. 2024-10-24 13:24:44 +04:00
John Preston
6bc1049858 Use nice icon for video message recording. 2024-10-24 13:24:43 +04:00
John Preston
ff44f626ba Allow switching between voice/video. 2024-10-24 13:24:43 +04:00
John Preston
552343fa37 PoC video messages sending. 2024-10-24 13:24:43 +04:00
John Preston
4dc7fd8cd1 Add AAC/H264 encoders, MP4 muxer. 2024-10-24 13:24:43 +04:00
John Preston
285c96fd2e Add "Respect the Focus settings" on Windows. 2024-10-24 13:24:43 +04:00
John Preston
e6af33367e Accept bot username in chatbot setup. 2024-10-24 13:24:43 +04:00
23rd
7092fe2242 Added default ripple animation style with windowBgOver color. 2024-10-24 09:05:27 +04:00
23rd
a32ff46579 Added special hotkeys to change IV zoom more gradually. 2024-10-24 09:05:27 +04:00
23rd
7f20cf59d1 Added ministars to button in service messages for gifts. 2024-10-24 09:05:27 +04:00
23rd
a8a1b08127 Added ministars to buttons in section of peer gifts. 2024-10-24 09:05:27 +04:00
23rd
1a1e777b87 Fixed display of generic media with small width. 2024-10-24 09:05:27 +04:00
23rd
9e76e64064 Improved parts alignments of round video messages. 2024-10-24 09:05:27 +04:00
23rd
975ae17ef9 Moved selection marks to bottom of message bubbles. 2024-10-24 09:05:27 +04:00
23rd
ed9dcef66f Fixed display of selection marks for messages with huge code blocks. 2024-10-24 09:05:27 +04:00
23rd
b1e537e54e Added subscription icon to channel photo in ReceiptCreditsBox. 2024-10-24 09:05:27 +04:00
23rd
e5886862c3 Changed behavior to copy phone number from profile sections as trimmed. 2024-10-24 09:05:27 +04:00
Ilya Fedin
d85b668d4f Fix lambda execution for portal dark mode getter 2024-10-24 07:04:52 +02:00
Grigory
b363d8bfb5 fix(ui/webview_helpers): append 0 if color channel value is 1 char long 2024-10-19 18:43:11 +02:00
John Preston
754d467440 Version 5.6.3: Fix build with Xcode. 2024-10-15 22:13:06 +04:00
John Preston
598f08d6c7 Version 5.6.3.
- Add ability to change page scale in Instant View pages.
- Fix unnecessary timestamp jump to a new line.
- Fix secondary button positioning in miniapps.
- Fix a crash in QR code copy for a chat without a photo.
- Fix a crash in share options menu showing.
- Fix a crash on monitor disconnect.
2024-10-15 21:01:27 +04:00
23rd
224fdc1864 Returned back freedom to copy links even in non-forwardable histories.
This reverts commits b64c610abb
and 9cd194e60e.
2024-10-15 13:04:57 +03:00
23rd
e646b4dc9a Slightly improved position of selection marks for wide chats. 2024-10-15 12:59:15 +03:00
23rd
9077db2e97 Added verified mark of web view bots to box of confirmation to open. 2024-10-15 12:46:13 +03:00
23rd
7e14277ead Added ability to change zoom in IV. 2024-10-15 12:46:13 +03:00
John Preston
d351a7d697 Fix crash on monitor arrangement change. 2024-10-15 12:13:53 +04:00
John Preston
70ed43b811 Fix crash on send options menu show.
Fixes #28532.
2024-10-15 12:13:53 +04:00
John Preston
913083ebc6 Fix miniapp buttons positioning. 2024-10-15 12:13:52 +04:00
John Preston
588a95a7ae Fix time jumping to a new line. 2024-10-15 12:13:52 +04:00
23rd
c0a0ad4ec5 Added info to context menu for non-forwardable history. 2024-10-15 01:15:04 +03:00
23rd
5eafe96525 Fixed crash in share QR box on copy without profile photo. 2024-10-15 01:15:00 +03:00
John Preston
b41ac0fc2a Version 5.6.2: Fix build with GCC. 2024-10-14 16:34:45 +04:00
John Preston
cfe93530b8 Version 5.6.2.
- Message selection marks.
- Better copy username / public link / ID menu in profiles.
- Keyboard navigation (Up/Down/PageUp/PageDown) in chat preview.
- Improved QR options for profile links.
- Fix calls color problems on Windows.
- Fix scroll problems on Linux.
2024-10-14 13:58:27 +04:00
John Preston
4492e72ffa Fix web app window resize on macOS. 2024-10-14 13:55:29 +04:00
John Preston
1a0fd35f36 Check gift sold out error on sending. 2024-10-14 12:49:55 +04:00
John Preston
db475ef0b4 Update API scheme to layer 190.
Support adding messages to gifts of Premium subscriptions.
2024-10-14 12:49:55 +04:00
John Preston
b3dddc1dfe Fix indent of RTL text with as skip block.
Fixes #27748.
2024-10-14 12:49:54 +04:00
23rd
b64c610abb Fixed ability to copy shared links for channels with select restriction.
Fixed #28492.
2024-10-14 12:49:33 +04:00
23rd
c5add2fca9 Revert "Added experimental option to disable floating reactions strip."
This reverts commit a32a9aa3fc.
2024-10-14 12:49:33 +04:00
23rd
9ea78f7d28 Fixed display of message highlights in selection mode. 2024-10-14 12:49:33 +04:00
23rd
54b0d965ae Slightly eased conditions for selection mode to avoid shaky animations. 2024-10-14 12:49:33 +04:00
23rd
7b4bd5696b Added check views to selection mode of messages. 2024-10-14 12:49:33 +04:00
23rd
9064f3ba4b Fixed behavior of checkbox to remove background from QR in share QR box. 2024-10-14 12:49:33 +04:00
23rd
e00e562b5f Fixed update of width for usernames label in profile section as well. 2024-10-14 12:49:32 +04:00
John Preston
73f38c896f Use new phrase for miniapp launch confirmation. 2024-10-14 12:49:32 +04:00
John Preston
5767cbd0e3 Fix send menus with additional items. 2024-10-14 12:49:32 +04:00
John Preston
ba31756bf9 Fix build with MSVC. 2024-10-14 12:49:32 +04:00
Ilya Fedin
a88f48cd93 Fallback to portal on Linux if QStyleHints::colorScheme is unknown 2024-10-14 12:39:41 +04:00
K900
f2e0e481de Update for lib_ui API change 2024-10-12 19:54:34 +04:00
K900
566f279137 Fix build without QT_IMPLICIT_QCHAR_CONSTRUCTION 2024-10-12 19:54:34 +04:00
John Preston
394ef13955 Fix code block language change.
Fixes #28513.
2024-10-11 09:32:21 +04:00
John Preston
6812e17d07 Improve star gift size in the box. 2024-10-11 09:31:45 +04:00
John Preston
fdf826b686 Improve gifts layout a bit. 2024-10-11 09:31:45 +04:00
John Preston
e57742e7de Don't show non-working wallpaper button. 2024-10-11 09:31:45 +04:00
23rd
73b63aa414 Fixed update of width for username label in profile section. 2024-10-10 18:56:20 +03:00
23rd
44aa2aec5d Added ability to change font size in share QR box. 2024-10-10 18:56:20 +03:00
23rd
8d9d7c4cea Added ability to remove background from QR in share QR box. 2024-10-10 18:56:20 +03:00
23rd
9557f0c844 Improved quality of profile photo in share QR box. 2024-10-10 18:56:20 +03:00
23rd
a32a9aa3fc Added experimental option to disable floating reactions strip. 2024-10-10 18:56:20 +03:00
23rd
86fa98dfbb Fixed position of mini stars in header if gift box. 2024-10-10 18:56:20 +03:00
23rd
6a69447d90 Fixed non focused window when process credits payment from web view bot. 2024-10-10 18:56:20 +03:00
John Preston
2d20e7a9e2 Add keyboard navigation to chat preview. 2024-10-10 14:16:43 +04:00
John Preston
ac7b2e0da0 Keyboard navigation in closed topics.
Fixes #27319.
Fixes #28288.
2024-10-10 14:16:34 +04:00
John Preston
388325a496 Set dpi awareness in the manifest. 2024-10-10 13:53:06 +04:00
John Preston
af728e82fc Unify channel links and usernames clicks. 2024-10-10 13:53:06 +04:00
John Preston
3cb33f0825 Custom "Copy ID" for peer ID context menu. 2024-10-10 13:53:06 +04:00
John Preston
1038baf467 Allow timestamp links under spoilers. 2024-10-10 13:53:05 +04:00
John Preston
828ecabc78 Don't show user badge in star stats rows. 2024-10-10 13:53:05 +04:00
Ilya Fedin
88703ba1eb Toggle fullscreen by double click in media viewe title area 2024-10-10 09:21:29 +02:00
Ilya Fedin
b0ecb2c535 Update patches and lib_ui 2024-10-09 11:34:24 +02:00
Ilya Fedin
c70482dbc4 Qt 6.8.0-rc1 -> 6.8.0 2024-10-09 11:34:24 +02:00
John Preston
51f1999412 Version 5.6.1: Fix build with Xcode. 2024-10-07 15:03:59 +04:00
John Preston
dd4fbc256c Version 5.6.1: Support sold out gifts better. 2024-10-07 14:50:18 +04:00
John Preston
cc2265583f Remove assert in GL init. 2024-10-07 14:49:00 +04:00
John Preston
1e7a4db57f Version 5.6.1.
- Fix media viewer sometimes blocking the app after launch.
2024-10-07 13:43:01 +04:00
John Preston
9a9e30c88b Fix commands list show after scheduled visit. 2024-10-07 13:42:36 +04:00
John Preston
3d98ebff42 Hide layer when showing topics list. 2024-10-07 13:23:10 +04:00
John Preston
a6f4b1ae8e Fix build with Qt 6. 2024-10-07 12:50:20 +04:00
Ilya Fedin
3e6ea8109c Don't treat window controls as continuous 2024-10-07 10:48:56 +02:00
Ilya Fedin
ec407d57a5 Use one side controls in group call 2024-10-07 10:48:56 +02:00
John Preston
838ad66166 Improve hidpi/window geometry init. 2024-10-07 12:48:06 +04:00
23rd
c6bf905253 Slightly improved style of button to install update from dialogs list. 2024-10-06 12:21:23 +03:00
John Preston
8310230582 Version 5.6: Fix builds. 2024-10-05 21:41:22 +04:00
John Preston
1f21af0bdb Version 5.6.
- Gifts for Telegram Stars.
- Mention / hashtag autocomplete in media caption field.
- Resizing the window used for web apps and games.
- "Include muted chats in folder counters" notifications setting.
- Emoji from Unicode 15.1.
2024-10-05 20:46:11 +04:00
John Preston
127e4a5086 Attempt to fix screen dpi changes on Windows. 2024-10-05 20:36:33 +04:00
John Preston
6a30967f23 Attempt to fix media viewer with OpenGL. 2024-10-05 20:33:51 +04:00
John Preston
df2b020b42 Add debug logs for recent/top peers resetting. 2024-10-05 20:32:09 +04:00
23rd
a74c5a89a6 Added manage channel button to profile section of owned channels. 2024-10-05 18:50:03 +03:00
23rd
3cb0be5be5 Added administrators button to profile section of owned channels. 2024-10-05 18:50:03 +03:00
23rd
d56f3cfecf Added credits balance to profile section of owned bots. 2024-10-05 18:50:03 +03:00
23rd
9716a901d1 Added ability to cache credits balance by peer. 2024-10-05 18:50:03 +03:00
23rd
649d242e9a Added userpic of web view bots to box of confirmation to open. 2024-10-05 18:50:03 +03:00
23rd
8ac3c2157f Removed redundant code from swipe-to-reply for macOS. 2024-10-05 18:50:03 +03:00
23rd
7ca4ca21fa Fixed middle elision in filenames from document views. 2024-10-05 18:50:03 +03:00
23rd
e565acba91 Added api support for stories reports with server options. 2024-10-05 18:49:21 +03:00
John Preston
ede771e51b Focus gift message field. 2024-10-04 10:06:21 +04:00
John Preston
0282786b4c Generalize gift sending. 2024-10-04 10:06:21 +04:00
John Preston
42dd08ace5 Use a separate title for star gift recipient box. 2024-10-04 10:06:21 +04:00
John Preston
cf1d274b0d Return gift_credits_box with gifting stars. 2024-10-04 10:06:21 +04:00
John Preston
d361d5f3b2 Move gift_credits_box -> star_gift_box, strip old code. 2024-10-04 10:06:21 +04:00
John Preston
3e63b40564 Server-side star gift message length limit. 2024-10-04 10:06:21 +04:00
John Preston
c5139ed06a Fix emoji panel position in star gift box. 2024-10-04 10:06:21 +04:00
23rd
3dc93526ff Renamed report_box with report_box_graphics. 2024-10-04 10:06:21 +04:00
23rd
3edf8e10e2 Added api support for reports with server options. 2024-10-04 10:06:21 +04:00
John Preston
00215622cc Update API scheme on layer 189. Build broken. 2024-10-04 10:06:21 +04:00
John Preston
479b7c3140 Add emoji selector to star gift sending. 2024-10-04 10:06:21 +04:00
John Preston
a3ca8ddcfc Support entities in star gift message in box. 2024-10-04 10:06:21 +04:00
John Preston
9ace04d2c9 Support entities in star gift messages. 2024-10-04 10:06:21 +04:00
John Preston
8b11d2d5e7 Show nice star gift transactions in history. 2024-10-04 10:06:21 +04:00
John Preston
05fb0f81f9 Show gifts about text. 2024-10-04 10:06:21 +04:00
John Preston
da49efa1ed Simple profile page for Verification Codes. 2024-10-04 10:06:21 +04:00
John Preston
39fb0a5b66 Remove custom PeerData::isVerifyCodes() userpic. 2024-10-04 10:06:21 +04:00
John Preston
962d4d29ee Process gift updates in list. 2024-10-04 10:06:21 +04:00
John Preston
779e9b658b Update API scheme on layer 189. 2024-10-04 10:06:20 +04:00
John Preston
f9ee4dcb51 Show 'hidden from profile' icon in my gifts. 2024-10-04 10:06:20 +04:00
John Preston
1acdbb69ae Show correct gift sender. 2024-10-04 10:06:20 +04:00
John Preston
c022a1c838 Fix gifts list loading both ways. 2024-10-04 10:06:20 +04:00
John Preston
52afd3d5a8 Display gifts list in the profile. 2024-10-04 10:06:20 +04:00
John Preston
79b1c0edee Support PeerData::isVerifyCodes() peer type. 2024-10-04 10:06:20 +04:00
John Preston
4803bd4b3f Started gifts list in profile. 2024-10-04 10:06:20 +04:00
John Preston
215a262076 Allow converting gifts to stars. 2024-10-04 10:06:20 +04:00
John Preston
7a35577d3a Save/unsave gifts to the profile page. 2024-10-04 10:06:20 +04:00
John Preston
efbd3ca8fa Implement star gift view box. 2024-10-04 10:06:20 +04:00
John Preston
18904412cd Show chat and toast about sent gift. 2024-10-04 10:06:20 +04:00
John Preston
0d0d0ab994 Allow sending premium / star gifts. 2024-10-04 10:06:20 +04:00
John Preston
71deef61f5 Add support for star gifts API. 2024-10-04 10:06:20 +04:00
John Preston
46c8a55f56 Make nicer discount/limited gift badges. 2024-10-04 10:06:20 +04:00
John Preston
37f837dcb7 Add sending gift preview. 2024-10-04 10:06:20 +04:00
John Preston
a5be0a685a Extract gift button into separate widget. 2024-10-04 10:06:20 +04:00
John Preston
4cdd1fec95 Start UI of the gifts. 2024-10-04 10:06:20 +04:00
John Preston
761617c1ce Start UI of the new gift sending box. 2024-10-04 10:06:20 +04:00
John Preston
e3bc4dab85 Update API scheme to layer 189. 2024-10-04 10:06:20 +04:00
John Preston
335095a332 Beta version 5.5.8: Fix build with Xcode. 2024-10-03 12:21:06 +04:00
John Preston
15fcb73e19 Beta version 5.5.8.
- Allow drag-to-scroll in reply quote edit box.
- Add bot verified badge to web app title.
- Fix crash when opening some channels.
- Fix some visual glitches.
2024-10-03 11:59:57 +04:00
John Preston
a8b0f2934b Scroll quote selection in the draft options box. 2024-10-03 11:56:04 +04:00
John Preston
7d67b3d00a Use circle userpics in reactions from forums. 2024-10-03 11:55:57 +04:00
John Preston
b60c7e97ab Don't show full query in "nothing found" info. 2024-10-03 11:55:57 +04:00
23rd
440ebfcbf6 Removed display of userpic for sponsored messages. 2024-10-03 11:55:57 +04:00
23rd
8d93eb919b Added verified badge to title in panel for web view bots. 2024-10-03 11:55:55 +04:00
John Preston
d3df2dc1e5 Use Data::MediaPreload in sponsored messages. 2024-10-03 11:54:28 +04:00
John Preston
677314dacb Extract Data::MediaPreload. 2024-10-03 11:54:28 +04:00
John Preston
20f53c89ad Fix poll bottom ripple.
Fixes #28471.
2024-10-03 11:54:28 +04:00
John Preston
1cb92ef69a Beta version 5.5.7: Workaround MSVC bug. 2024-10-01 10:55:42 +04:00
John Preston
b518f7c4c4 Beta version 5.5.7.
- Add ability to share QR code of bots.
- Add mention/hashtag autocomplete to Saved GIF caption.
- Fixed display of working hours in profiles.
- Respect the maximum reply quote length.
2024-10-01 10:06:45 +04:00
John Preston
9fdc099c3a Update lib_webview. 2024-10-01 08:57:59 +04:00
23rd
1db8ada2aa Slightly improved code style of history widget. 2024-10-01 08:57:39 +04:00
23rd
b753448052 Changed behavior for sponsored messages to display them only when ready. 2024-10-01 08:57:38 +04:00
23rd
e29c6d2f23 Fixed infinite attempts to draw foreground in userpic builder. 2024-10-01 08:57:37 +04:00
GitHub Action
1ee53ee0c1 Update User-Agent for DNS to Chrome 129.0.0.0. 2024-10-01 08:52:46 +04:00
John Preston
57438867b6 Respect the maximum quote length. 2024-09-30 14:46:18 +04:00
John Preston
86c04424f6 Add mention/hashtag autocomplete to GIF caption. 2024-09-30 14:46:18 +04:00
John Preston
632639d581 Allow custom tab action on all autocomplete fields.
Fixes #28455.
2024-09-30 14:46:18 +04:00
23rd
99c8edb3eb Fixed display of working hours in profiles. 2024-09-30 13:29:19 +03:00
23rd
4582e61cfc Improved code style in newest code from export submodule. 2024-09-30 13:12:28 +03:00
Bohdan Tkachenko
a970fe93c1 Add reactions to export chat history (#28252)
Co-authored-by: 23rd <23rd@vivaldi.net>
2024-09-30 03:02:22 -07:00
GitHub Action
91f5c72cf0 Update User-Agent for DNS to Chrome 128.0.0.0. 2024-09-30 11:03:36 +02:00
John Preston
d2f3d04ad4 Update cmake/lib_base submodules. 2024-09-30 12:47:22 +04:00
John Preston
07b93e1e02 Fix media editing in topics. 2024-09-30 12:47:22 +04:00
23rd
ab5e7b1588 Fixed color of mini icon for drafts with reply when dialog is selected. 2024-09-30 12:47:22 +04:00
23rd
0f4aadddfc Fixed display of corner status of video in sponsored messages. 2024-09-30 12:47:22 +04:00
John Preston
a3c79feeba Always show My Stars menu. 2024-09-30 12:47:22 +04:00
John Preston
e6b76fefa1 Show correct error for discuss groups. 2024-09-30 12:47:22 +04:00
23rd
a37f512949 Fixed emoji display in filenames from document views. 2024-09-30 12:47:22 +04:00
23rd
3f34c0ce37 Slightly improved style of box for warning of web view box. 2024-09-30 12:47:22 +04:00
23rd
7cc9a0b9aa Added ability to share QR code of bots. 2024-09-30 12:47:22 +04:00
23rd
fbaa79f168 Fixed display of outline in box for share of QR code on non-retina. 2024-09-30 12:47:22 +04:00
Ilya Fedin
2b0aa8e418 Add description for external video player option 2024-09-27 14:37:51 +02:00
Vladislav Grudinin
1fa2e76c9b Updated codegen submodule. 2024-09-27 14:37:29 +02:00
Vladislav Grudinin
cb166e2591 Fix build with clang 19.1.0.
Fixes #28283.
2024-09-27 14:37:29 +02:00
xmdn
187c2dda20 Check more protocols while adding link (#28407) 2024-09-26 10:54:16 -07:00
Ilya Fedin
b962d2b550 Update Qt 6.8.0-beta4 -> 6.8.0-rc1 2024-09-26 16:15:25 +02:00
Ilya Fedin
d69bcfa138 Update lib_webview & lib_ui 2024-09-26 16:15:25 +02:00
c0re100
0960a3b21d Fix restrict user in recent actions 2024-09-19 17:48:37 +03:00
John Preston
aec220fd2c Beta version 5.5.6.
- Allow resizing the window used for web apps and games.
- Add mention / hashtag autocomplete in media caption field.
- Add "Include muted chats in folder counters" notifications setting.
- Auto-select URL suggested from clipboard when creating a custom link.
- Update emoji to Unicode 15.1.
- Fix replying with a quote from media album caption.
- Fix viewing custom emoji packs from media album caption.
2024-09-19 15:02:16 +04:00
John Preston
666dacf73b Add 'tonsite' handler information to lib/xdg/*. 2024-09-19 13:19:50 +04:00
John Preston
67ce27afaa Update patches for Qt 6.8 on Windows. 2024-09-19 12:52:18 +04:00
23rd
3f6d184435 Added api support for clicks on sponsored messages with media. 2024-09-19 12:47:54 +04:00
John Preston
a2fad84dae Update API scheme to layer 188. 2024-09-19 12:47:54 +04:00
John Preston
4a84f9fa00 Allow resizing window of web apps / games. 2024-09-19 12:47:46 +04:00
Ilya Fedin
7abc921d20 Revert "Fullscreen hack for Windows 11 native resize"
This reverts commit f7bff01793.
2024-09-19 10:46:46 +02:00
Ilya Fedin
a307e7a798 Replace fullscreen mask workaround with Qt one 2024-09-19 10:46:46 +02:00
Ilya Fedin
3a9eb8463d Try to always use QWidget::screen 2024-09-19 10:46:10 +02:00
John Preston
4befb125e3 Update emoji to Unicode 15.1. 2024-09-19 12:26:52 +04:00
John Preston
ca00a19736 Updated emoji resources to Unicode 15.1. 2024-09-19 10:26:30 +02:00
John Preston
296969bcd1 Add "Ada" library mention to README. 2024-09-19 10:43:38 +04:00
John Preston
3218c30983 Add "Include muted chats in folder counters". 2024-09-19 10:34:01 +04:00
John Preston
3658b32db5 Correctly count 'other accounts unread' in menu. 2024-09-19 10:34:01 +04:00
John Preston
592d46c8f2 Fix emoji packs from album captions. 2024-09-19 10:34:01 +04:00
John Preston
8b86f12c23 Fix quote-reply to album captions. 2024-09-19 10:34:01 +04:00
John Preston
2e2d8d2af3 Add FieldAutocomplete to EditCaptionBox. 2024-09-19 10:34:01 +04:00
John Preston
3eec43cacd Add FieldAutocomplete to SendFilesBox. 2024-09-19 10:34:01 +04:00
John Preston
ba7cd25f21 Extract FieldAutocomplete code. 2024-09-19 10:34:01 +04:00
John Preston
969152e949 Move FieldAutocomplete to ChatHelpers. 2024-09-19 10:34:01 +04:00
23rd
b78443cf2d Added mini icon to drafts with reply. 2024-09-19 10:34:01 +04:00
23rd
4288ba2449 Improved UX in box for link creation when user has link in clipboard. 2024-09-19 10:34:01 +04:00
Ilya Fedin
22a3093815 Check updater exit status on Linux 2024-09-18 08:27:18 +02:00
John Preston
98ba2c7ce4 Version 5.5.5.
- Suggest URL from clipboard when creating a custom link.
- Fix animated topic icons in topic info page.
- Fix excessive getChannelDifference requests.
2024-09-13 21:04:07 +04:00
John Preston
36962b8c62 Fix possible crash in swipe-to-reply. 2024-09-13 21:04:07 +04:00
John Preston
d0a8bd1f03 Use colored topic icons only when required. 2024-09-13 20:06:38 +04:00
John Preston
c9e0f50f0f Revert "Improved color of topic icon in TopicIconView."
This reverts commit a38dcb6ee5.
2024-09-13 20:06:32 +04:00
John Preston
1dc310586d Unregister channel from shortpoll. 2024-09-13 20:06:19 +04:00
23rd
6f71d21bb7 Slightly improved box for link creation when user has link in clipboard. 2024-09-13 17:14:48 +03:00
Ilya Fedin
c93f047056 Add run0 support 2024-09-12 23:08:06 +04:00
530 changed files with 25881 additions and 6025 deletions

2
.gitmodules vendored
View File

@@ -3,7 +3,7 @@
url = https://github.com/telegramdesktop/libtgvoip
[submodule "Telegram/ThirdParty/GSL"]
path = Telegram/ThirdParty/GSL
url = https://github.com/desktop-app/GSL.git
url = https://github.com/Microsoft/GSL.git
[submodule "Telegram/ThirdParty/xxHash"]
path = Telegram/ThirdParty/xxHash
url = https://github.com/Cyan4973/xxHash.git

View File

@@ -64,6 +64,7 @@ Version **1.8.15** was the last that supports older systems
* QR Code generator ([MIT License](https://github.com/nayuki/QR-Code-generator#license))
* CMake ([New BSD License](https://github.com/Kitware/CMake/blob/master/Copyright.txt))
* Hunspell ([LGPL](https://github.com/hunspell/hunspell/blob/master/COPYING.LESSER))
* Ada ([Apache License 2.0](https://github.com/ada-url/ada/blob/main/LICENSE-APACHE))
## Build instructions

View File

@@ -112,6 +112,8 @@ PRIVATE
api/api_bot.h
api/api_chat_filters.cpp
api/api_chat_filters.h
api/api_chat_filters_remove_manager.cpp
api/api_chat_filters_remove_manager.h
api/api_chat_invite.cpp
api/api_chat_invite.h
api/api_chat_links.cpp
@@ -322,6 +324,8 @@ PRIVATE
boxes/sessions_box.h
boxes/share_box.cpp
boxes/share_box.h
boxes/star_gift_box.cpp
boxes/star_gift_box.h
boxes/sticker_set_box.cpp
boxes/sticker_set_box.h
boxes/stickers_box.cpp
@@ -569,6 +573,8 @@ PRIVATE
data/data_lastseen_status.h
data/data_location.cpp
data/data_location.h
data/data_media_preload.cpp
data/data_media_preload.h
data/data_media_rotation.cpp
data/data_media_rotation.h
data/data_media_types.cpp
@@ -604,6 +610,7 @@ PRIVATE
data/data_replies_list.h
data/data_reply_preview.cpp
data/data_reply_preview.h
data/data_report.h
data/data_saved_messages.cpp
data/data_saved_messages.h
data/data_saved_sublist.cpp
@@ -631,6 +638,8 @@ PRIVATE
data/data_thread.h
data/data_types.cpp
data/data_types.h
data/data_unread_value.cpp
data/data_unread_value.h
data/data_user.cpp
data/data_user.h
data/data_user_photos.cpp
@@ -947,6 +956,10 @@ PRIVATE
info/media/info_media_widget.h
info/members/info_members_widget.cpp
info/members/info_members_widget.h
info/peer_gifts/info_peer_gifts_common.cpp
info/peer_gifts/info_peer_gifts_common.h
info/peer_gifts/info_peer_gifts_widget.cpp
info/peer_gifts/info_peer_gifts_widget.h
info/polls/info_polls_results_inner_widget.cpp
info/polls/info_polls_results_inner_widget.h
info/polls/info_polls_results_widget.cpp
@@ -973,6 +986,10 @@ PRIVATE
info/profile/info_profile_values.h
info/profile/info_profile_widget.cpp
info/profile/info_profile_widget.h
info/reactions_list/info_reactions_list_widget.cpp
info/reactions_list/info_reactions_list_widget.h
info/requests_list/info_requests_list_widget.cpp
info/requests_list/info_requests_list_widget.h
info/saved/info_saved_sublists_widget.cpp
info/saved/info_saved_sublists_widget.h
info/settings/info_settings_widget.cpp
@@ -986,6 +1003,7 @@ PRIVATE
info/statistics/info_statistics_list_controllers.h
info/statistics/info_statistics_recent_message.cpp
info/statistics/info_statistics_recent_message.h
info/statistics/info_statistics_tag.h
info/statistics/info_statistics_widget.cpp
info/statistics/info_statistics_widget.h
info/stories/info_stories_inner_widget.cpp
@@ -1022,6 +1040,10 @@ PRIVATE
info/info_wrap_widget.h
inline_bots/bot_attach_web_view.cpp
inline_bots/bot_attach_web_view.h
inline_bots/inline_bot_confirm_prepared.cpp
inline_bots/inline_bot_confirm_prepared.h
inline_bots/inline_bot_downloads.cpp
inline_bots/inline_bot_downloads.h
inline_bots/inline_bot_layout_internal.cpp
inline_bots/inline_bot_layout_internal.h
inline_bots/inline_bot_layout_item.cpp
@@ -1153,6 +1175,8 @@ PRIVATE
media/streaming/media_streaming_player.h
media/streaming/media_streaming_reader.cpp
media/streaming/media_streaming_reader.h
media/streaming/media_streaming_round_preview.cpp
media/streaming/media_streaming_round_preview.h
media/streaming/media_streaming_utility.cpp
media/streaming/media_streaming_utility.h
media/streaming/media_streaming_video_track.cpp
@@ -1492,6 +1516,8 @@ PRIVATE
ui/chat/choose_send_as.h
ui/chat/choose_theme_controller.cpp
ui/chat/choose_theme_controller.h
ui/chat/sponsored_message_bar.cpp
ui/chat/sponsored_message_bar.h
ui/controls/emoji_button_factory.cpp
ui/controls/emoji_button_factory.h
ui/controls/location_picker.cpp
@@ -1523,6 +1549,8 @@ PRIVATE
ui/widgets/expandable_peer_list.h
ui/widgets/label_with_custom_emoji.cpp
ui/widgets/label_with_custom_emoji.h
ui/widgets/chat_filters_tabs_strip.cpp
ui/widgets/chat_filters_tabs_strip.h
ui/countryinput.cpp
ui/countryinput.h
ui/dynamic_thumbnails.cpp
@@ -1840,7 +1868,7 @@ endif()
set_target_properties(Telegram PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_folder})
if (WIN32 AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
if (MSVC)
target_link_libraries(Telegram
PRIVATE
delayimp
@@ -1937,7 +1965,7 @@ if (NOT DESKTOP_APP_DISABLE_AUTOUPDATE AND NOT build_macstore AND NOT build_wins
base/platform/win/base_windows_safe_library.h
)
target_include_directories(Updater PRIVATE ${lib_base_loc})
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
if (MSVC)
target_link_libraries(Updater
PRIVATE
delayimp

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 787 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 832 KiB

After

Width:  |  Height:  |  Size: 935 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 234 KiB

View File

@@ -582,3 +582,55 @@ div.toast_shown {
.bot_button_column_separator {
width: 2px
}
.reactions {
margin: 5px 0;
}
.reactions .reaction {
display: inline-flex;
height: 20px;
border-radius: 15px;
background-color: #e8f5fc;
color: #168acd;
font-weight: bold;
margin-bottom: 5px;
}
.reactions .reaction.active {
background-color: #40a6e2;
color: #fff;
}
.reactions .reaction.paid {
background-color: #fdf6e1;
color: #c58523;
}
.reactions .reaction.active.paid {
background-color: #ecae0a;
color: #fdf6e1;
}
.reactions .reaction .emoji {
line-height: 20px;
margin: 0 5px;
font-size: 15px;
}
.reactions .reaction .userpic:not(:first-child) {
margin-left: -8px;
}
.reactions .reaction .userpic {
display: inline-block;
}
.reactions .reaction .userpic .initials {
font-size: 8px;
}
.reactions .reaction .count {
margin-right: 8px;
line-height: 20px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -12,6 +12,7 @@ body {
margin: 0;
background-color: var(--td-window-bg);
color: var(--td-window-fg);
zoom: var(--td-zoom-percentage);
}
html.custom_scroll ::-webkit-scrollbar {

View File

@@ -72,6 +72,9 @@ var IV = {
}
},
frameKeyDown: function (e) {
const key0 = (e.key === '0')
|| (e.code === 'Key0')
|| (e.keyCode === 48);
const keyW = (e.key === 'w')
|| (e.code === 'KeyW')
|| (e.keyCode === 87);
@@ -81,12 +84,12 @@ var IV = {
const keyM = (e.key === 'm')
|| (e.code === 'KeyM')
|| (e.keyCode === 77);
if ((e.metaKey || e.ctrlKey) && (keyW || keyQ || keyM)) {
if ((e.metaKey || e.ctrlKey) && (keyW || keyQ || keyM || key0)) {
e.preventDefault();
IV.notify({
event: 'keydown',
modifier: e.ctrlKey ? 'ctrl' : 'cmd',
key: keyW ? 'w' : keyQ ? 'q' : 'm',
key: key0 ? '0' : keyW ? 'w' : keyQ ? 'q' : 'm',
});
} else if (e.key === 'Escape' || e.keyCode === 27) {
e.preventDefault();

View File

@@ -453,6 +453,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_username_app_not_found" = "Bot application not found.";
"lng_username_link" = "This link opens a chat with you:";
"lng_username_copied" = "Link copied to clipboard.";
"lng_username_text_copied" = "Username copied to clipboard.";
"lng_usernames_edit" = "click to edit";
"lng_usernames_active" = "active";
@@ -487,6 +488,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_collectible_phone_info" = "This phone number was bought on **Fragment** on {date} for {price}";
"lng_collectible_phone_copy" = "Copy Phone Number";
"lng_collectible_learn_more" = "Learn More";
"lng_collectible_phone_copied" = "Phone number copied to clipboard.";
"lng_settings_section_info" = "Info";
@@ -497,8 +499,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_notify_global" = "Global settings";
"lng_settings_notify_title" = "Notifications for chats";
"lng_settings_desktop_notify" = "Desktop notifications";
"lng_settings_native_title" = "Native notifications";
"lng_settings_native_title" = "System integration";
"lng_settings_use_windows" = "Use Windows notifications";
"lng_settings_skip_in_focus" = "Respect system Focus mode";
"lng_settings_use_native_notifications" = "Use native notifications";
"lng_settings_notifications_position" = "Location on the screen";
"lng_settings_notifications_count" = "Notifications count";
@@ -508,6 +511,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_alert_linux" = "Draw attention to the window";
"lng_settings_badge_title" = "Badge counter";
"lng_settings_include_muted" = "Include muted chats in unread count";
"lng_settings_include_muted_folders" = "Include muted chats in folder counters";
"lng_settings_count_unread" = "Count unread messages";
"lng_settings_events_title" = "Events";
"lng_settings_events_joined" = "Contact joined Telegram";
@@ -678,6 +682,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_messages_privacy" = "Messages";
"lng_settings_voices_privacy" = "Voice messages";
"lng_settings_bio_privacy" = "Bio";
"lng_settings_gifts_privacy" = "Gifts";
"lng_settings_birthday_privacy" = "Date of Birth";
"lng_settings_privacy_premium" = "Only subscribers of {link} can restrict receiving voice messages.";
"lng_settings_privacy_premium_link" = "Telegram Premium";
@@ -1158,19 +1163,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_blocked_list_subtitle#other" = "{count} blocked users";
"lng_edit_privacy_everyone" = "Everybody";
"lng_edit_privacy_no_miniapps" = "Not Mini Apps";
"lng_edit_privacy_contacts" = "My contacts";
"lng_edit_privacy_close_friends" = "Close friends";
"lng_edit_privacy_contacts_and_premium" = "Contacts & Premium";
"lng_edit_privacy_contacts_and_miniapps" = "Contacts & Mini Apps";
"lng_edit_privacy_nobody" = "Nobody";
"lng_edit_privacy_premium" = "Premium users";
"lng_edit_privacy_miniapps" = "Mini Apps";
"lng_edit_privacy_exceptions" = "Add exceptions";
"lng_edit_privacy_user_types" = "User types";
"lng_edit_privacy_users_and_groups" = "Users and groups";
"lng_edit_privacy_premium_status" = "all Telegram Premium subscribers";
"lng_edit_privacy_miniapps_status" = "web mini apps that you use";
"lng_edit_privacy_exceptions_count#one" = "{count} user";
"lng_edit_privacy_exceptions_count#other" = "{count} users";
"lng_edit_privacy_exceptions_premium_and" = "Premium & {users}";
"lng_edit_privacy_exceptions_miniapps_and" = "Mini Apps & {users}";
"lng_edit_privacy_exceptions_add" = "Add users";
"lng_edit_privacy_phone_number_title" = "Phone number privacy";
@@ -1224,6 +1234,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_privacy_birthday_yet" = "You haven't entered your date of birth yet.\n{link}";
"lng_edit_privacy_birthday_yet_link" = "Add my birthday >";
"lng_edit_privacy_gifts_title" = "Gifts";
"lng_edit_privacy_gifts_header" = "Who can display gifts on my profile";
"lng_edit_privacy_gifts_always_empty" = "Always allow";
"lng_edit_privacy_gifts_never_empty" = "Never allow";
"lng_edit_privacy_gifts_exceptions" = "Choose whether gifts from specific senders need your approval before they're visible to others on your profile.";
"lng_edit_privacy_gifts_always_title" = "Always allow";
"lng_edit_privacy_gifts_never_title" = "Never allow";
"lng_edit_privacy_calls_title" = "Calls";
"lng_edit_privacy_calls_header" = "Who can call me";
"lng_edit_privacy_calls_always_empty" = "Always allow";
@@ -1322,6 +1340,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_similar_channels#other" = "{count} similar channels";
"lng_profile_saved_messages#one" = "{count} saved message";
"lng_profile_saved_messages#other" = "{count} saved messages";
"lng_profile_peer_gifts#one" = "{count} gift";
"lng_profile_peer_gifts#other" = "{count} gifts";
"lng_profile_participants_section" = "Members";
"lng_profile_subscribers_section" = "Subscribers";
"lng_profile_add_contact" = "Add Contact";
@@ -1376,6 +1396,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_copy_fullname" = "Copy Name";
"lng_profile_photo_by_you" = "photo set by you";
"lng_profile_public_photo" = "public photo";
"lng_profile_administrators#one" = "{count} administrator";
"lng_profile_administrators#other" = "{count} administrators";
"lng_profile_manage" = "Channel settings";
"lng_invite_upgrade_title" = "Upgrade to Premium";
"lng_invite_upgrade_group_invite#one" = "{users} only accepts invitations to groups from Contacts and **Premium** users.";
@@ -1452,11 +1475,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_open_app" = "Open App";
"lng_profile_open_app_about" = "By launching this mini app, you agree to the {terms}.";
"lng_profile_open_app_terms" = "Terms of Service for Mini Apps";
"lng_profile_bot_permissions_title" = "Allow access to";
"lng_profile_bot_emoji_status_access" = "Emoji Status";
"lng_info_add_as_contact" = "Add to contacts";
"lng_profile_shared_media" = "Shared media";
"lng_profile_suggest_photo" = "Suggest Profile Photo";
"lng_profile_suggest_photo_from_clipboard" = "Suggest From Clipboard";
"lng_profile_set_photo_for" = "Set Profile Photo";
"lng_profile_set_photo_for_from_clipboard" = "Set From Clipboard";
"lng_profile_photo_reset" = "Reset to Original";
"lng_profile_photo_from_clipboard" = "From clipboard";
"lng_profile_suggest_sure" = "You can suggest {user} to set this photo for their Telegram profile.";
"lng_profile_suggest_button" = "Suggest";
"lng_profile_set_personal_sure" = "Only you will see this photo and it will replace any photo {user} sets for themselves.";
@@ -1585,6 +1613,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_manage_peer_bot_public_link" = "Public Link";
"lng_manage_peer_bot_public_links" = "Public Links";
"lng_manage_peer_bot_balance" = "Balance";
"lng_manage_peer_bot_balance_currency" = "Toncoin";
"lng_manage_peer_bot_balance_credits" = "Stars";
"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";
@@ -1659,6 +1689,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_report_and_ban_button" = "Ban user";
"lng_report_details_about" = "Please enter any additional details relevant to your report.";
"lng_report_details" = "Additional Details";
"lng_report_details_optional" = "Add Comment (Optional)";
"lng_report_details_non_optional" = "Add Comment";
"lng_report_details_message_about" = "Please help us by telling what is wrong with the message you have selected";
"lng_report_reason_spam" = "Spam";
"lng_report_reason_fake" = "Fake Account";
"lng_report_reason_violence" = "Violence";
@@ -1852,8 +1885,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_proximity_distance_km#other" = "{count} km";
"lng_action_webview_data_done" = "Data from the \"{text}\" button was transferred to the bot.";
"lng_action_gift_received" = "{user} sent you a gift for {cost}";
"lng_action_gift_received_me" = "You sent to {user} a gift for {cost}";
"lng_action_gift_sent" = "You sent a gift for {cost}";
"lng_action_gift_received_anonymous" = "Unknown user sent you a gift for {cost}";
"lng_action_gift_for_stars#one" = "{count} Star";
"lng_action_gift_for_stars#other" = "{count} Stars";
"lng_action_gift_got_subtitle" = "Gift from {user}";
"lng_action_gift_got_stars_text#one" = "Display this gift on your page or convert it to **{count}** Star.";
"lng_action_gift_got_stars_text#other" = "Display this gift on your page or convert it to **{count}** Stars.";
"lng_action_gift_got_gift_text" = "You can keep this gift on your page.";
"lng_action_gift_can_remove_text" = "You can remove this gift from your page.";
"lng_action_gift_sent_subtitle" = "Gift for {user}";
"lng_action_gift_sent_text#one" = "{user} can display this gift on their page or convert it to {count} Star.";
"lng_action_gift_sent_text#other" = "{user} can display this gift on their page or convert it to {count} Stars.";
"lng_action_gift_premium_months#one" = "{count} Month Premium";
"lng_action_gift_premium_months#other" = "{count} Months Premium";
"lng_action_gift_premium_about" = "Subscription for exclusive Telegram features.";
"lng_action_suggested_photo_me" = "You suggested this photo for {user}'s Telegram profile.";
"lng_action_suggested_photo" = "{user} suggests this photo for your Telegram profile.";
"lng_action_suggested_photo_button" = "View Photo";
@@ -1915,6 +1961,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_similar_channels_premium_all_link" = "Telegram Premium";
"lng_similar_channels_show_more" = "Show more channels";
"lng_peer_gifts_title" = "Gifts";
"lng_peer_gifts_about" = "These gifts were sent to {user} by other users.";
"lng_peer_gifts_about_mine" = "These gifts were sent to you by other users. Click on a gift to convert it to Stars or change its privacy settings.";
"lng_premium_gift_duration_months#one" = "for {count} month";
"lng_premium_gift_duration_months#other" = "for {count} months";
"lng_premium_gift_duration_years#one" = "for {count} year";
@@ -2071,6 +2121,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_channel_public_link_copied" = "Link copied to clipboard.";
"lng_context_about_private_link" = "This link will only work for members of this chat.";
"lng_public_post_private_hint_ctrl" = "Use Ctrl+Click to copy a non-public link.";
"lng_public_post_private_hint_cmd" = "Use Cmd+Click to copy a non-public link.";
"lng_forwarded" = "Forwarded from {user}";
"lng_forwarded_story" = "Story from {user}";
@@ -2089,8 +2141,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_recommended_message_title" = "Recommended";
"lng_edited" = "edited";
"lng_commented" = "commented";
"lng_approximate" = "appx.";
"lng_edited_date" = "Edited: {date}";
"lng_sent_date" = "Sent: {date}";
"lng_approximate_about" = "Estimated date of video publishing.";
"lng_views_tooltip#one" = "Views: {count}";
"lng_views_tooltip#other" = "Views: {count}";
"lng_forwards_tooltip#one" = "Shares: {count}";
@@ -2125,6 +2179,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_media_cancel" = "Cancel";
"lng_media_video" = "Video";
"lng_media_audio" = "Voice message";
"lng_media_round" = "Video message";
"lng_media_auto_settings" = "Automatic media download";
"lng_media_auto_in_private" = "In private chats";
@@ -2378,6 +2433,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_credits_summary_history_tab_out" = "Outgoing";
"lng_credits_summary_history_entry_inner_in" = "In-App Purchase";
"lng_credits_summary_balance" = "Balance";
"lng_credits_more_options" = "More Options";
"lng_credits_balance_me" = "your balance";
"lng_credits_buy_button" = "Buy More Stars";
"lng_credits_gift_button" = "Gift Stars to Friends";
"lng_credits_box_out_title" = "Confirm Your Purchase";
"lng_credits_box_out_sure#one" = "Do you want to buy **\"{text}\"** in **{bot}** for **{count} Star**?";
@@ -2415,18 +2473,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_credits_box_history_entry_gift_name" = "Received Gift";
"lng_credits_box_history_entry_giveaway_name" = "Received Prize";
"lng_credits_box_history_entry_gift_sent" = "Sent Gift";
"lng_credits_box_history_entry_gift_converted" = "Converted Gift";
"lng_credits_box_history_entry_gift_unavailable" = "Unavailable";
"lng_credits_box_history_entry_gift_sold_out" = "This gift has sold out";
"lng_credits_box_history_entry_gift_out_about" = "With Stars, **{user}** will be able to unlock content and services on Telegram.\n{link}";
"lng_credits_box_history_entry_gift_in_about" = "Use Stars to unlock content and services on Telegram. {link}";
"lng_credits_box_history_entry_gift_about_link" = "See Examples {emoji}";
"lng_credits_box_history_entry_gift_examples" = "Examples";
"lng_credits_box_history_entry_ads" = "Ads Platform";
"lng_credits_box_history_entry_premium_bot" = "Stars Top-Up";
"lng_credits_box_history_entry_api" = "Paid Broadcast";
"lng_credits_box_history_entry_floodskip_about#one" = "{count} Message";
"lng_credits_box_history_entry_floodskip_about#other" = "{count} Messages";
"lng_credits_box_history_entry_floodskip_row" = "Messages";
"lng_credits_box_history_entry_via_premium_bot" = "Premium Bot";
"lng_credits_box_history_entry_id" = "Transaction ID";
"lng_credits_box_history_entry_id_copied" = "Transaction ID copied to clipboard.";
"lng_credits_box_history_entry_success_date" = "Transaction date";
"lng_credits_box_history_entry_success_url" = "Transaction link";
"lng_credits_box_history_entry_media" = "Media";
"lng_credits_box_history_entry_message" = "Message";
"lng_credits_box_history_entry_about" = "You can dispute this transaction {link}.";
"lng_credits_box_history_entry_about_link" = "here";
"lng_credits_box_history_entry_reaction_name" = "Star Reaction";
@@ -2461,6 +2527,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_credits_small_balance_about" = "Buy **Stars** and use them on **{bot}** and other miniapps.";
"lng_credits_small_balance_reaction" = "Buy **Stars** and send them to {channel} to support their posts.";
"lng_credits_small_balance_subscribe" = "Buy **Stars** and subscribe to **{channel}** and other channels.";
"lng_credits_small_balance_star_gift" = "Buy **Stars** to send gifts to {user} and other contacts.";
"lng_credits_small_balance_fallback" = "Buy **Stars** to unlock content and services on Telegram.";
"lng_credits_purchase_blocked" = "Sorry, you can't purchase this item with Telegram Stars.";
"lng_credits_enough" = "You have enough stars at the moment. {link}";
@@ -2960,6 +3027,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_link_reason_unclaimed" = "Incomplete Giveaway";
"lng_gift_link_reason_chosen" = "You were selected by the channel";
"lng_gift_link_label_date" = "Date";
"lng_gift_link_label_first_sale" = "First Sale";
"lng_gift_link_label_last_sale" = "Last Sale";
"lng_gift_link_label_value" = "Value";
"lng_gift_link_also_send" = "You can also {link} to a friend as a gift.";
"lng_gift_link_also_send_link" = "send this link";
"lng_gift_link_use" = "Use Link";
@@ -2980,6 +3050,59 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_stars_incoming" = "Use Stars to unlock content and services on Telegram.";
"lng_gift_until" = "Until";
"lng_gift_premium_or_stars" = "Gift Premium or Stars";
"lng_gift_premium_subtitle" = "Gift Premium";
"lng_gift_premium_about" = "Give {name} access to exclusive features with Telegram Premium. {features}";
"lng_gift_premium_features" = "See Features >";
"lng_gift_premium_label" = "Premium";
"lng_gift_stars_subtitle" = "Gift Stars";
"lng_gift_stars_about" = "Give {name} gifts that can be kept on your profile or converted to Stars. {link}";
"lng_gift_stars_link" = "What are Stars >";
"lng_gift_stars_limited" = "limited";
"lng_gift_stars_sold_out" = "sold out";
"lng_gift_stars_tabs_all" = "All Gifts";
"lng_gift_stars_tabs_limited" = "Limited";
"lng_gift_send_title" = "Send a Gift";
"lng_gift_send_message" = "Enter Message";
"lng_gift_send_anonymous" = "Hide My Name";
"lng_gift_send_anonymous_about" = "You can hide your name and message from visitors to {user}'s profile. {recipient} will still see your name and message.";
"lng_gift_send_premium_about" = "Only {user} will see your message.";
"lng_gift_send_button" = "Send a Gift for {cost}";
"lng_gift_sent_title" = "Gift Sent!";
"lng_gift_sent_about#one" = "You spent **{count}** Star from your balance.";
"lng_gift_sent_about#other" = "You spent **{count}** Stars from your balance.";
"lng_gift_limited_of_one" = "unique";
"lng_gift_limited_of_count" = "1 of {amount}";
"lng_gift_anonymous_hint" = "Only you can see the sender's name.";
"lng_gift_hidden_hint" = "This gift is hidden. Only you can see it.";
"lng_gift_visible_hint" = "This gift is visible to visitors of your page.";
"lng_gift_availability" = "Availability";
"lng_gift_from_hidden" = "Hidden User";
"lng_gift_availability_left#one" = "{count} of {amount} left";
"lng_gift_availability_left#other" = "{count} of {amount} left";
"lng_gift_availability_none" = "None of {amount} left";
"lng_gift_display_on_page" = "Display on my Page";
"lng_gift_display_on_page_hide" = "Hide from my Page";
"lng_gift_convert_to_stars#one" = "Convert to {count} Star";
"lng_gift_convert_to_stars#other" = "Convert to {count} Stars";
"lng_gift_convert_sure_title" = "Convert Gift to Stars";
"lng_gift_convert_sure_confirm#one" = "Do you want to convert this gift from {user} to **{count} Star**?";
"lng_gift_convert_sure_confirm#other" = "Do you want to convert this gift from {user} to **{count} Stars**?";
"lng_gift_convert_sure_limit#one" = "Conversion is available for the next **{count} day**.";
"lng_gift_convert_sure_limit#other" = "Conversion is available for the next **{count} days**.";
"lng_gift_convert_sure_caution" = "This action cannot be undone. This will permanently destroy the gift.";
"lng_gift_convert_sure" = "Convert";
"lng_gift_display_done" = "The gift is now shown on your profile page.";
"lng_gift_display_done_hide" = "The gift is now hidden from your profile page.";
"lng_gift_got_stars#one" = "You got **{count} Star** for this gift.";
"lng_gift_got_stars#other" = "You got **{count} Stars** for this gift.";
"lng_gift_sold_out_title" = "Sold Out!";
"lng_gift_sold_out_text#one" = "All {count} gift was already sold.";
"lng_gift_sold_out_text#other" = "All {count} gifts were already sold.";
"lng_gift_send_small" = "send a gift";
"lng_gift_sell_small#one" = "sell for {count} Star";
"lng_gift_sell_small#other" = "sell for {count} Stars";
"lng_accounts_limit_title" = "Limit Reached";
"lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected account.";
"lng_accounts_limit1#other" = "You have reached the limit of **{count}** connected accounts.";
@@ -3168,11 +3291,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_record_cancel" = "Release outside this field to cancel";
"lng_record_cancel_stories" = "Release outside to cancel";
"lng_record_lock_cancel_sure" = "Do you want to stop recording and discard your voice message?";
"lng_record_lock_cancel_sure_round" = "Do you want to stop recording and discard your video message?";
"lng_record_listen_cancel_sure" = "Do you want to discard your recorded voice message?";
"lng_record_listen_cancel_sure_round" = "Do you want to discard your recorded video message?";
"lng_record_lock_discard" = "Discard";
"lng_record_hold_tip" = "Please hold the mouse button pressed to record a voice message.";
"lng_record_voice_tip" = "Hold to record audio. Click to switch to video.";
"lng_record_video_tip" = "Hold to record video. Click to switch to audio.";
"lng_record_audio_problem" = "Could not start audio recording. Please check your microphone.";
"lng_record_video_problem" = "Could not start video recording. Please check your camera.";
"lng_record_once_first_tooltip" = "Click to set this message to **Play Once**.";
"lng_record_once_active_tooltip" = "The recipient will be able to listen only once.";
"lng_record_once_active_video" = "The recipient will be able to watch only once.";
"lng_will_be_notified" = "Subscribers will be notified when you post.";
"lng_wont_be_notified" = "Subscribers will receive a silent notification.";
"lng_willbe_history" = "Select a chat to start messaging";
@@ -3201,6 +3331,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_scheduled_send_now" = "Send message now?";
"lng_scheduled_send_now_many#one" = "Send {count} message now?";
"lng_scheduled_send_now_many#other" = "Send {count} messages now?";
"lng_scheduled_video_tip_title" = "Improving video...";
"lng_scheduled_video_tip_text" = "The video will be published after it's optimized for the best viewing experience.";
"lng_scheduled_video_tip" = "Processing video may take a few minutes.";
"lng_scheduled_video_published" = "Video Published.";
"lng_scheduled_video_view" = "View";
"lng_replies_view#one" = "View {count} Reply";
"lng_replies_view#other" = "View {count} Replies";
@@ -3222,6 +3357,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_replies_discussion_started" = "Discussion started";
"lng_replies_no_comments" = "No comments here yet...";
"lng_verification_codes" = "Verification Codes";
"lng_archived_name" = "Archived chats";
"lng_archived_add" = "Archive";
"lng_archived_remove" = "Unarchive";
@@ -3246,7 +3383,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_open_link" = "Open";
"lng_allow_bot_pass" = "Allow {bot_name} to pass your Telegram name and ID (not your phone number) to the web pages you open via this bot?";
"lng_allow_bot" = "Allow";
"lng_allow_bot_webview" = "{bot_name} would like to open its web app to proceed.\n\nIt will be able to access your **IP address** and basic device info.";
"lng_allow_bot_webview_details" = "More about this bot {emoji}";
"lng_allow_bot_webview_details_about" = "To launch this web app, you will connect to its website.\n\nIt will be able to access your **IP address** and basic device info.";
"lng_url_auth_open_confirm" = "Do you want to open {link}?";
"lng_url_auth_login_option" = "Log in to {domain} as {user}";
"lng_url_auth_allow_messages" = "Allow {bot} to send me messages";
@@ -3281,6 +3419,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_bot_settings" = "Settings";
"lng_bot_open" = "Open Bot";
"lng_bot_terms" = "Terms of Use";
"lng_bot_privacy" = "Privacy Policy";
"lng_bot_reload_page" = "Reload Page";
"lng_bot_add_to_menu" = "{bot} asks your permission to be added as an option to your attachment menu so you can access it from any chat.";
"lng_bot_add_to_menu_done" = "Bot added to the menu.";
@@ -3296,6 +3435,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_bot_add_to_side_menu_done" = "Bot added to the main menu.";
"lng_bot_no_scan_qr" = "QR Codes for bots are not supported on Desktop. Please use one of Telegram's mobile apps.";
"lng_bot_no_share_story" = "Sharing to Stories is not supported on Desktop. Please use one of Telegram's mobile apps.";
"lng_bot_emoji_status_confirm" = "Confirm";
"lng_bot_emoji_status_title" = "Set Emoji Status";
"lng_bot_emoji_status_text" = "Do you want to set this emoji status suggested by {bot}?";
"lng_bot_emoji_status_access_text" = "{bot} requests access to set your **emoji status**. You will be able to revoke this access in the profile page of {name}.";
"lng_bot_emoji_status_access_allow" = "Allow";
"lng_bot_share_prepared_title" = "Share Message";
"lng_bot_share_prepared_about" = "{bot} mini app suggests you to send this message to a chat you select.";
"lng_bot_share_prepared_button" = "Share With...";
"lng_bot_download_file" = "Download File";
"lng_bot_download_file_sure" = "{bot} suggests you download the following file:";
"lng_bot_download_file_button" = "Download";
"lng_bot_download_starting" = "Starting...";
"lng_bot_download_failed" = "Failed. {retry}";
"lng_bot_download_retry" = "Retry";
"lng_bot_status_users#one" = "{count} monthly user";
"lng_bot_status_users#other" = "{count} monthly users";
@@ -3498,11 +3651,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_animated_reactions_many#one" = "Reactions contain emoji from **{count} pack**.";
"lng_context_animated_reactions_many#other" = "Reactions contain emoji from **{count} packs**.";
"lng_context_noforwards_info_channel" = "Copying and forwarding is not allowed in this channel.";
"lng_context_noforwards_info_group" = "Copying and forwarding is not allowed in this group.";
"lng_context_noforwards_info_bot" = "Copying and forwarding is not allowed from this bot.";
"lng_context_spoiler_effect" = "Hide with Spoiler";
"lng_context_disable_spoiler" = "Remove Spoiler";
"lng_context_make_paid" = "Make This Content Paid";
"lng_context_change_price" = "Change Price";
"lng_context_mention" = "Mention";
"lng_context_search_from" = "Search messages";
"lng_factcheck_title" = "Fact Check";
"lng_factcheck_placeholder" = "Add Facts or Context";
"lng_factcheck_whats_this" = "what's this?";
@@ -3600,6 +3760,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_reply_in_another_title" = "Reply in...";
"lng_reply_in_another_chat" = "Reply in Another Chat";
"lng_reply_in_author" = "Message author";
"lng_reply_in_chats_list" = "Your chats";
"lng_reply_show_in_chat" = "Show in Chat";
"lng_reply_remove" = "Do Not Reply";
"lng_reply_about_quote" = "You can select a specific part to quote.";
@@ -3608,6 +3770,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_reply_header_short" = "Reply";
"lng_reply_quote_selected" = "Quote Selected";
"lng_reply_from_private_chat" = "This reply is from a private chat.";
"lng_reply_quote_long_title" = "Quote too long!";
"lng_reply_quote_long_text" = "The selected text is too long to quote.";
"lng_link_options_header" = "Link Preview Settings";
"lng_link_header_short" = "Link";
"lng_link_move_up" = "Move Up";
@@ -3785,6 +3949,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_mediaview_downloads" = "Downloads";
"lng_mediaview_playback_speed" = "Playback speed: {speed}";
"lng_mediaview_rotate_video" = "Rotate video";
"lng_mediaview_quality_auto" = "Auto";
"lng_theme_preview_title" = "Theme Preview";
"lng_theme_preview_generating" = "Generating color theme preview...";
@@ -3866,7 +4031,9 @@ 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 (webkitgtk-6.0/webkit2gtk-4.1/webkit2gtk-4.0) using your package manager.";
"lng_payments_webview_install_webkit" = "Please install WebKitGTK (webkit2gtk-4.1/webkit2gtk-4.0) using your package manager.";
"lng_payments_webview_enable_opengl" = "Please enable OpenGL in application settings.";
"lng_payments_webview_switch_x11" = "Unsupported display server. Please switch to X11.";
"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";
@@ -4226,13 +4393,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_rights_edit_admin_rank_about" = "A title that members will see instead of '{title}'.";
"lng_rights_about_add_admins_yes" = "This admin will be able to add new admins with equal or fewer rights.";
"lng_rights_about_add_admins_no" = "This admin will not be able to add new admins.";
"lng_rights_about_by" = "This admin promoted by {user} at {date}.";
"lng_rights_about_by" = "This admin promoted by {user} on {date}.";
"lng_rights_about_admin_cant_edit" = "You can't edit the rights of this admin.";
"lng_rights_about_restriction_cant_edit" = "You cannot change the restrictions for this user.";
"lng_rights_restriction_for_all" = "This option is disabled for all members in Group Permissions.";
"lng_rights_permission_for_all" = "This option is enabled for all members in Group Permissions.";
"lng_rights_permission_unavailable" = "This permission is not available in public groups.";
"lng_rights_permission_in_discuss" = "This permission is not available in discussion groups.";
"lng_rights_permission_cant_edit" = "You cannot change this permission.";
"lng_rights_user_restrictions" = "User permissions";
"lng_rights_user_restrictions_header" = "What can this member do?";
@@ -4304,8 +4472,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_rights_chat_files" = "Files";
"lng_rights_chat_voice_messages" = "Voice messages";
"lng_rights_chat_video_messages" = "Video messages";
"lng_rights_chat_restricted_by" = "Restricted by {user} at {date}.";
"lng_rights_chat_banned_by" = "Banned by {user} at {date}.";
"lng_rights_chat_restricted_by" = "Restricted by {user} on {date}.";
"lng_rights_chat_banned_by" = "Banned by {user} on {date}.";
"lng_rights_chat_banned_until_header" = "Restricted until";
"lng_rights_chat_banned_forever" = "Forever";
"lng_rights_chat_banned_day#one" = "For {count} day";
@@ -4914,6 +5082,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_outdated_now" = "So Telegram Desktop can update to newer versions.";
"lng_filters_all" = "All chats";
"lng_filters_all_short" = "All";
"lng_filters_setup" = "Edit";
"lng_filters_title" = "Folders";
"lng_filters_subtitle" = "My folders";
@@ -4968,6 +5137,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"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_view_subtitle" = "Tabs view";
"lng_filters_vertical" = "Tabs on the left";
"lng_filters_horizontal" = "Tabs at the top";
"lng_filters_delete_sure" = "Are you sure you want to delete this folder? This will also deactivate all the invite links created to share this folder.";
"lng_filters_link" = "Share Folder";
@@ -5087,13 +5259,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_sponsored_revenued_subtitle" = "Telegram Ads are very different from ads on other platforms. Ads such as this one:";
"lng_sponsored_revenued_info1_title" = "Respect Your Privacy";
"lng_sponsored_revenued_info1_description" = "Ads on Telegram do not use your personal information and are based on the channel in which you see them.";
"lng_sponsored_revenued_info1_bot_description" = "Ads on Telegram do not use your personal information and are based on the mini app in which you see them.";
"lng_sponsored_revenued_info2_title" = "Help the Channel Creator";
"lng_sponsored_revenued_info2_bot_title" = "Help the Bot Developer";
"lng_sponsored_revenued_info2_description" = "50% of the revenue from Telegram Ads goes to the owner of the channel where they are displayed.";
"lng_sponsored_revenued_info2_bot_description" = "50% of the revenue from Telegram Ads goes to the developer of the mini app where they are displayed.";
"lng_sponsored_revenued_info3_title" = "Can Be Removed";
"lng_sponsored_revenued_info3_description#one" = "You can turn off ads by subscribing to {link}, and Level {count} channels can remove them for their subscribers.";
"lng_sponsored_revenued_info3_description#other" = "You can turn off ads by subscribing to {link}, and Level {count} channels can remove them for their subscribers.";
"lng_sponsored_revenued_info3_bot_description" = "You can turn off ads in mini apps by subscribing to {link}.";
"lng_sponsored_revenued_footer_title" = "Can I Launch an Ad?";
"lng_sponsored_revenued_footer_description" = "Anyone can create an ad to display in this channel — with minimal budgets. Check out the **Telegram Ad Platform** for details. {link}";
"lng_sponsored_revenued_footer_bot_description" = "Anyone can create an ad to display in this bot — with minimal budgets. Check out the **Telegram Ad Platform** for details. {link}";
"lng_sponsored_top_bar_hide" = "remove";
"lng_telegram_features_url" = "https://t.me/TelegramTips";
@@ -5397,6 +5575,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_channel_earn_title" = "Monetization";
"lng_channel_earn_about" = "Telegram shares 50% of the revenue from ads displayed in your channel as rewards. {link}";
"lng_channel_earn_about_bot" = "Telegram shares 50% of the revenue from ads displayed in your bot. {link}";
"lng_channel_earn_about_link" = "Learn more {emoji}";
"lng_channel_earn_overview_title" = "Rewards overview";
"lng_channel_earn_available" = "Rewards available for collection";
@@ -5429,8 +5608,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_channel_earn_cpm#one" = "{emoji} {count} CPM";
"lng_channel_earn_cpm#other" = "{emoji} {count} CPM";
"lng_channel_earn_learn_title" = "Earn From Your Channel";
"lng_channel_earn_bot_learn_title" = "Earn From Your Bot";
"lng_channel_earn_learn_in_subtitle" = "Telegram Ads";
"lng_channel_earn_learn_in_about" = "Telegram can display ads in your channel.";
"lng_channel_earn_learn_bot_in_about" = "Telegram can display ads in your bot.";
"lng_channel_earn_learn_split_subtitle" = "50:50 revenue split";
"lng_channel_earn_learn_split_about" = "You can receive 50% of the ad revenue as rewards in TON.";
"lng_channel_earn_learn_out_subtitle" = "Flexible withdrawals";
@@ -5467,6 +5648,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_bot_earn_credits_out_minimal" = "You cannot withdraw less than {link}.";
"lng_bot_earn_credits_out_minimal_link#one" = "{count} star";
"lng_bot_earn_credits_out_minimal_link#other" = "{count} stars";
"lng_bot_copy_text_tooltip" = "Copy to Clipboard: {text}";
"lng_contact_add" = "Add";
"lng_contact_send_message" = "Message";
@@ -5477,6 +5659,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_iv_window_title" = "Instant View";
"lng_iv_wrong_layout" = "Wrong layout?";
"lng_iv_not_supported" = "This link appears to be invalid.";
"lng_iv_zoom_tooltip_ctrl" = "Hold Ctrl to zoom by 5%.\nHold Alt to zoom by 1%.";
"lng_iv_zoom_tooltip_cmd" = "Hold Cmd to zoom by 5%.\nHold Alt to zoom by 1%.";
"lng_limit_download_title" = "Download speed limited";
"lng_limit_download_subscribe" = "Subscribe to {link} to increase download speed {increase}.";
@@ -5514,6 +5698,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_channels_recommended" = "Similar channels";
"lng_bot_apps_your" = "Apps you use";
"lng_bot_apps_popular" = "Grossing apps";
"lng_bot_apps_which" = "Which apps are included here? {link}";
"lng_bot_apps_which_link" = "Learn >";
"lng_popular_apps_info_title" = "Top Mini Apps";
"lng_popular_apps_info_text" = "This catalogue ranks mini apps based on their daily revenue, measured in Stars. To be listed, developers must set their main mini apps in {bot} (as described {link}), have over **1,000** daily users, and earn a daily revenue above **1,000** Stars, based on the weekly average.";
"lng_popular_apps_info_bot" = "@botfather";
"lng_popular_apps_info_here" = "here";
"lng_popular_apps_info_url" = "https://core.telegram.org/bots/webapps#launching-the-main-mini-app";
"lng_popular_apps_info_confirm" = "Understood";
"lng_font_box_title" = "Choose font family";
"lng_font_default" = "Default";
@@ -5550,6 +5743,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_qr_box_quality1" = "Normal";
"lng_qr_box_quality2" = "High";
"lng_qr_box_quality3" = "Very High";
"lng_qr_box_transparent_background" = "Transparent Background";
"lng_qr_box_font_size" = "Font size";
// Wnd specific

View File

@@ -28,6 +28,7 @@
<file alias="collectible_phone.tgs">../../animations/collectible_phone.tgs</file>
<file alias="search.tgs">../../animations/search.tgs</file>
<file alias="noresults.tgs">../../animations/noresults.tgs</file>
<file alias="hello_status.tgs">../../animations/hello_status.tgs</file>
<file alias="dice_idle.tgs">../../animations/dice/dice_idle.tgs</file>
<file alias="dart_idle.tgs">../../animations/dice/dart_idle.tgs</file>

View File

@@ -7,6 +7,7 @@
<file alias="art/logo_256.png">../../art/logo_256.png</file>
<file alias="art/logo_256_no_margin.png">../../art/logo_256_no_margin.png</file>
<file alias="art/themeimage.jpg">../../art/themeimage.jpg</file>
<file alias="art/round_placeholder.jpg">../../art/round_placeholder.jpg</file>
<file alias="day-blue.tdesktop-theme">../../day-blue.tdesktop-theme</file>
<file alias="night.tdesktop-theme">../../night.tdesktop-theme</file>
<file alias="night-green.tdesktop-theme">../../night-green.tdesktop-theme</file>

View File

@@ -10,7 +10,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="5.5.4.0" />
Version="5.8.2.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>

View File

@@ -16,6 +16,8 @@
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness>
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
</windowsSettings>
</application>

View File

@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 5,5,4,0
PRODUCTVERSION 5,5,4,0
FILEVERSION 5,8,2,0
PRODUCTVERSION 5,8,2,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "5.5.4.0"
VALUE "FileVersion", "5.8.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "5.5.4.0"
VALUE "ProductVersion", "5.8.2.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 5,5,4,0
PRODUCTVERSION 5,5,4,0
FILEVERSION 5,8,2,0
PRODUCTVERSION 5,8,2,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", "5.5.4.0"
VALUE "FileVersion", "5.8.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "5.5.4.0"
VALUE "ProductVersion", "5.8.2.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -39,6 +39,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
namespace Api {
namespace {
@@ -503,11 +506,19 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
bot->session().attachWebView().open({
.bot = bot,
.context = { .controller = controller },
.button = {.text = button->text, .url = button->data },
.button = { .text = button->text, .url = button->data },
.source = InlineBots::WebViewSourceButton{ .simple = true },
});
}
} break;
case ButtonType::CopyText: {
const auto text = QString::fromUtf8(button->data);
if (!text.isEmpty()) {
QGuiApplication::clipboard()->setText(text);
controller->showToast(tr::lng_text_copied(tr::now));
}
} break;
}
}

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/premium_limits_box.h"
#include "boxes/filters/edit_filter_links.h" // FilterChatStatusText
#include "core/application.h"
#include "core/core_settings.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_chat_filters.h"
@@ -152,6 +153,7 @@ void InitFilterLinkHeader(
.badge = (type == Ui::FilterLinkHeaderType::AddingChats
? std::move(count)
: rpl::single(0)),
.horizontalFilters = Core::App().settings().chatFiltersHorizontal(),
});
const auto widget = header.widget;
widget->resizeToWidth(st::boxWideWidth);

View File

@@ -0,0 +1,128 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_chat_filters_remove_manager.h"
#include "api/api_chat_filters.h"
#include "apiwrap.h"
#include "data/data_chat_filters.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/boxes/confirm_box.h"
#include "ui/ui_utility.h"
#include "window/window_controller.h"
#include "window/window_session_controller.h"
#include "styles/style_layers.h"
namespace Api {
namespace {
void RemoveChatFilter(
not_null<Main::Session*> session,
FilterId filterId,
std::vector<not_null<PeerData*>> leave) {
const auto api = &session->api();
session->data().chatsFilters().apply(MTP_updateDialogFilter(
MTP_flags(MTPDupdateDialogFilter::Flag(0)),
MTP_int(filterId),
MTPDialogFilter()));
if (leave.empty()) {
api->request(MTPmessages_UpdateDialogFilter(
MTP_flags(MTPmessages_UpdateDialogFilter::Flag(0)),
MTP_int(filterId),
MTPDialogFilter()
)).send();
} else {
api->request(MTPchatlists_LeaveChatlist(
MTP_inputChatlistDialogFilter(MTP_int(filterId)),
MTP_vector<MTPInputPeer>(ranges::views::all(
leave
) | ranges::views::transform([](not_null<PeerData*> peer) {
return MTPInputPeer(peer->input);
}) | ranges::to<QVector<MTPInputPeer>>())
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result);
}).send();
}
}
} // namespace
RemoveComplexChatFilter::RemoveComplexChatFilter() = default;
void RemoveComplexChatFilter::request(
QPointer<Ui::RpWidget> widget,
base::weak_ptr<Window::SessionController> weak,
FilterId id) {
const auto session = &weak->session();
const auto &list = session->data().chatsFilters().list();
const auto i = ranges::find(list, id, &Data::ChatFilter::id);
const auto filter = (i != end(list)) ? *i : Data::ChatFilter();
const auto has = filter.hasMyLinks();
const auto confirm = [=](Fn<void()> action, bool onlyWhenHas = false) {
if (!has && onlyWhenHas) {
action();
return;
}
weak->window().show(Ui::MakeConfirmBox({
.text = (has
? tr::lng_filters_delete_sure()
: tr::lng_filters_remove_sure()),
.confirmed = [=](Fn<void()> &&close) { close(); action(); },
.confirmText = (has
? tr::lng_box_delete()
: tr::lng_filters_remove_yes()),
.confirmStyle = &st::attentionBoxButton,
}));
};
const auto simple = [=] {
confirm([=] { RemoveChatFilter(session, id, {}); });
};
const auto suggestRemoving = Api::ExtractSuggestRemoving(filter);
if (suggestRemoving.empty()) {
simple();
return;
} else if (_removingRequestId) {
if (_removingId == id) {
return;
}
session->api().request(_removingRequestId).cancel();
}
_removingId = id;
_removingRequestId = session->api().request(
MTPchatlists_GetLeaveChatlistSuggestions(
MTP_inputChatlistDialogFilter(
MTP_int(id)))
).done(crl::guard(widget, [=, this](const MTPVector<MTPPeer> &result) {
_removingRequestId = 0;
const auto suggestRemovePeers = ranges::views::all(
result.v
) | ranges::views::transform([=](const MTPPeer &peer) {
return session->data().peer(peerFromMTP(peer));
}) | ranges::to_vector;
const auto chosen = crl::guard(widget, [=](
std::vector<not_null<PeerData*>> peers) {
RemoveChatFilter(session, id, std::move(peers));
});
confirm(crl::guard(widget, [=] {
Api::ProcessFilterRemove(
weak,
filter.title(),
filter.iconEmoji(),
suggestRemoving,
suggestRemovePeers,
chosen);
}), true);
})).fail(crl::guard(widget, [=, this] {
_removingRequestId = 0;
simple();
})).send();
}
} // namespace Api

View File

@@ -0,0 +1,35 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Window {
class SessionController;
} // namespace Window
namespace Ui {
class RpWidget;
} // namespace Ui
namespace Api {
class RemoveComplexChatFilter final {
public:
RemoveComplexChatFilter();
void request(
QPointer<Ui::RpWidget> widget,
base::weak_ptr<Window::SessionController> weak,
FilterId id);
private:
FilterId _removingId = 0;
mtpRequestId _removingRequestId = 0;
};
} // namespace Api

View File

@@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_credits_graphics.h"
#include "ui/boxes/confirm_box.h"
#include "ui/controls/userpic_button.h"
#include "ui/effects/credits_graphics.h"
#include "ui/effects/premium_graphics.h"
#include "ui/effects/premium_stars_colored.h"
#include "ui/empty_userpic.h"
@@ -129,6 +130,7 @@ void ConfirmSubscriptionBox(
struct State final {
std::shared_ptr<Data::PhotoMedia> photoMedia;
std::unique_ptr<Ui::EmptyUserpic> photoEmpty;
QImage frame;
std::optional<MTP::Sender> api;
Ui::RpWidget* saveButton = nullptr;
@@ -146,25 +148,45 @@ void ConfirmSubscriptionBox(
const auto userpic = userpicWrap->entity();
const auto photoSize = st::confirmInvitePhotoSize;
userpic->resize(Size(photoSize));
const auto creditsIconSize = photoSize / 3;
const auto creditsIconCallback =
Ui::PaintOutlinedColoredCreditsIconCallback(
creditsIconSize,
1.5);
state->frame = QImage(
Size(photoSize * style::DevicePixelRatio()),
QImage::Format_ARGB32_Premultiplied);
state->frame.setDevicePixelRatio(style::DevicePixelRatio());
const auto options = Images::Option::RoundCircle;
userpic->paintRequest(
) | rpl::start_with_next([=, small = Data::PhotoSize::Small] {
auto p = QPainter(userpic);
if (state->photoMedia) {
if (const auto image = state->photoMedia->image(small)) {
p.drawPixmap(
state->frame.fill(Qt::transparent);
{
auto p = QPainter(&state->frame);
if (state->photoMedia) {
if (const auto image = state->photoMedia->image(small)) {
p.drawPixmap(
0,
0,
image->pix(Size(photoSize), { .options = options }));
}
} else if (state->photoEmpty) {
state->photoEmpty->paintCircle(
p,
0,
0,
image->pix(Size(photoSize), { .options = options }));
userpic->width(),
photoSize);
}
if (creditsIconCallback) {
p.translate(
photoSize - creditsIconSize,
photoSize - creditsIconSize);
creditsIconCallback(p);
}
} else if (state->photoEmpty) {
state->photoEmpty->paintCircle(
p,
0,
0,
userpic->width(),
photoSize);
}
auto p = QPainter(userpic);
p.drawImage(0, 0, state->frame);
}, userpicWrap->lifetime());
userpicWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
if (photo) {
@@ -275,7 +297,6 @@ void ConfirmSubscriptionBox(
: 0;
state->api->request(
MTPpayments_SendStarsForm(
MTP_flags(0),
MTP_long(formId),
MTP_inputInvoiceChatInviteSubscription(MTP_string(hash)))
).done([=](const MTPpayments_PaymentResult &result) {

View File

@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_updates.h"
#include "apiwrap.h"
#include "base/unixtime.h"
#include "data/components/credits.h"
#include "data/data_channel.h"
#include "data/data_document.h"
#include "data/data_peer.h"
@@ -38,8 +39,8 @@ constexpr auto kTransactionsLimit = 100;
if (const auto list = tl.data().vextended_media()) {
extended.reserve(list->v.size());
for (const auto &media : list->v) {
media.match([&](const MTPDmessageMediaPhoto &photo) {
if (const auto inner = photo.vphoto()) {
media.match([&](const MTPDmessageMediaPhoto &data) {
if (const auto inner = data.vphoto()) {
const auto photo = owner->processPhoto(*inner);
if (!photo->isNull()) {
extended.push_back(CreditsHistoryMedia{
@@ -48,9 +49,11 @@ constexpr auto kTransactionsLimit = 100;
});
}
}
}, [&](const MTPDmessageMediaDocument &document) {
if (const auto inner = document.vdocument()) {
const auto document = owner->processDocument(*inner);
}, [&](const MTPDmessageMediaDocument &data) {
if (const auto inner = data.vdocument()) {
const auto document = owner->processDocument(
*inner,
data.valt_documents());
if (document->isAnimation()
|| document->isVideoFile()
|| document->isGifv()) {
@@ -69,18 +72,26 @@ constexpr auto kTransactionsLimit = 100;
}, [](const auto &) {
return PeerId(0);
}).value;
const auto stargift = tl.data().vstargift();
const auto reaction = tl.data().is_reaction();
const auto incoming = (int64(tl.data().vstars().v) >= 0);
const auto saveActorId = (reaction || !extended.empty()) && incoming;
return Data::CreditsHistoryEntry{
.id = qs(tl.data().vid()),
.title = qs(tl.data().vtitle().value_or_empty()),
.description = qs(tl.data().vdescription().value_or_empty()),
.description = { qs(tl.data().vdescription().value_or_empty()) },
.date = base::unixtime::parse(tl.data().vdate().v),
.photoId = photo ? photo->id : 0,
.extended = std::move(extended),
.credits = tl.data().vstars().v,
.bareMsgId = uint64(tl.data().vmsg_id().value_or_empty()),
.barePeerId = barePeerId,
.barePeerId = saveActorId ? peer->id.value : barePeerId,
.bareGiveawayMsgId = uint64(
tl.data().vgiveaway_post_id().value_or_empty()),
.bareGiftStickerId = (stargift
? owner->processDocument(stargift->data().vsticker())->id
: 0),
.bareActorId = saveActorId ? barePeerId : uint64(0),
.peerType = tl.data().vpeer().match([](const HistoryPeerTL &) {
return Data::CreditsHistoryEntry::PeerType::Peer;
}, [](const MTPDstarsTransactionPeerPlayMarket &) {
@@ -95,6 +106,8 @@ constexpr auto kTransactionsLimit = 100;
return Data::CreditsHistoryEntry::PeerType::PremiumBot;
}, [](const MTPDstarsTransactionPeerAds &) {
return Data::CreditsHistoryEntry::PeerType::Ads;
}, [](const MTPDstarsTransactionPeerAPI &) {
return Data::CreditsHistoryEntry::PeerType::API;
}),
.subscriptionUntil = tl.data().vsubscription_period()
? base::unixtime::parse(base::unixtime::now()
@@ -104,12 +117,18 @@ constexpr auto kTransactionsLimit = 100;
? base::unixtime::parse(tl.data().vtransaction_date()->v)
: QDateTime(),
.successLink = qs(tl.data().vtransaction_url().value_or_empty()),
.starsConverted = int(stargift
? stargift->data().vconvert_stars().v
: 0),
.floodSkip = int(tl.data().vfloodskip_number().value_or(0)),
.converted = stargift && incoming,
.stargift = stargift.has_value(),
.reaction = tl.data().is_reaction(),
.refunded = tl.data().is_refund(),
.pending = tl.data().is_pending(),
.failed = tl.data().is_failed(),
.in = (int64(tl.data().vstars().v) >= 0),
.gift = tl.data().is_gift(),
.in = incoming,
.gift = tl.data().is_gift() || stargift.has_value(),
};
}
@@ -156,7 +175,8 @@ constexpr auto kTransactionsLimit = 100;
.balance = status.data().vbalance().v,
.subscriptionsMissingBalance
= status.data().vsubscriptions_missing_balance().value_or_empty(),
.allLoaded = !status.data().vnext_offset().has_value(),
.allLoaded = !status.data().vnext_offset().has_value()
&& !status.data().vsubscriptions_next_offset().has_value(),
.token = qs(status.data().vnext_offset().value_or_empty()),
.tokenSubscriptions = qs(
status.data().vsubscriptions_next_offset().value_or_empty()),
@@ -239,6 +259,8 @@ void CreditsStatus::request(
_peer->isSelf() ? MTP_inputPeerSelf() : _peer->input
)).done([=](const TLResult &result) {
_requestId = 0;
const auto balance = result.data().vbalance().v;
_peer->session().credits().apply(_peer->id, balance);
if (const auto onstack = done) {
onstack(StatusFromTL(result, _peer));
}

View File

@@ -99,8 +99,8 @@ public:
[[nodiscard]] Data::CreditsEarnStatistics data() const;
private:
const bool _isUser = false;
Data::CreditsEarnStatistics _data;
bool _isUser = false;
mtpRequestId _requestId = 0;

View File

@@ -40,6 +40,7 @@ void HandleWithdrawalButton(
std::shared_ptr<Ui::Show> show) {
Expects(receiver.currencyReceiver
|| (receiver.creditsReceiver && receiver.creditsAmount));
struct State {
rpl::lifetime lifetime;
bool loading = false;
@@ -58,8 +59,7 @@ void HandleWithdrawalButton(
const auto processOut = [=] {
if (state->loading) {
return;
}
if (peer && !receiver.creditsAmount()) {
} else if (peer && !receiver.creditsAmount()) {
return;
}
state->loading = true;
@@ -89,12 +89,15 @@ void HandleWithdrawalButton(
}
};
const auto fail = [=](const MTP::Error &error) {
show->showToast(error.type());
const auto message = error.type();
if (box && !box->handleCustomCheckError(message)) {
show->showToast(message);
}
};
if (channel) {
session->api().request(
MTPstats_GetBroadcastRevenueWithdrawalUrl(
channel->inputChannel,
channel->input,
result.result
)).done([=](const ChannelOutUrl &r) {
done(qs(r.data().vurl()));
@@ -134,7 +137,7 @@ void HandleWithdrawalButton(
if (channel) {
session->api().request(
MTPstats_GetBroadcastRevenueWithdrawalUrl(
channel->inputChannel,
channel->input,
MTP_inputCheckPasswordEmpty()
)).fail(fail).send();
} else if (peer) {

View File

@@ -37,7 +37,8 @@ MTPVector<MTPDocumentAttribute> ComposeSendingDocumentAttributes(
MTP_int(dimensions.width()),
MTP_int(dimensions.height()),
MTPint(), // preload_prefix_size
MTPdouble())); // video_start_ts
MTPdouble(), // video_start_ts
MTPstring())); // video_codec
} else {
attributes.push_back(MTP_documentAttributeImageSize(
MTP_int(dimensions.width()),

View File

@@ -550,6 +550,24 @@ Payments::InvoicePremiumGiftCode PremiumGiftCodeOptions::invoice(
};
}
std::vector<GiftOptionData> PremiumGiftCodeOptions::optionsForPeer() const {
auto result = std::vector<GiftOptionData>();
if (!_optionsForOnePerson.currency.isEmpty()) {
const auto count = int(_optionsForOnePerson.months.size());
result.reserve(count);
for (auto i = 0; i != count; ++i) {
Assert(i < _optionsForOnePerson.totalCosts.size());
result.push_back({
.cost = _optionsForOnePerson.totalCosts[i],
.currency = _optionsForOnePerson.currency,
.months = _optionsForOnePerson.months[i],
});
}
}
return result;
}
Data::PremiumSubscriptionOptions PremiumGiftCodeOptions::options(int amount) {
const auto it = _subscriptionOptions.find(amount);
if (it != end(_subscriptionOptions)) {
@@ -571,6 +589,41 @@ Data::PremiumSubscriptionOptions PremiumGiftCodeOptions::options(int amount) {
}
}
auto PremiumGiftCodeOptions::requestStarGifts()
-> rpl::producer<rpl::no_value, QString> {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
_api.request(MTPpayments_GetStarGifts(
MTP_int(0)
)).done([=](const MTPpayments_StarGifts &result) {
result.match([&](const MTPDpayments_starGifts &data) {
_giftsHash = data.vhash().v;
const auto &list = data.vgifts().v;
const auto session = &_peer->session();
auto gifts = std::vector<StarGift>();
gifts.reserve(list.size());
for (const auto &gift : list) {
if (auto parsed = FromTL(session, gift)) {
gifts.push_back(std::move(*parsed));
}
}
_gifts = std::move(gifts);
}, [&](const MTPDpayments_starGiftsNotModified &) {
});
consumer.put_done();
}).fail([=](const MTP::Error &error) {
consumer.put_error_copy(error.type());
}).send();
return lifetime;
};
}
const std::vector<StarGift> &PremiumGiftCodeOptions::starGifts() const {
return _gifts;
}
int PremiumGiftCodeOptions::giveawayBoostsPerPremium() const {
constexpr auto kFallbackCount = 4;
return _peer->session().appConfig().get<int>(
@@ -705,4 +758,59 @@ rpl::producer<DocumentData*> RandomHelloStickerValue(
}) | rpl::take(1) | rpl::map(random));
}
std::optional<StarGift> FromTL(
not_null<Main::Session*> session,
const MTPstarGift &gift) {
const auto &data = gift.data();
const auto document = session->data().processDocument(
data.vsticker());
const auto remaining = data.vavailability_remains();
const auto total = data.vavailability_total();
if (!document->sticker()) {
return {};
}
return StarGift{
.id = uint64(data.vid().v),
.stars = int64(data.vstars().v),
.starsConverted = int64(data.vconvert_stars().v),
.document = document,
.limitedLeft = remaining.value_or_empty(),
.limitedCount = total.value_or_empty(),
.firstSaleDate = data.vfirst_sale_date().value_or_empty(),
.lastSaleDate = data.vlast_sale_date().value_or_empty(),
.birthday = data.is_birthday(),
};
}
std::optional<UserStarGift> FromTL(
not_null<UserData*> to,
const MTPuserStarGift &gift) {
const auto session = &to->session();
const auto &data = gift.data();
auto parsed = FromTL(session, data.vgift());
if (!parsed) {
return {};
}
return UserStarGift{
.info = std::move(*parsed),
.message = (data.vmessage()
? TextWithEntities{
.text = qs(data.vmessage()->data().vtext()),
.entities = Api::EntitiesFromMTP(
session,
data.vmessage()->data().ventities().v),
}
: TextWithEntities()),
.starsConverted = int64(data.vconvert_stars().value_or_empty()),
.fromId = (data.vfrom_id()
? peerFromUser(data.vfrom_id()->v)
: PeerId()),
.messageId = data.vmsg_id().value_or_empty(),
.date = data.vdate().v,
.anonymous = data.is_name_hidden(),
.hidden = data.is_unsaved(),
.mine = to->isSelf(),
};
}
} // namespace Api

View File

@@ -67,6 +67,40 @@ struct GiveawayInfo {
}
};
struct GiftOptionData {
int64 cost = 0;
QString currency;
int months = 0;
};
struct StarGift {
uint64 id = 0;
int64 stars = 0;
int64 starsConverted = 0;
not_null<DocumentData*> document;
int limitedLeft = 0;
int limitedCount = 0;
TimeId firstSaleDate = 0;
TimeId lastSaleDate = 0;
bool birthday = false;
friend inline bool operator==(
const StarGift &,
const StarGift &) = default;
};
struct UserStarGift {
StarGift info;
TextWithEntities message;
int64 starsConverted = 0;
PeerId fromId = 0;
MsgId messageId = 0;
TimeId date = 0;
bool anonymous = false;
bool hidden = false;
bool mine = false;
};
class Premium final {
public:
explicit Premium(not_null<ApiWrap*> api);
@@ -171,6 +205,7 @@ public:
PremiumGiftCodeOptions(not_null<PeerData*> peer);
[[nodiscard]] rpl::producer<rpl::no_value, QString> request();
[[nodiscard]] std::vector<GiftOptionData> optionsForPeer() const;
[[nodiscard]] Data::PremiumSubscriptionOptions options(int amount);
[[nodiscard]] const std::vector<int> &availablePresets() const;
[[nodiscard]] int monthsFromPreset(int monthsIndex);
@@ -187,6 +222,9 @@ public:
[[nodiscard]] int giveawayPeriodMax() const;
[[nodiscard]] bool giveawayGiftsPurchaseAvailable() const;
[[nodiscard]] rpl::producer<rpl::no_value, QString> requestStarGifts();
[[nodiscard]] const std::vector<StarGift> &starGifts() const;
private:
struct Token final {
int users = 0;
@@ -206,7 +244,7 @@ private:
base::flat_map<Amount, PremiumSubscriptionOptions> _subscriptionOptions;
struct {
std::vector<int> months;
std::vector<float64> totalCosts;
std::vector<int64> totalCosts;
QString currency;
} _optionsForOnePerson;
@@ -214,6 +252,9 @@ private:
base::flat_map<Token, Store> _stores;
int32 _giftsHash = 0;
std::vector<StarGift> _gifts;
MTP::Sender _api;
};
@@ -242,4 +283,11 @@ enum class RequirePremiumState {
[[nodiscard]] rpl::producer<DocumentData*> RandomHelloStickerValue(
not_null<Main::Session*> session);
[[nodiscard]] std::optional<StarGift> FromTL(
not_null<Main::Session*> session,
const MTPstarGift &gift);
[[nodiscard]] std::optional<UserStarGift> FromTL(
not_null<UserData*> to,
const MTPuserStarGift &gift);
} // namespace Api

View File

@@ -10,10 +10,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "data/data_peer.h"
#include "data/data_photo.h"
#include "data/data_report.h"
#include "data/data_user.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/boxes/report_box.h"
#include "ui/boxes/report_box_graphics.h"
#include "ui/layers/show.h"
namespace Api {
@@ -39,52 +40,106 @@ MTPreportReason ReasonToTL(const Ui::ReportReason &reason) {
} // namespace
void SendReport(
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*>,
StoryId> data) {
auto done = [=] {
void SendPhotoReport(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
Ui::ReportReason reason,
const QString &comment,
not_null<PhotoData*> photo) {
peer->session().api().request(MTPaccount_ReportProfilePhoto(
peer->input,
photo->mtpInput(),
ReasonToTL(reason),
MTP_string(comment)
)).done([=] {
show->showToast(tr::lng_report_thanks(tr::now));
};
v::match(data, [&](v::null_t) {
peer->session().api().request(MTPaccount_ReportPeer(
peer->input,
ReasonToTL(reason),
MTP_string(comment)
)).done(std::move(done)).send();
}, [&](const MessageIdsList &ids) {
auto apiIds = QVector<MTPint>();
apiIds.reserve(ids.size());
for (const auto &fullId : ids) {
apiIds.push_back(MTP_int(fullId.msg));
}).send();
}
auto CreateReportMessagesOrStoriesCallback(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer)
-> Fn<void(Data::ReportInput, Fn<void(ReportResult)>)> {
using TLChoose = MTPDreportResultChooseOption;
using TLAddComment = MTPDreportResultAddComment;
using TLReported = MTPDreportResultReported;
using Result = ReportResult;
struct State final {
#ifdef _DEBUG
~State() {
qDebug() << "Messages or Stories Report ~State().";
}
peer->session().api().request(MTPmessages_Report(
peer->input,
MTP_vector<MTPint>(apiIds),
ReasonToTL(reason),
MTP_string(comment)
)).done(std::move(done)).send();
}, [&](not_null<PhotoData*> photo) {
peer->session().api().request(MTPaccount_ReportProfilePhoto(
peer->input,
photo->mtpInput(),
ReasonToTL(reason),
MTP_string(comment)
)).done(std::move(done)).send();
}, [&](StoryId id) {
peer->session().api().request(MTPstories_Report(
peer->input,
MTP_vector<MTPint>(1, MTP_int(id)),
ReasonToTL(reason),
MTP_string(comment)
)).done(std::move(done)).send();
});
#endif
mtpRequestId requestId = 0;
};
const auto state = std::make_shared<State>();
return [=](
Data::ReportInput reportInput,
Fn<void(Result)> done) {
auto apiIds = QVector<MTPint>();
apiIds.reserve(reportInput.ids.size() + reportInput.stories.size());
for (const auto &id : reportInput.ids) {
apiIds.push_back(MTP_int(id));
}
for (const auto &story : reportInput.stories) {
apiIds.push_back(MTP_int(story));
}
const auto received = [=](
const MTPReportResult &result,
mtpRequestId requestId) {
if (state->requestId != requestId) {
return;
}
state->requestId = 0;
done(result.match([&](const TLChoose &data) {
const auto t = qs(data.vtitle());
auto list = Result::Options();
list.reserve(data.voptions().v.size());
for (const auto &tl : data.voptions().v) {
list.emplace_back(Result::Option{
.id = tl.data().voption().v,
.text = qs(tl.data().vtext()),
});
}
return Result{ .options = std::move(list), .title = t };
}, [&](const TLAddComment &data) -> Result {
return {
.commentOption = ReportResult::CommentOption{
.optional = data.is_optional(),
.id = data.voption().v,
}
};
}, [&](const TLReported &data) -> Result {
return { .successful = true };
}));
};
const auto fail = [=](const MTP::Error &error) {
state->requestId = 0;
done({ .error = error.type() });
};
if (!reportInput.stories.empty()) {
state->requestId = peer->session().api().request(
MTPstories_Report(
peer->input,
MTP_vector<MTPint>(apiIds),
MTP_bytes(reportInput.optionId),
MTP_string(reportInput.comment))
).done(received).fail(fail).send();
} else {
state->requestId = peer->session().api().request(
MTPmessages_Report(
peer->input,
MTP_vector<MTPint>(apiIds),
MTP_bytes(reportInput.optionId),
MTP_string(reportInput.comment))
).done(received).fail(fail).send();
}
};
}
} // namespace Api

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class HistoryItem;
class PeerData;
class PhotoData;
@@ -15,17 +16,41 @@ class Show;
enum class ReportReason;
} // namespace Ui
namespace Data {
struct ReportInput;
} // namespace Data
namespace Api {
void SendReport(
struct ReportResult final {
using Id = QByteArray;
struct Option final {
Id id = 0;
QString text;
};
using Options = std::vector<Option>;
Options options;
QString title;
QString error;
QString comment;
struct CommentOption {
bool optional = false;
Id id = 0;
};
std::optional<CommentOption> commentOption;
bool successful = false;
};
void SendPhotoReport(
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*>,
StoryId> data);
not_null<PhotoData*> photo);
[[nodiscard]] auto CreateReportMessagesOrStoriesCallback(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer)
-> Fn<void(Data::ReportInput, Fn<void(ReportResult)>)>;
} // namespace Api

View File

@@ -456,6 +456,7 @@ void SendConfirmedFile(
not_null<Main::Session*> session,
const std::shared_ptr<FilePrepareResult> &file) {
const auto isEditing = (file->type != SendMediaType::Audio)
&& (file->type != SendMediaType::Round)
&& (file->to.replaceMediaOf != 0);
const auto newId = FullMsgId(
file->to.peer,
@@ -525,7 +526,8 @@ void SendConfirmedFile(
// Shortcut messages have no 'edited' badge.
flags |= MessageFlag::HideEdited;
}
if (file->type == SendMediaType::Audio) {
if (file->type == SendMediaType::Audio
|| file->type == SendMediaType::Round) {
if (!peer->isChannel() || peer->isMegagroup()) {
flags |= MessageFlag::MediaIsUnread;
}
@@ -547,32 +549,28 @@ void SendConfirmedFile(
MTP_flags(Flag::f_document
| (file->spoiler ? Flag::f_spoiler : Flag())),
file->document,
MTPDocument(), // alt_document
MTPVector<MTPDocument>(), // alt_documents
MTPint());
} else if (file->type == SendMediaType::Audio) {
const auto ttlSeconds = file->to.options.ttlSeconds;
const auto isVoice = [&] {
return file->document.match([](const MTPDdocumentEmpty &d) {
return false;
}, [](const MTPDdocument &d) {
return ranges::any_of(d.vattributes().v, [&](
const MTPDocumentAttribute &attribute) {
using Att = MTPDdocumentAttributeAudio;
return attribute.match([](const Att &data) -> bool {
return data.vflags().v & Att::Flag::f_voice;
}, [](const auto &) {
return false;
});
});
});
}();
using Flag = MTPDmessageMediaDocument::Flag;
return MTP_messageMediaDocument(
MTP_flags(Flag::f_document
| (isVoice ? Flag::f_voice : Flag())
| Flag::f_voice
| (ttlSeconds ? Flag::f_ttl_seconds : Flag())),
file->document,
MTPDocument(), // alt_document
MTPVector<MTPDocument>(), // alt_documents
MTP_int(ttlSeconds));
} else if (file->type == SendMediaType::Round) {
using Flag = MTPDmessageMediaDocument::Flag;
const auto ttlSeconds = file->to.options.ttlSeconds;
return MTP_messageMediaDocument(
MTP_flags(Flag::f_document
| Flag::f_round
| (ttlSeconds ? Flag::f_ttl_seconds : Flag())
| (file->spoiler ? Flag::f_spoiler : Flag())),
file->document,
MTPVector<MTPDocument>(), // alt_documents
MTP_int(ttlSeconds));
} else {
Unexpected("Type in sendFilesConfirmed.");

View File

@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_stories.h"
#include "data/data_story.h"
#include "data/data_user.h"
#include "history/history.h"
#include "main/main_session.h"
@@ -341,6 +342,10 @@ void PublicForwards::request(
.token = nextToken,
});
};
const auto processFail = [=] {
_requestId = 0;
done({});
};
constexpr auto kLimit = tl::make_int(100);
if (_fullId.messageId) {
@@ -349,14 +354,14 @@ void PublicForwards::request(
MTP_int(_fullId.messageId.msg),
MTP_string(token),
kLimit
)).done(processResult).fail([=] { _requestId = 0; }).send();
)).done(processResult).fail(processFail).send();
} else if (_fullId.storyId) {
_requestId = makeRequest(MTPstats_GetStoryPublicForwards(
channel->input,
MTP_int(_fullId.storyId.story),
MTP_string(token),
kLimit
)).done(processResult).fail([=] { _requestId = 0; }).send();
)).done(processResult).fail(processFail).send();
}
}
@@ -381,7 +386,7 @@ Data::PublicForwardsSlice MessageStatistics::firstSlice() const {
}
void MessageStatistics::request(Fn<void(Data::MessageStatistics)> done) {
if (channel()->isMegagroup()) {
if (channel()->isMegagroup() && !_storyId) {
return;
}
const auto requestFirstPublicForwards = [=](
@@ -681,17 +686,18 @@ Data::BoostStatus Boosts::boostStatus() const {
return _boostStatus;
}
ChannelEarnStatistics::ChannelEarnStatistics(not_null<ChannelData*> channel)
: StatisticsRequestSender(channel) {
EarnStatistics::EarnStatistics(not_null<PeerData*> peer)
: StatisticsRequestSender(peer)
, _isUser(peer->isUser()) {
}
rpl::producer<rpl::no_value, QString> ChannelEarnStatistics::request() {
rpl::producer<rpl::no_value, QString> EarnStatistics::request() {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
makeRequest(MTPstats_GetBroadcastRevenueStats(
MTP_flags(0),
channel()->inputChannel
(_isUser ? user()->input : channel()->input)
)).done([=](const MTPstats_BroadcastRevenueStats &result) {
const auto &data = result.data();
const auto &balances = data.vbalances().data();
@@ -708,18 +714,22 @@ rpl::producer<rpl::no_value, QString> ChannelEarnStatistics::request() {
requestHistory({}, [=](Data::EarnHistorySlice &&slice) {
_data.firstHistorySlice = std::move(slice);
api().request(
MTPchannels_GetFullChannel(channel()->inputChannel)
).done([=](const MTPmessages_ChatFull &result) {
result.data().vfull_chat().match([&](
const MTPDchannelFull &d) {
_data.switchedOff = d.is_restricted_sponsored();
}, [](const auto &) {
});
if (!_isUser) {
api().request(
MTPchannels_GetFullChannel(channel()->inputChannel)
).done([=](const MTPmessages_ChatFull &result) {
result.data().vfull_chat().match([&](
const MTPDchannelFull &d) {
_data.switchedOff = d.is_restricted_sponsored();
}, [](const auto &) {
});
consumer.put_done();
}).fail([=](const MTP::Error &error) {
consumer.put_error_copy(error.type());
}).send();
} else {
consumer.put_done();
}).fail([=](const MTP::Error &error) {
consumer.put_error_copy(error.type());
}).send();
}
});
}).fail([=](const MTP::Error &error) {
consumer.put_error_copy(error.type());
@@ -729,7 +739,7 @@ rpl::producer<rpl::no_value, QString> ChannelEarnStatistics::request() {
};
}
void ChannelEarnStatistics::requestHistory(
void EarnStatistics::requestHistory(
const Data::EarnHistorySlice::OffsetToken &token,
Fn<void(Data::EarnHistorySlice)> done) {
if (_requestId) {
@@ -738,7 +748,7 @@ void ChannelEarnStatistics::requestHistory(
constexpr auto kTlFirstSlice = tl::make_int(kFirstSlice);
constexpr auto kTlLimit = tl::make_int(kLimit);
_requestId = api().request(MTPstats_GetBroadcastRevenueTransactions(
channel()->inputChannel,
(_isUser ? user()->input : channel()->input),
MTP_int(token),
(!token) ? kTlFirstSlice : kTlLimit
)).done([=](const MTPstats_BroadcastRevenueTransactions &result) {
@@ -799,7 +809,7 @@ void ChannelEarnStatistics::requestHistory(
}).send();
}
Data::EarnStatistics ChannelEarnStatistics::data() const {
Data::EarnStatistics EarnStatistics::data() const {
return _data;
}

View File

@@ -79,9 +79,9 @@ private:
};
class ChannelEarnStatistics final : public StatisticsRequestSender {
class EarnStatistics final : public StatisticsRequestSender {
public:
explicit ChannelEarnStatistics(not_null<ChannelData*> channel);
explicit EarnStatistics(not_null<PeerData*> peer);
[[nodiscard]] rpl::producer<rpl::no_value, QString> request();
void requestHistory(
@@ -94,6 +94,7 @@ public:
static constexpr auto kLimit = int(10);
private:
const bool _isUser = false;
Data::EarnStatistics _data;
mtpRequestId _requestId = 0;

View File

@@ -316,6 +316,9 @@ void Updates::feedUpdateVector(
} else if (policy == SkipUpdatePolicy::SkipExceptGroupCallParticipants) {
return;
}
if (policy == SkipUpdatePolicy::SkipNone) {
applyConvertToScheduledOnSend(updates);
}
for (const auto &entry : std::as_const(list)) {
const auto type = entry.type();
if ((policy == SkipUpdatePolicy::SkipMessageIds
@@ -329,6 +332,15 @@ void Updates::feedUpdateVector(
session().data().sendHistoryChangeNotifications();
}
void Updates::checkForSentToScheduled(const MTPUpdates &updates) {
updates.match([&](const MTPDupdates &data) {
applyConvertToScheduledOnSend(data.vupdates(), true);
}, [&](const MTPDupdatesCombined &data) {
applyConvertToScheduledOnSend(data.vupdates(), true);
}, [](const auto &) {
});
}
void Updates::feedMessageIds(const MTPVector<MTPUpdate> &updates) {
for (const auto &update : updates.v) {
if (update.type() == mtpc_updateMessageID) {
@@ -417,13 +429,12 @@ void Updates::channelDifferenceDone(
"{ good - after not final channelDifference was received }%1"
).arg(_session->mtp().isTestMode() ? " TESTMODE" : ""));
getChannelDifference(channel);
} else if (ranges::contains(
_activeChats,
channel,
[](const auto &pair) { return pair.second.peer; })) {
channel->ptsWaitingForShortPoll(timeout
} else if (inActiveChats(channel)) {
channel->ptsSetWaitingForShortPoll(timeout
? (timeout * crl::time(1000))
: kWaitForChannelGetDifference);
} else {
channel->ptsSetWaitingForShortPoll(-1);
}
}
@@ -433,6 +444,7 @@ void Updates::feedChannelDifference(
session().data().processChats(data.vchats());
_handlingChannelDifference = true;
applyConvertToScheduledOnSend(data.vother_updates());
feedMessageIds(data.vother_updates());
session().data().processMessages(
data.vnew_messages(),
@@ -597,6 +609,7 @@ void Updates::feedDifference(
Core::App().checkAutoLock();
session().data().processUsers(users);
session().data().processChats(chats);
applyConvertToScheduledOnSend(other);
feedMessageIds(other);
session().data().processMessages(msgs, NewMessageType::Unread);
feedUpdateVector(other, SkipUpdatePolicy::SkipMessageIds);
@@ -743,16 +756,32 @@ void Updates::addActiveChat(rpl::producer<PeerData*> chat) {
std::move(
chat
) | rpl::start_with_next_done([=](PeerData *peer) {
_activeChats[key].peer = peer;
if (const auto channel = peer ? peer->asChannel() : nullptr) {
channel->ptsWaitingForShortPoll(
kWaitForChannelGetDifference);
auto &active = _activeChats[key];
const auto was = active.peer;
if (was != peer) {
active.peer = peer;
if (const auto channel = was ? was->asChannel() : nullptr) {
if (!inActiveChats(channel)) {
channel->ptsSetWaitingForShortPoll(-1);
}
}
if (const auto channel = peer ? peer->asChannel() : nullptr) {
channel->ptsSetWaitingForShortPoll(
kWaitForChannelGetDifference);
}
}
}, [=] {
_activeChats.erase(key);
}, _activeChats[key].lifetime);
}
bool Updates::inActiveChats(not_null<PeerData*> peer) const {
return ranges::contains(
_activeChats,
peer.get(),
[](const auto &pair) { return pair.second.peer; });
}
void Updates::requestChannelRangeDifference(not_null<History*> history) {
Expects(history->peer->isChannel());
@@ -866,6 +895,51 @@ void Updates::mtpUpdateReceived(const MTPUpdates &updates) {
}
}
void Updates::applyConvertToScheduledOnSend(
const MTPVector<MTPUpdate> &other,
bool skipScheduledCheck) {
for (const auto &update : other.v) {
update.match([&](const MTPDupdateNewScheduledMessage &data) {
const auto &message = data.vmessage();
const auto id = IdFromMessage(message);
const auto scheduledMessages = &_session->scheduledMessages();
const auto scheduledId = scheduledMessages->localMessageId(id);
for (const auto &updateId : other.v) {
updateId.match([&](const MTPDupdateMessageID &dataId) {
if (dataId.vid().v == id) {
auto &owner = session().data();
if (skipScheduledCheck) {
const auto peerId = PeerFromMessage(message);
const auto history = owner.historyLoaded(peerId);
if (history) {
_session->data().sentToScheduled({
.history = history,
.scheduledId = scheduledId,
});
}
return;
}
const auto rand = dataId.vrandom_id().v;
const auto localId = owner.messageIdByRandomId(rand);
if (const auto local = owner.message(localId)) {
if (!local->isScheduled()) {
_session->data().sentToScheduled({
.history = local->history(),
.scheduledId = scheduledId,
});
// We've sent a non-scheduled message,
// but it was converted to a scheduled.
local->destroy();
}
}
}
}, [](const auto &) {});
}
}, [](const auto &) {});
}
}
void Updates::applyGroupCallParticipantUpdates(const MTPUpdates &updates) {
updates.match([&](const MTPDupdates &data) {
session().data().processUsers(data.vusers());

View File

@@ -40,6 +40,8 @@ public:
void applyUpdatesNoPtsCheck(const MTPUpdates &updates);
void applyUpdateNoPtsCheck(const MTPUpdate &update);
void checkForSentToScheduled(const MTPUpdates &updates);
[[nodiscard]] int32 pts() const;
void updateOnline(crl::time lastNonIdleTime = 0);
@@ -63,6 +65,7 @@ public:
void requestChannelRangeDifference(not_null<History*> history);
void addActiveChat(rpl::producer<PeerData*> chat);
[[nodiscard]] bool inActiveChats(not_null<PeerData*> peer) const;
private:
enum class ChannelDifferenceRequest {
@@ -130,6 +133,9 @@ private:
// Doesn't call sendHistoryChangeNotifications itself.
void feedUpdate(const MTPUpdate &update);
void applyConvertToScheduledOnSend(
const MTPVector<MTPUpdate> &other,
bool skipScheduledCheck = false);
void applyGroupCallParticipantUpdates(const MTPUpdates &updates);
bool whenGetDiffChanged(

View File

@@ -69,6 +69,9 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
if (rule.always.premiums && (rule.option != Option::Everyone)) {
result.push_back(MTP_inputPrivacyValueAllowPremium());
}
if (rule.always.miniapps && (rule.option != Option::Everyone)) {
result.push_back(MTP_inputPrivacyValueAllowBots());
}
}
if (!rule.ignoreNever) {
const auto users = collectInputUsers(rule.never);
@@ -83,6 +86,9 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
MTP_inputPrivacyValueDisallowChatParticipants(
MTP_vector<MTPlong>(chats)));
}
if (rule.never.miniapps && (rule.option != Option::Nobody)) {
result.push_back(MTP_inputPrivacyValueDisallowBots());
}
}
result.push_back([&] {
switch (rule.option) {
@@ -124,6 +130,10 @@ UserPrivacy::Rule TLToRules(const TLRules &rules, Data::Session &owner) {
setOption(Option::CloseFriends);
}, [&](const MTPDprivacyValueAllowPremium &) {
result.always.premiums = true;
}, [&](const MTPDprivacyValueAllowBots &) {
result.always.miniapps = true;
}, [&](const MTPDprivacyValueDisallowBots &) {
result.never.miniapps = true;
}, [&](const MTPDprivacyValueAllowUsers &data) {
const auto &users = data.vusers().v;
always.reserve(always.size() + users.size());
@@ -199,6 +209,7 @@ MTPInputPrivacyKey KeyToTL(UserPrivacy::Key key) {
case Key::Voices: return MTP_inputPrivacyKeyVoiceMessages();
case Key::About: return MTP_inputPrivacyKeyAbout();
case Key::Birthday: return MTP_inputPrivacyKeyBirthday();
case Key::GiftsAutoSave: return MTP_inputPrivacyKeyStarGiftsAutoSave();
}
Unexpected("Key in Api::UserPrivacy::KetToTL.");
}
@@ -228,6 +239,8 @@ std::optional<UserPrivacy::Key> TLToKey(mtpTypeId type) {
case mtpc_inputPrivacyKeyAbout: return Key::About;
case mtpc_privacyKeyBirthday:
case mtpc_inputPrivacyKeyBirthday: return Key::Birthday;
case mtpc_privacyKeyStarGiftsAutoSave:
case mtpc_inputPrivacyKeyStarGiftsAutoSave: return Key::GiftsAutoSave;
}
return std::nullopt;
}

View File

@@ -31,6 +31,7 @@ public:
Voices,
About,
Birthday,
GiftsAutoSave,
};
enum class Option {
Everyone,
@@ -41,6 +42,7 @@ public:
struct Exceptions {
std::vector<not_null<PeerData*>> peers;
bool premiums = false;
bool miniapps = false;
};
struct Rule {
Option option = Option::Everyone;

View File

@@ -756,5 +756,19 @@ rpl::producer<Ui::WhoReadContent> WhoReacted(
const style::WhoRead &st) {
return WhoReacted(item, reaction, context, st, nullptr);
}
rpl::producer<Ui::WhoReadContent> WhenEdited(
not_null<PeerData*> author,
TimeId date) {
return rpl::single(Ui::WhoReadContent{
.participants = { Ui::WhoReadParticipant{
.name = author->name(),
.date = FormatReadDate(date, QDateTime::currentDateTime()),
.id = author->id.value,
} },
.type = Ui::WhoReadType::Edited,
.fullReadCount = 1,
});
}
} // namespace Api

View File

@@ -61,5 +61,8 @@ struct WhoReadList {
const Data::ReactionId &reaction,
not_null<QWidget*> context, // Cache results for this lifetime.
const style::WhoRead &st);
[[nodiscard]] rpl::producer<Ui::WhoReadContent> WhenEdited(
not_null<PeerData*> author,
TimeId date);
} // namespace Api

View File

@@ -708,7 +708,8 @@ void ApiWrap::finalizeMessageDataRequest(
QString ApiWrap::exportDirectMessageLink(
not_null<HistoryItem*> item,
bool inRepliesContext) {
bool inRepliesContext,
bool forceNonPublicLink) {
Expects(item->history()->peer->isChannel());
const auto itemId = item->fullId();
@@ -731,7 +732,7 @@ QString ApiWrap::exportDirectMessageLink(
const auto sender = root
? root->discussionPostOriginalSender()
: nullptr;
if (sender && sender->hasUsername()) {
if (sender && sender->hasUsername() && !forceNonPublicLink) {
// Comment to a public channel.
const auto forwarded = root->Get<HistoryMessageForwarded>();
linkItemId = forwarded->savedFromMsgId;
@@ -747,7 +748,7 @@ QString ApiWrap::exportDirectMessageLink(
}
}
}
const auto base = linkChannel->hasUsername()
const auto base = (linkChannel->hasUsername() && !forceNonPublicLink)
? linkChannel->username()
: "c/" + QString::number(peerToChannel(linkChannel->id).bare);
const auto post = QString::number(linkItemId.bare);
@@ -761,6 +762,7 @@ QString ApiWrap::exportDirectMessageLink(
? (QString::number(linkThreadId.bare) + '/' + post)
: post);
if (linkChannel->hasUsername()
&& !forceNonPublicLink
&& !linkChannel->isMegagroup()
&& !linkCommentId
&& !linkThreadId) {
@@ -774,6 +776,9 @@ QString ApiWrap::exportDirectMessageLink(
}
return session().createInternalLinkFull(query);
};
if (forceNonPublicLink) {
return fallback();
}
const auto i = _unlikelyMessageLinks.find(itemId);
const auto current = (i != end(_unlikelyMessageLinks))
? i->second
@@ -2028,7 +2033,7 @@ void ApiWrap::deleteHistory(
}
if (const auto channel = peer->asChannel()) {
if (!justClear && !revoke) {
channel->ptsWaitingForShortPoll(-1);
channel->ptsSetWaitingForShortPoll(-1);
leaveChannel(channel);
} else {
if (const auto migrated = peer->migrateFrom()) {
@@ -3329,6 +3334,7 @@ void ApiWrap::forwardMessages(
}
const auto requestType = Data::Histories::RequestType::Send;
const auto idsCopy = localIds;
const auto scheduled = action.options.scheduled;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
history->sendRequestId = request(MTPmessages_ForwardMessages(
MTP_flags(sendFlags),
@@ -3341,6 +3347,9 @@ void ApiWrap::forwardMessages(
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, action.options.shortcutId)
)).done([=](const MTPUpdates &result) {
if (!scheduled) {
this->updates().checkForSentToScheduled(result);
}
applyUpdates(result);
if (shared && !--shared->requestsLeft) {
shared->callback();
@@ -3502,6 +3511,7 @@ void ApiWrap::sendVoiceMessage(
QByteArray result,
VoiceWaveform waveform,
crl::time duration,
bool video,
const SendAction &action) {
const auto caption = TextWithTags();
const auto to = FileLoadTaskOptions(action);
@@ -3510,6 +3520,7 @@ void ApiWrap::sendVoiceMessage(
result,
duration,
waveform,
video,
to,
caption));
}
@@ -3936,7 +3947,8 @@ void ApiWrap::sendInlineResult(
not_null<UserData*> bot,
not_null<InlineBots::Result*> data,
const SendAction &action,
std::optional<MsgId> localMessageId) {
std::optional<MsgId> localMessageId,
Fn<void(bool)> done) {
sendAction(action);
const auto history = action.history;
@@ -4016,11 +4028,17 @@ void ApiWrap::sendInlineResult(
history->finishSavingCloudDraft(
topicRootId,
UnixtimeFromMsgId(response.outerMsgId));
if (done) {
done(true);
}
}, [=](const MTP::Error &error, const MTP::Response &response) {
sendMessageFail(error, peer, randomId, newId);
history->finishSavingCloudDraft(
topicRootId,
UnixtimeFromMsgId(response.outerMsgId));
if (done) {
done(false);
}
});
finishForwarding(action);
}
@@ -4225,6 +4243,7 @@ void ApiWrap::sendMultiPaidMedia(
auto &histories = history->owner().histories();
const auto peer = history->peer;
const auto itemId = item->fullId();
album->sent = true;
histories.sendPreparedMessage(
history,
replyTo,
@@ -4296,6 +4315,9 @@ void ApiWrap::sendAlbumWithCancelled(
}
void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
if (album->sent) {
return;
}
const auto groupId = album->groupId;
if (album->items.empty()) {
_sendingAlbums.remove(groupId);
@@ -4320,6 +4342,7 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
return;
} else if (medias.size() < 2) {
const auto &single = medias.front().data();
album->sent = true;
sendMediaWithRandomId(
sample,
single.vmedia(),
@@ -4346,6 +4369,7 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
| (album->options.invertCaption ? Flag::f_invert_media : Flag(0));
auto &histories = history->owner().histories();
const auto peer = history->peer;
album->sent = true;
histories.sendPreparedMessage(
history,
replyTo,

View File

@@ -164,7 +164,8 @@ public:
void requestMessageData(PeerData *peer, MsgId msgId, Fn<void()> done);
QString exportDirectMessageLink(
not_null<HistoryItem*> item,
bool inRepliesContext);
bool inRepliesContext,
bool forceNonPublicLink = false);
QString exportDirectStoryLink(not_null<Data::Story*> item);
void requestContacts();
@@ -317,6 +318,7 @@ public:
QByteArray result,
VoiceWaveform waveform,
crl::time duration,
bool video,
const SendAction &action);
void sendFiles(
Ui::PreparedList &&list,
@@ -359,7 +361,8 @@ public:
not_null<UserData*> bot,
not_null<InlineBots::Result*> data,
const SendAction &action,
std::optional<MsgId> localMessageId);
std::optional<MsgId> localMessageId,
Fn<void(bool)> done = nullptr);
void sendMessageFail(
const MTP::Error &error,
not_null<PeerData*> peer,

View File

@@ -154,9 +154,7 @@ contactsSortButton: IconButton(defaultIconButton) {
iconPosition: point(10px, -1px);
rippleAreaPosition: point(1px, 6px);
rippleAreaSize: 42px;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
ripple: defaultRippleAnimationBgOver;
}
contactsSortOnlineIcon: icon{{ "contacts_online", boxTitleCloseFg }};
contactsSortOnlineIconOver: icon{{ "contacts_online", boxTitleCloseFgOver }};
@@ -416,9 +414,7 @@ calendarPrevious: IconButton {
rippleAreaPosition: point(2px, 2px);
rippleAreaSize: 44px;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
ripple: defaultRippleAnimationBgOver;
}
calendarPreviousDisabled: icon {{ "calendar_down-flip_vertical", menuIconFg }};
calendarNext: IconButton(calendarPrevious) {
@@ -616,9 +612,7 @@ proxyTryIPv6Padding: margins(22px, 8px, 22px, 5px);
proxyRowPadding: margins(22px, 8px, 8px, 8px);
proxyRowIconSkip: 32px;
proxyRowSkip: 2px;
proxyRowRipple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
proxyRowRipple: defaultRippleAnimationBgOver;
proxyRowTitleFg: windowFg;
proxyRowTitlePalette: TextPalette(defaultTextPalette) {
linkFg: windowSubTextFg;
@@ -683,9 +677,7 @@ themesMenuToggle: IconButton(defaultIconButton) {
rippleAreaPosition: point(4px, 4px);
rippleAreaSize: 36px;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
ripple: defaultRippleAnimationBgOver;
}
themesMenuPosition: point(-2px, 25px);
@@ -738,9 +730,7 @@ createPollOptionRemove: CrossButton {
duration: 150;
loadingPeriod: 1000;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
ripple: defaultRippleAnimationBgOver;
}
createPollOptionRemovePosition: point(11px, 9px);
createPollOptionEmojiPositionSkip: 4px;
@@ -888,6 +878,13 @@ peerListWithInviteViaLink: PeerList(peerListBox) {
peerListSingleRow: PeerList(peerListBox) {
padding: margins(0px, 0px, 0px, 0px);
}
peerListSmallSkips: PeerList(peerListBox) {
padding: margins(
0px,
defaultVerticalListSkip,
0px,
defaultVerticalListSkip);
}
scheduleHeight: 95px;
scheduleDateTop: 38px;
@@ -951,9 +948,7 @@ sponsoredUrlButton: RoundButton(defaultActiveButton) {
textTop: 7px;
style: defaultTextStyle;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
ripple: defaultRippleAnimationBgOver;
}
requestPeerRestriction: FlatLabel(defaultFlatLabel) {

View File

@@ -96,6 +96,9 @@ void ChangeFilterById(
Ui::Text::WithEntities));
}
}).fail([=](const MTP::Error &error) {
LOG(("API Error: failed to %1 a dialog to a folder. %2")
.arg(add ? u"add"_q : u"remove"_q)
.arg(error.type()));
// Revert filter on fail.
history->owner().chatsFilters().set(was);
}).send();

View File

@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/premium_limits_box.h"
#include "boxes/premium_preview_box.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/field_autocomplete.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
@@ -237,7 +238,7 @@ EditCaptionBox::EditCaptionBox(
Fn<void()> saved)
: _controller(controller)
, _historyItem(item)
, _isAllowedEditMedia(item->media() && item->media()->allowsEditMedia())
, _isAllowedEditMedia(item->allowsEditMedia())
, _albumType(ComputeAlbumType(item))
, _controls(base::make_unique_q<Ui::VerticalLayout>(this))
, _scroll(base::make_unique_q<Ui::ScrollArea>(this, st::boxScroll))
@@ -252,8 +253,8 @@ EditCaptionBox::EditCaptionBox(
, _initialText(std::move(text))
, _initialList(std::move(list))
, _saved(std::move(saved)) {
Expects(item->media() != nullptr);
Expects(item->media()->allowsEditCaption());
Expects(!_initialList.files.empty());
Expects(!item->media() || item->media()->allowsEditCaption());
_mediaEditManager.start(item, spoilered, invertCaption);
@@ -371,6 +372,14 @@ void EditCaptionBox::StartPhotoEdit(
});
}
void EditCaptionBox::showFinished() {
if (const auto raw = _autocomplete.get()) {
InvokeQueued(raw, [=] {
raw->raise();
});
}
}
void EditCaptionBox::prepare() {
const auto button = addButton(tr::lng_settings_save(), [=] { save(); });
addButton(tr::lng_cancel(), [=] { closeBox(); });
@@ -413,7 +422,8 @@ void EditCaptionBox::prepare() {
setInitialText();
if (!setPreparedList(std::move(_initialList))) {
rebuildPreview();
crl::on_main(this, [=] { closeBox(); });
return;
}
setupEditEventHandler();
SetupShadowsToScrollContent(this, _scroll, _contentHeight.events());
@@ -525,6 +535,7 @@ void EditCaptionBox::setupField() {
_field.get(),
Window::GifPauseReason::Layer,
allow);
setupFieldAutocomplete();
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_field,
@@ -562,6 +573,55 @@ void EditCaptionBox::setupField() {
});
}
void EditCaptionBox::setupFieldAutocomplete() {
const auto parent = getDelegate()->outerContainer();
ChatHelpers::InitFieldAutocomplete(_autocomplete, {
.parent = parent,
.show = _controller->uiShow(),
.field = _field.get(),
.peer = _historyItem->history()->peer,
.features = [=] {
auto result = ChatHelpers::ComposeFeatures();
result.autocompleteCommands = false;
result.suggestStickersByEmoji = false;
return result;
},
});
const auto raw = _autocomplete.get();
const auto scheduled = std::make_shared<bool>();
const auto recountPostponed = [=] {
if (*scheduled) {
return;
}
*scheduled = true;
Ui::PostponeCall(raw, [=] {
*scheduled = false;
auto field = Ui::MapFrom(parent, this, _field->geometry());
_autocomplete->setBoundings(QRect(
field.x() - _field->x(),
st::defaultBox.margin.top(),
width(),
(field.y()
+ st::defaultComposeFiles.caption.textMargins.top()
+ st::defaultComposeFiles.caption.placeholderShift
+ st::defaultComposeFiles.caption.placeholderFont->height
- st::defaultBox.margin.top())));
});
};
for (auto w = (QWidget*)_field.get(); w; w = w->parentWidget()) {
base::install_event_filter(raw, w, [=](not_null<QEvent*> e) {
if (e->type() == QEvent::Move || e->type() == QEvent::Resize) {
recountPostponed();
}
return base::EventFilterResult::Continue;
});
if (w == parent) {
break;
}
}
}
void EditCaptionBox::setInitialText() {
_field->setTextWithTags(
_initialText,

View File

@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace ChatHelpers {
class TabbedPanel;
class FieldAutocomplete;
} // namespace ChatHelpers
namespace Window {
@@ -68,6 +69,8 @@ public:
bool invertCaption,
Fn<void()> saved);
void showFinished() override;
protected:
void prepare() override;
void setInnerFocus() override;
@@ -81,6 +84,7 @@ private:
void setupEditEventHandler();
void setupPhotoEditorEventHandler();
void setupField();
void setupFieldAutocomplete();
void setupControls();
void setInitialText();
@@ -115,6 +119,8 @@ private:
const base::unique_qptr<Ui::InputField> _field;
const base::unique_qptr<Ui::EmojiButton> _emojiToggle;
std::unique_ptr<ChatHelpers::FieldAutocomplete> _autocomplete;
base::unique_qptr<Ui::AbstractSinglePreview> _content;
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;
base::unique_qptr<QObject> _emojiFilter;

View File

@@ -36,13 +36,63 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_settings.h"
#include "styles/style_layers.h"
#include "styles/style_menu_icons.h"
#include "styles/style_window.h"
namespace {
constexpr auto kPremiumsRowId = PeerId(FakeChatId(BareId(1))).value;
constexpr auto kMiniAppsRowId = PeerId(FakeChatId(BareId(2))).value;
using Exceptions = Api::UserPrivacy::Exceptions;
enum class SpecialRowType {
Premiums,
MiniApps,
};
[[nodiscard]] PaintRoundImageCallback GeneratePremiumsUserpicCallback(
bool forceRound) {
return [=](QPainter &p, int x, int y, int outerWidth, int size) {
auto gradient = QLinearGradient(
QPointF(x, y),
QPointF(x + size, y + size));
gradient.setStops(Ui::Premium::ButtonGradientStops());
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(gradient);
if (forceRound) {
p.drawEllipse(x, y, size, size);
} else {
const auto radius = size * Ui::ForumUserpicRadiusMultiplier();
p.drawRoundedRect(x, y, size, size, radius, radius);
}
st::settingsPrivacyPremium.paintInCenter(p, QRect(x, y, size, size));
};
}
[[nodiscard]] PaintRoundImageCallback GenerateMiniAppsUserpicCallback(
bool forceRound) {
return [=](QPainter &p, int x, int y, int outerWidth, int size) {
const auto &color1 = st::historyPeer6UserpicBg;
const auto &color2 = st::historyPeer6UserpicBg2;
auto hq = PainterHighQualityEnabler(p);
auto gradient = QLinearGradient(x, y, x, y + size);
gradient.setStops({ { 0., color1->c }, { 1., color2->c } });
p.setPen(Qt::NoPen);
p.setBrush(gradient);
if (forceRound) {
p.drawEllipse(x, y, size, size);
} else {
const auto radius = size * Ui::ForumUserpicRadiusMultiplier();
p.drawRoundedRect(x, y, size, size, radius, radius);
}
st::windowFilterTypeBots.paintInCenter(p, QRect(x, y, size, size));
};
}
void CreateRadiobuttonLock(
not_null<Ui::RpWidget*> widget,
const style::Checkbox &st) {
@@ -102,7 +152,7 @@ public:
not_null<Main::Session*> session,
rpl::producer<QString> title,
const Exceptions &selected,
bool allowChoosePremiums);
std::optional<SpecialRowType> allowChooseSpecial);
Main::Session &session() const override;
void rowClicked(not_null<PeerListRow*> row) override;
@@ -110,18 +160,20 @@ public:
bool handleDeselectForeignRow(PeerListRowId itemId) override;
[[nodiscard]] bool premiumsSelected() const;
[[nodiscard]] bool miniAppsSelected() const;
protected:
void prepareViewHook() override;
std::unique_ptr<Row> createRow(not_null<History*> history) override;
private:
[[nodiscard]] object_ptr<Ui::RpWidget> preparePremiumsRowList();
[[nodiscard]] object_ptr<Ui::RpWidget> prepareSpecialRowList(
SpecialRowType type);
const not_null<Main::Session*> _session;
rpl::producer<QString> _title;
Exceptions _selected;
bool _allowChoosePremiums = false;
std::optional<SpecialRowType> _allowChooseSpecial;
PeerListContentDelegate *_typesDelegate = nullptr;
Fn<void(PeerListRowId)> _deselectOption;
@@ -133,9 +185,9 @@ struct RowSelectionChange {
bool checked = false;
};
class PremiumsRow final : public PeerListRow {
class SpecialRow final : public PeerListRow {
public:
PremiumsRow();
explicit SpecialRow(SpecialRowType type);
QString generateName() override;
QString generateShortName() override;
@@ -147,66 +199,61 @@ public:
class TypesController final : public PeerListController {
public:
TypesController(not_null<Main::Session*> session, bool premiums);
TypesController(not_null<Main::Session*> session, SpecialRowType type);
Main::Session &session() const override;
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
[[nodiscard]] bool premiumsSelected() const;
[[nodiscard]] rpl::producer<bool> premiumsChanges() const;
[[nodiscard]] bool specialSelected() const;
[[nodiscard]] rpl::producer<bool> specialChanges() const;
[[nodiscard]] auto rowSelectionChanges() const
-> rpl::producer<RowSelectionChange>;
private:
const not_null<Main::Session*> _session;
const SpecialRowType _type;
rpl::event_stream<> _selectionChanged;
rpl::event_stream<RowSelectionChange> _rowSelectionChanges;
};
PremiumsRow::PremiumsRow() : PeerListRow(kPremiumsRowId) {
setCustomStatus(tr::lng_edit_privacy_premium_status(tr::now));
SpecialRow::SpecialRow(SpecialRowType type)
: PeerListRow((type == SpecialRowType::Premiums)
? kPremiumsRowId
: kMiniAppsRowId) {
setCustomStatus((id() == kPremiumsRowId)
? tr::lng_edit_privacy_premium_status(tr::now)
: tr::lng_edit_privacy_miniapps_status(tr::now));
}
QString PremiumsRow::generateName() {
return tr::lng_edit_privacy_premium(tr::now);
QString SpecialRow::generateName() {
return (id() == kPremiumsRowId)
? tr::lng_edit_privacy_premium(tr::now)
: tr::lng_edit_privacy_miniapps(tr::now);
}
QString PremiumsRow::generateShortName() {
QString SpecialRow::generateShortName() {
return generateName();
}
PaintRoundImageCallback PremiumsRow::generatePaintUserpicCallback(
PaintRoundImageCallback SpecialRow::generatePaintUserpicCallback(
bool forceRound) {
return [=](QPainter &p, int x, int y, int outerWidth, int size) {
auto gradient = QLinearGradient(
QPointF(x, y),
QPointF(x + size, y + size));
gradient.setStops(Ui::Premium::ButtonGradientStops());
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(gradient);
if (forceRound) {
p.drawEllipse(x, y, size, size);
} else {
const auto radius = size * Ui::ForumUserpicRadiusMultiplier();
p.drawRoundedRect(x, y, size, size, radius, radius);
}
st::settingsPrivacyPremium.paintInCenter(p, QRect(x, y, size, size));
};
return (id() == kPremiumsRowId)
? GeneratePremiumsUserpicCallback(forceRound)
: GenerateMiniAppsUserpicCallback(forceRound);
}
bool PremiumsRow::useForumLikeUserpic() const {
bool SpecialRow::useForumLikeUserpic() const {
return true;
}
TypesController::TypesController(
not_null<Main::Session*> session,
bool premiums)
: _session(session) {
SpecialRowType type)
: _session(session)
, _type(type) {
}
Main::Session &TypesController::session() const {
@@ -214,12 +261,15 @@ Main::Session &TypesController::session() const {
}
void TypesController::prepare() {
delegate()->peerListAppendRow(std::make_unique<PremiumsRow>());
delegate()->peerListAppendRow(std::make_unique<SpecialRow>(_type));
delegate()->peerListRefreshRows();
}
bool TypesController::premiumsSelected() const {
const auto row = delegate()->peerListFindRow(kPremiumsRowId);
bool TypesController::specialSelected() const {
const auto premiums = (_type == SpecialRowType::Premiums);
const auto row = delegate()->peerListFindRow(premiums
? kPremiumsRowId
: kMiniAppsRowId);
Assert(row != nullptr);
return row->checked();
@@ -231,10 +281,10 @@ void TypesController::rowClicked(not_null<PeerListRow*> row) {
_rowSelectionChanges.fire({ row, checked });
}
rpl::producer<bool> TypesController::premiumsChanges() const {
rpl::producer<bool> TypesController::specialChanges() const {
return _rowSelectionChanges.events(
) | rpl::map([=] {
return premiumsSelected();
return specialSelected();
});
}
@@ -247,12 +297,12 @@ PrivacyExceptionsBoxController::PrivacyExceptionsBoxController(
not_null<Main::Session*> session,
rpl::producer<QString> title,
const Exceptions &selected,
bool allowChoosePremiums)
std::optional<SpecialRowType> allowChooseSpecial)
: ChatsListBoxController(session)
, _session(session)
, _title(std::move(title))
, _selected(selected)
, _allowChoosePremiums(allowChoosePremiums) {
, _allowChooseSpecial(allowChooseSpecial) {
}
Main::Session &PrivacyExceptionsBoxController::session() const {
@@ -261,14 +311,18 @@ Main::Session &PrivacyExceptionsBoxController::session() const {
void PrivacyExceptionsBoxController::prepareViewHook() {
delegate()->peerListSetTitle(std::move(_title));
if (_allowChoosePremiums || _selected.premiums) {
delegate()->peerListSetAboveWidget(preparePremiumsRowList());
if (_allowChooseSpecial || _selected.premiums || _selected.miniapps) {
delegate()->peerListSetAboveWidget(prepareSpecialRowList(
_allowChooseSpecial.value_or(_selected.premiums
? SpecialRowType::Premiums
: SpecialRowType::MiniApps)));
}
delegate()->peerListAddSelectedPeers(_selected.peers);
}
bool PrivacyExceptionsBoxController::isForeignRow(PeerListRowId itemId) {
return (itemId == kPremiumsRowId);
return (itemId == kPremiumsRowId)
|| (itemId == kMiniAppsRowId);
}
bool PrivacyExceptionsBoxController::handleDeselectForeignRow(
@@ -280,7 +334,8 @@ bool PrivacyExceptionsBoxController::handleDeselectForeignRow(
return false;
}
auto PrivacyExceptionsBoxController::preparePremiumsRowList()
auto PrivacyExceptionsBoxController::prepareSpecialRowList(
SpecialRowType type)
-> object_ptr<Ui::RpWidget> {
auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
const auto container = result.data();
@@ -291,30 +346,39 @@ auto PrivacyExceptionsBoxController::preparePremiumsRowList()
_typesDelegate = lifetime.make_state<PeerListContentDelegateSimple>();
const auto controller = lifetime.make_state<TypesController>(
&session(),
_selected.premiums);
type);
const auto content = result->add(object_ptr<PeerListContent>(
container,
controller));
_typesDelegate->setContent(content);
controller->setDelegate(_typesDelegate);
const auto selectType = [&](PeerListRowId id) {
const auto row = _typesDelegate->peerListFindRow(id);
if (row) {
content->changeCheckState(row, true, anim::type::instant);
this->delegate()->peerListSetForeignRowChecked(
row,
true,
anim::type::instant);
}
};
if (_selected.premiums) {
const auto row = _typesDelegate->peerListFindRow(kPremiumsRowId);
Assert(row != nullptr);
content->changeCheckState(row, true, anim::type::instant);
this->delegate()->peerListSetForeignRowChecked(
row,
true,
anim::type::instant);
selectType(kPremiumsRowId);
} else if (_selected.miniapps) {
selectType(kMiniAppsRowId);
}
container->add(CreatePeerListSectionSubtitle(
container,
tr::lng_edit_privacy_users_and_groups()));
controller->premiumsChanges(
) | rpl::start_with_next([=](bool premiums) {
_selected.premiums = premiums;
controller->specialChanges(
) | rpl::start_with_next([=](bool chosen) {
if (type == SpecialRowType::Premiums) {
_selected.premiums = chosen;
} else {
_selected.miniapps = chosen;
}
}, lifetime);
controller->rowSelectionChanges(
@@ -329,6 +393,8 @@ auto PrivacyExceptionsBoxController::preparePremiumsRowList()
if (const auto row = _typesDelegate->peerListFindRow(itemId)) {
if (itemId == kPremiumsRowId) {
_selected.premiums = false;
} else if (itemId == kMiniAppsRowId) {
_selected.miniapps = false;
}
_typesDelegate->peerListSetRowChecked(row, false);
}
@@ -337,10 +403,14 @@ auto PrivacyExceptionsBoxController::preparePremiumsRowList()
return result;
}
[[nodiscard]] bool PrivacyExceptionsBoxController::premiumsSelected() const {
bool PrivacyExceptionsBoxController::premiumsSelected() const {
return _selected.premiums;
}
bool PrivacyExceptionsBoxController::miniAppsSelected() const {
return _selected.miniapps;
}
void PrivacyExceptionsBoxController::rowClicked(not_null<PeerListRow*> row) {
const auto peer = row->peer();
@@ -356,11 +426,12 @@ void PrivacyExceptionsBoxController::rowClicked(not_null<PeerListRow*> row) {
auto PrivacyExceptionsBoxController::createRow(not_null<History*> history)
-> std::unique_ptr<Row> {
if (history->peer->isSelf() || history->peer->isRepliesChat()) {
const auto peer = history->peer;
if (peer->isSelf() || peer->isRepliesChat() || peer->isVerifyCodes()) {
return nullptr;
} else if (!history->peer->isUser()
&& !history->peer->isChat()
&& !history->peer->isMegagroup()) {
} else if (!peer->isUser()
&& !peer->isChat()
&& !peer->isMegagroup()) {
return nullptr;
}
auto result = std::make_unique<Row>(history);
@@ -411,6 +482,11 @@ EditPrivacyBox::EditPrivacyBox(
// If we switch from Everyone to Contacts or Nobody suggest Premiums.
_value.always.premiums = true;
}
if (_controller->allowMiniAppsToggle(Exception::Always)
&& _value.option == Option::Everyone) {
// If we switch from Everyone to Contacts or Nobody suggest MiniApps.
_value.always.miniapps = true;
}
}
void EditPrivacyBox::prepare() {
@@ -426,12 +502,18 @@ void EditPrivacyBox::editExceptions(
&_window->session(),
_controller->exceptionBoxTitle(exception),
exceptions(exception),
_controller->allowPremiumsToggle(exception));
(_controller->allowPremiumsToggle(exception)
? SpecialRowType::Premiums
: _controller->allowMiniAppsToggle(exception)
? SpecialRowType::MiniApps
: std::optional<SpecialRowType>()));
auto initBox = [=, controller = controller.get()](
not_null<PeerListBox*> box) {
box->addButton(tr::lng_settings_save(), crl::guard(this, [=] {
exceptions(exception).peers = box->collectSelectedRows();
exceptions(exception).premiums = controller->premiumsSelected();
auto &setTo = exceptions(exception);
setTo.peers = box->collectSelectedRows();
setTo.premiums = controller->premiumsSelected();
setTo.miniapps = controller->miniAppsSelected();
const auto type = [&] {
switch (exception) {
case Exception::Always: return Exception::Never;
@@ -439,11 +521,17 @@ void EditPrivacyBox::editExceptions(
}
Unexpected("Invalid exception value.");
}();
auto &removeFrom = exceptions(type).peers;
auto &removeFrom = exceptions(type);
for (const auto peer : exceptions(exception).peers) {
removeFrom.erase(
ranges::remove(removeFrom, peer),
end(removeFrom));
removeFrom.peers.erase(
ranges::remove(removeFrom.peers, peer),
end(removeFrom.peers));
}
if (setTo.premiums) {
removeFrom.premiums = false;
}
if (setTo.miniapps) {
removeFrom.miniapps = false;
}
done();
box->closeBox();
@@ -565,14 +653,21 @@ void EditPrivacyBox::setupContent() {
lt_count,
count)
: tr::lng_edit_privacy_exceptions_add(tr::now);
return !value.premiums
? users
: !count
? tr::lng_edit_privacy_premium(tr::now)
: tr::lng_edit_privacy_exceptions_premium_and(
tr::now,
lt_users,
users);
return value.premiums
? (!count
? tr::lng_edit_privacy_premium(tr::now)
: tr::lng_edit_privacy_exceptions_premium_and(
tr::now,
lt_users,
users))
: value.miniapps
? (!count
? tr::lng_edit_privacy_miniapps(tr::now)
: tr::lng_edit_privacy_exceptions_miniapps_and(
tr::now,
lt_users,
users))
: users;
});
_controller->handleExceptionsChange(
exception,

View File

@@ -61,6 +61,10 @@ public:
Exception exception) const {
return false;
}
[[nodiscard]] virtual bool allowMiniAppsToggle(
Exception exception) const {
return false;
}
virtual void handleExceptionsChange(
Exception exception,
rpl::producer<int> value) {

View File

@@ -131,10 +131,13 @@ ExceptionRow::ExceptionRow(not_null<History*> history) : Row(history) {
}
QString ExceptionRow::generateName() {
return peer()->isSelf()
const auto peer = this->peer();
return peer->isSelf()
? tr::lng_saved_messages(tr::now)
: peer()->isRepliesChat()
: peer->isRepliesChat()
? tr::lng_replies_messages(tr::now)
: peer->isVerifyCodes()
? tr::lng_verification_codes(tr::now)
: Row::generateName();
}
@@ -152,10 +155,11 @@ PaintRoundImageCallback ExceptionRow::generatePaintUserpicCallback(
return ForceRoundUserpicCallback(peer);
}
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
using namespace Ui;
if (saved) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
} else if (replies) {
Ui::EmptyUserpic::PaintRepliesMessages(p, x, y, outerWidth, size);
EmptyUserpic::PaintRepliesMessages(p, x, y, outerWidth, size);
} else {
peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
}

View File

@@ -122,9 +122,11 @@ void FilterChatsPreview::paintEvent(QPaintEvent *e) {
top += st.height;
}
for (auto &[history, userpic, name, button] : _removePeer) {
const auto savedMessages = history->peer->isSelf();
const auto repliesMessages = history->peer->isRepliesChat();
if (savedMessages || repliesMessages) {
const auto peer = history->peer;
const auto savedMessages = peer->isSelf();
const auto repliesMessages = peer->isRepliesChat();
const auto verifyCodes = peer->isVerifyCodes();
if (savedMessages || repliesMessages || verifyCodes) {
if (savedMessages) {
Ui::EmptyUserpic::PaintSavedMessages(
p,
@@ -132,13 +134,21 @@ void FilterChatsPreview::paintEvent(QPaintEvent *e) {
top + iconTop,
width(),
st.photoSize);
} else {
} else if (repliesMessages) {
Ui::EmptyUserpic::PaintRepliesMessages(
p,
iconLeft,
top + iconTop,
width(),
st.photoSize);
} else {
history->peer->paintUserpicLeft(
p,
userpic,
iconLeft,
top + iconTop,
width(),
st.photoSize);
}
p.setPen(st::contactsNameFg);
p.drawTextLeft(
@@ -147,7 +157,9 @@ void FilterChatsPreview::paintEvent(QPaintEvent *e) {
width(),
(savedMessages
? tr::lng_saved_messages(tr::now)
: tr::lng_replies_messages(tr::now)));
: repliesMessages
? tr::lng_replies_messages(tr::now)
: tr::lng_verification_codes(tr::now)));
} else {
history->peer->paintUserpicLeft(
p,

View File

@@ -337,12 +337,13 @@ PaintRoundImageCallback ChatRow::generatePaintUserpicCallback(
int y,
int outerWidth,
int size) mutable {
using namespace Ui;
if (forceRound && peer->isForum()) {
ForceRoundUserpicCallback(peer)(p, x, y, outerWidth, size);
} else if (saved) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
} else if (replies) {
Ui::EmptyUserpic::PaintRepliesMessages(p, x, y, outerWidth, size);
EmptyUserpic::PaintRepliesMessages(p, x, y, outerWidth, size);
} else {
peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
}

View File

@@ -123,7 +123,9 @@ void GiftCreditsBox(
box->verticalLayout(),
peer,
0,
[=] { gifted(); box->uiShow()->hideLayer(); });
[=] { gifted(); box->uiShow()->hideLayer(); },
tr::lng_credits_summary_options_subtitle(),
{});
box->setPinnedToBottomContent(
object_ptr<Ui::VerticalLayout>(box));

File diff suppressed because it is too large Load Diff

View File

@@ -9,8 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h"
class UserData;
namespace Api {
struct GiftCode;
} // namespace Api
@@ -29,29 +27,9 @@ class VerticalLayout;
} // namespace Ui
namespace Window {
class SessionController;
class SessionNavigation;
} // namespace Window
class GiftPremiumValidator final {
public:
GiftPremiumValidator(not_null<Window::SessionController*> controller);
void showBox(not_null<UserData*> user);
void showChoosePeerBox(const QString &ref);
void showChosenPeerBox(not_null<UserData*> user, const QString &ref);
void cancel();
private:
const not_null<Window::SessionController*> _controller;
MTP::Sender _api;
mtpRequestId _requestId = 0;
rpl::lifetime _manyGiftsLifetime;
};
[[nodiscard]] rpl::producer<QString> GiftDurationValue(int months);
[[nodiscard]] QString GiftDuration(int months);
@@ -76,6 +54,11 @@ void ResolveGiveawayInfo(
std::optional<Data::GiveawayStart> start,
std::optional<Data::GiveawayResults> results);
void AddStarGiftTable(
not_null<Window::SessionNavigation*> controller,
not_null<Ui::VerticalLayout*> container,
const Data::CreditsHistoryEntry &entry,
Fn<void()> convertToStars);
void AddCreditsHistoryEntryTable(
not_null<Window::SessionNavigation*> controller,
not_null<Ui::VerticalLayout*> container,

View File

@@ -240,7 +240,8 @@ int LocalStorageBox::Row::resizeGetHeight(int newWidth) {
}
void LocalStorageBox::Row::paintEvent(QPaintEvent *e) {
if (!_progress || true) {
#if 0 // not used
if (!_progress) {
return;
}
auto p = QPainter(this);
@@ -254,6 +255,7 @@ void LocalStorageBox::Row::paintEvent(QPaintEvent *e) {
st::proxyCheckingPosition.y() + bottom
},
width());
#endif
}
QString LocalStorageBox::Row::titleText(const Database::TaggedSummary &data) const {

View File

@@ -206,7 +206,9 @@ void PeerListBox::keyPressEvent(QKeyEvent *e) {
content()->selectSkipPage(height(), 1);
} else if (e->key() == Qt::Key_PageUp) {
content()->selectSkipPage(height(), -1);
} else if (e->key() == Qt::Key_Escape && _select && !_select->entity()->getQuery().isEmpty()) {
} else if (e->key() == Qt::Key_Escape
&& _select
&& !_select->entity()->getQuery().isEmpty()) {
_select->entity()->clearQuery();
} else {
BoxContent::keyPressEvent(e);
@@ -215,7 +217,19 @@ void PeerListBox::keyPressEvent(QKeyEvent *e) {
void PeerListBox::searchQueryChanged(const QString &query) {
scrollToY(0);
content()->searchQueryChanged(query);
const auto isEmpty = content()->searchQueryChanged(query);
if (_specialTabsMode.enabled) {
const auto was = _specialTabsMode.searchIsActive;
_specialTabsMode.searchIsActive = !isEmpty;
if (was != _specialTabsMode.searchIsActive) {
if (_specialTabsMode.searchIsActive) {
_specialTabsMode.topSkip = _addedTopScrollSkip;
setAddedTopScrollSkip(0);
} else {
setAddedTopScrollSkip(_specialTabsMode.topSkip);
}
}
}
}
void PeerListBox::resizeEvent(QResizeEvent *e) {
@@ -447,6 +461,8 @@ void PeerListBox::addSelectItem(
? tr::lng_saved_short(tr::now)
: (respect && peer->isRepliesChat())
? tr::lng_replies_messages(tr::now)
: (respect && peer->isVerifyCodes())
? tr::lng_verification_codes(tr::now)
: peer->shortName();
addSelectItem(
peer->id.value,
@@ -541,6 +557,19 @@ auto PeerListBox::collectSelectedRows()
return result;
}
rpl::producer<int> PeerListBox::multiSelectHeightValue() const {
return _select ? _select->heightValue() : rpl::single(0);
}
void PeerListBox::setSpecialTabMode(bool value) {
content()->setIgnoreHiddenRowsOnSearch(value);
if (value) {
_specialTabsMode.enabled = true;
} else {
_specialTabsMode = {};
}
}
PeerListRow::PeerListRow(not_null<PeerData*> peer)
: PeerListRow(peer, peer->id.value) {
}
@@ -625,6 +654,8 @@ void PeerListRow::refreshName(const style::PeerListItem &st) {
? tr::lng_saved_messages(tr::now)
: _isRepliesMessagesChat
? tr::lng_replies_messages(tr::now)
: _isVerifyCodesChat
? tr::lng_verification_codes(tr::now)
: generateName();
_name.setText(st.nameStyle, text, Ui::NameTextOptions());
}
@@ -695,6 +726,8 @@ QString PeerListRow::generateShortName() {
? tr::lng_saved_short(tr::now)
: _isRepliesMessagesChat
? tr::lng_replies_messages(tr::now)
: _isVerifyCodesChat
? tr::lng_verification_codes(tr::now)
: peer()->shortName();
}
@@ -715,10 +748,11 @@ PaintRoundImageCallback PeerListRow::generatePaintUserpicCallback(
return ForceRoundUserpicCallback(peer);
}
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
using namespace Ui;
if (saved) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
} else if (replies) {
Ui::EmptyUserpic::PaintRepliesMessages(p, x, y, outerWidth, size);
EmptyUserpic::PaintRepliesMessages(p, x, y, outerWidth, size);
} else {
peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
}
@@ -757,12 +791,14 @@ int PeerListRow::paintNameIconGetWidth(
int availableWidth,
int outerWidth,
bool selected) {
if (special()
if (_skipPeerBadge
|| special()
|| !_savedMessagesStatus.isEmpty()
|| _isRepliesMessagesChat) {
|| _isRepliesMessagesChat
|| _isVerifyCodesChat) {
return 0;
}
return _bagde.drawGetWidth(
return _badge.drawGetWidth(
p,
QRect(
nameLeft,
@@ -874,12 +910,13 @@ void PeerListRow::paintDisabledCheckUserpic(
auto iconBorderPen = st.checkbox.check.border->p;
iconBorderPen.setWidth(st.checkbox.selectWidth);
const auto size = userpicRadius * 2;
if (!_savedMessagesStatus.isEmpty()) {
Ui::EmptyUserpic::PaintSavedMessages(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
Ui::EmptyUserpic::PaintSavedMessages(p, userpicLeft, userpicTop, outerWidth, size);
} else if (_isRepliesMessagesChat) {
Ui::EmptyUserpic::PaintRepliesMessages(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
Ui::EmptyUserpic::PaintRepliesMessages(p, userpicLeft, userpicTop, outerWidth, size);
} else {
peer()->paintUserpicLeft(p, _userpic, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
peer()->paintUserpicLeft(p, _userpic, userpicLeft, userpicTop, outerWidth, size);
}
{
@@ -1069,10 +1106,13 @@ void PeerListContent::setRowHidden(not_null<PeerListRow*> row, bool hidden) {
void PeerListContent::addRowEntry(not_null<PeerListRow*> row) {
const auto savedMessagesStatus = _controller->savedMessagesChatStatus();
if (!savedMessagesStatus.isEmpty() && !row->special()) {
if (row->peer()->isSelf()) {
const auto peer = row->peer();
if (peer->isSelf()) {
row->setSavedMessagesChatStatus(savedMessagesStatus);
} else if (row->peer()->isRepliesChat()) {
} else if (peer->isRepliesChat()) {
row->setIsRepliesMessagesChat(true);
} else if (peer->isVerifyCodes()) {
row->setIsVerifyCodesChat(true);
}
}
_rowsById.emplace(row->id(), row);
@@ -1372,10 +1412,12 @@ int PeerListContent::labelHeight() const {
void PeerListContent::refreshRows() {
if (!_hiddenRows.empty()) {
_filterResults.clear();
for (const auto &row : _rows) {
if (!row->hidden()) {
_filterResults.push_back(row.get());
if (!_ignoreHiddenRowsOnSearch || _normalizedSearchQuery.isEmpty()) {
_filterResults.clear();
for (const auto &row : _rows) {
if (!row->hidden()) {
_filterResults.push_back(row.get());
}
}
}
}
@@ -1931,6 +1973,13 @@ PeerListContent::SkipResult PeerListContent::selectSkip(int direction) {
}
}
if (_controller->overrideKeyboardNavigation(
direction,
_selected.index.value,
newSelectedIndex)) {
return { _selected.index.value, _selected.index.value };
}
_selected.index.value = newSelectedIndex;
_selected.element = 0;
if (newSelectedIndex >= 0) {
@@ -2030,13 +2079,16 @@ void PeerListContent::checkScrollForPreload() {
}
}
void PeerListContent::searchQueryChanged(QString query) {
PeerListContent::IsEmpty PeerListContent::searchQueryChanged(QString query) {
const auto searchWordsList = TextUtilities::PrepareSearchWords(query);
const auto normalizedQuery = searchWordsList.join(' ');
if (_ignoreHiddenRowsOnSearch && !normalizedQuery.isEmpty()) {
_filterResults.clear();
}
if (_normalizedSearchQuery != normalizedQuery) {
setSearchQuery(query, normalizedQuery);
if (_controller->searchInLocal() && !searchWordsList.isEmpty()) {
Assert(_hiddenRows.empty());
Assert(_hiddenRows.empty() || _ignoreHiddenRowsOnSearch);
auto minimalList = (const std::vector<not_null<PeerListRow*>>*)nullptr;
for (const auto &searchWord : searchWordsList) {
@@ -2084,6 +2136,7 @@ void PeerListContent::searchQueryChanged(QString query) {
}
refreshRows();
}
return _normalizedSearchQuery.isEmpty();
}
std::unique_ptr<PeerListState> PeerListContent::saveState() const {
@@ -2172,6 +2225,10 @@ void PeerListContent::dragLeft() {
clearSelection();
}
void PeerListContent::setIgnoreHiddenRowsOnSearch(bool value) {
_ignoreHiddenRowsOnSearch = value;
}
void PeerListContent::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {

View File

@@ -200,6 +200,9 @@ public:
void setIsRepliesMessagesChat(bool isRepliesMessagesChat) {
_isRepliesMessagesChat = isRepliesMessagesChat;
}
void setIsVerifyCodesChat(bool isVerifyCodesChat) {
_isVerifyCodesChat = isVerifyCodesChat;
}
template <typename UpdateCallback>
void setChecked(
@@ -251,6 +254,10 @@ public:
return _nameFirstLetters;
}
void setSkipPeerBadge(bool skip) {
_skipPeerBadge = skip;
}
virtual void lazyInitialize(const style::PeerListItem &st);
virtual void paintStatusText(
Painter &p,
@@ -288,7 +295,7 @@ private:
std::unique_ptr<Ui::RoundImageCheckbox> _checkbox;
Ui::Text::String _name;
Ui::Text::String _status;
Ui::PeerBadge _bagde;
Ui::PeerBadge _badge;
StatusType _statusType = StatusType::Online;
crl::time _statusValidTill = 0;
base::flat_set<QChar> _nameFirstLetters;
@@ -299,6 +306,8 @@ private:
bool _initialized : 1 = false;
bool _isSearchResult : 1 = false;
bool _isRepliesMessagesChat : 1 = false;
bool _isVerifyCodesChat : 1 = false;
bool _skipPeerBadge : 1 = false;
};
@@ -348,6 +357,8 @@ public:
virtual int peerListPartitionRows(Fn<bool(const PeerListRow &a)> border) = 0;
virtual std::shared_ptr<Main::SessionShow> peerListUiShow() = 0;
virtual void peerListSelectSkip(int direction) = 0;
virtual void peerListPressLeftToContextMenu(bool shown) = 0;
virtual bool peerListTrackRowPressFromGlobal(QPoint globalPosition) = 0;
@@ -564,6 +575,13 @@ public:
Unexpected("PeerListController::customRowRippleMaskGenerator.");
}
virtual bool overrideKeyboardNavigation(
int direction,
int fromIndex,
int toIndex) {
return false;
}
[[nodiscard]] rpl::lifetime &lifetime() {
return _lifetime;
}
@@ -634,12 +652,15 @@ public:
[[nodiscard]] bool hasPressed() const;
void clearSelection();
void searchQueryChanged(QString query);
using IsEmpty = bool;
IsEmpty searchQueryChanged(QString query);
bool submitted();
PeerListRowId updateFromParentDrag(QPoint globalPosition);
void dragLeft();
void setIgnoreHiddenRowsOnSearch(bool value);
// Interface for the controller.
void appendRow(std::unique_ptr<PeerListRow> row);
void appendSearchRow(std::unique_ptr<PeerListRow> row);
@@ -861,6 +882,7 @@ private:
int _aboveHeight = 0;
int _belowHeight = 0;
bool _hideEmpty = false;
bool _ignoreHiddenRowsOnSearch = false;
object_ptr<Ui::RpWidget> _aboveWidget = { nullptr };
object_ptr<Ui::RpWidget> _aboveSearchWidget = { nullptr };
object_ptr<Ui::RpWidget> _belowWidget = { nullptr };
@@ -1007,6 +1029,10 @@ public:
bool highlightRow,
Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr) override;
void peerListSelectSkip(int direction) override {
_content->selectSkip(direction);
}
void peerListPressLeftToContextMenu(bool shown) override {
_content->pressLeftToContextMenu(shown);
}
@@ -1080,6 +1106,9 @@ public:
[[nodiscard]] std::vector<PeerListRowId> collectSelectedIds();
[[nodiscard]] std::vector<not_null<PeerData*>> collectSelectedRows();
[[nodiscard]] rpl::producer<int> multiSelectHeightValue() const;
void setSpecialTabMode(bool value);
void peerListSetTitle(rpl::producer<QString> title) override {
setTitle(std::move(title));
@@ -1146,4 +1175,11 @@ private:
bool _scrollBottomFixed = false;
int _addedTopScrollSkip = 0;
struct SpecialTabsMode final {
bool enabled = false;
bool searchIsActive = false;
int topSkip = 0;
};
SpecialTabsMode _specialTabsMode;
};

View File

@@ -842,6 +842,7 @@ auto ChooseRecipientBoxController::createRow(
? !_filter(history)
: ((peer->isBroadcast() && !Data::CanSendAnything(peer))
|| peer->isRepliesChat()
|| peer->isVerifyCodes()
|| (peer->isUser() && (_premiumRequiredError
? !peer->asUser()->canSendIgnoreRequirePremium()
: !Data::CanSendAnything(peer))));
@@ -1064,6 +1065,11 @@ std::unique_ptr<PeerListRow> ChooseTopicBoxController::createSearchRow(
return nullptr;
}
std::unique_ptr<PeerListRow> ChooseTopicBoxController::MakeRow(
not_null<Data::ForumTopic*> topic) {
return std::make_unique<Row>(topic);
}
auto ChooseTopicBoxController::createRow(not_null<Data::ForumTopic*> topic)
-> std::unique_ptr<Row> {
const auto skip = _filter && !_filter(topic);

View File

@@ -335,6 +335,9 @@ public:
void loadMoreRows() override;
std::unique_ptr<PeerListRow> createSearchRow(PeerListRowId id) override;
[[nodiscard]] static std::unique_ptr<PeerListRow> MakeRow(
not_null<Data::ForumTopic*> topic);
private:
class Row final : public PeerListRow {
public:

View File

@@ -269,7 +269,8 @@ PaintRoundImageCallback ForbiddenRow::generatePaintUserpicCallback(
const auto peer = this->peer();
const auto saved = peer->isSelf();
const auto replies = peer->isRepliesChat();
auto userpic = (saved || replies)
const auto verifyCodes = peer->isVerifyCodes();
auto userpic = (saved || replies || verifyCodes)
? Ui::PeerUserpicView()
: ensureUserpicView();
auto paint = [=](
@@ -302,6 +303,7 @@ PaintRoundImageCallback ForbiddenRow::generatePaintUserpicCallback(
repaint = (_paletteVersion != style::PaletteVersion())
|| (!saved
&& !replies
&& !verifyCodes
&& (_userpicKey != peer->userpicUniqueKey(userpic)));
}
if (repaint) {

View File

@@ -30,10 +30,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h"
#include "base/unixtime.h"
#include "ui/effects/outline_segments.h"
#include "ui/widgets/menu/menu_multiline_action.h"
#include "ui/widgets/popup_menu.h"
#include "ui/text/text_utilities.h"
#include "info/profile/info_profile_values.h"
#include "window/window_session_controller.h"
#include "history/history.h"
#include "styles/style_chat.h"
#include "styles/style_menu_icons.h"
namespace {
@@ -1645,6 +1648,51 @@ base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
auto result = base::make_unique_q<Ui::PopupMenu>(
parent,
st::popupMenuWithIcons);
const auto addToEnd = gsl::finally([&] {
const auto addInfoAction = [&](
not_null<PeerData*> by,
tr::phrase<lngtag_user, lngtag_date> phrase,
TimeId since) {
auto text = phrase(
tr::now,
lt_user,
Ui::Text::Bold(by->name()),
lt_date,
Ui::Text::Bold(
langDateTimeFull(base::unixtime::parse(since))),
Ui::Text::WithEntities);
auto button = base::make_unique_q<Ui::Menu::MultilineAction>(
result->menu(),
result->st().menu,
st::historyHasCustomEmoji,
st::historyHasCustomEmojiPosition,
std::move(text));
if (const auto n = _navigation) {
button->setClickedCallback([=] {
n->parentController()->show(PrepareShortInfoBox(by, n));
});
}
result->addSeparator();
result->addAction(std::move(button));
};
if (const auto by = _additional.restrictedBy(participant)) {
if (const auto since = _additional.restrictedSince(participant)) {
addInfoAction(
by,
_additional.isKicked(participant)
? tr::lng_rights_chat_banned_by
: tr::lng_rights_chat_restricted_by,
since);
}
} else if (user) {
if (const auto by = _additional.adminPromotedBy(user)) {
if (const auto since = _additional.adminPromotedSince(user)) {
addInfoAction(by, tr::lng_rights_about_by, since);
}
}
}
});
if (_navigation) {
result->addAction(
(participant->isUser()
@@ -1652,39 +1700,14 @@ base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
: participant->isBroadcast()
? tr::lng_context_view_channel
: tr::lng_context_view_group)(tr::now),
crl::guard(this, [=] {
_navigation->showPeerInfo(participant); }),
crl::guard(this, [=, this] {
_navigation->parentController()->show(
PrepareShortInfoBox(participant, _navigation));
}),
(participant->isUser()
? &st::menuIconProfile
: &st::menuIconInfo));
}
if (const auto by = _additional.restrictedBy(participant)) {
result->addAction(
(_role == Role::Kicked
? tr::lng_channel_banned_status_removed_by
: tr::lng_channel_banned_status_restricted_by)(
tr::now,
lt_user,
by->name()),
crl::guard(this, [=] {
_navigation->parentController()->show(
PrepareShortInfoBox(by, _navigation));
}),
&st::menuIconAdmin);
} else if (user) {
if (const auto by = _additional.adminPromotedBy(user)) {
result->addAction(
tr::lng_channel_admin_status_promoted_by(
tr::now,
lt_user,
by->name()),
crl::guard(this, [=] {
_navigation->parentController()->show(
PrepareShortInfoBox(by, _navigation));
}),
&st::menuIconAdmin);
}
}
if (_role == Role::Kicked) {
if (_peer->isMegagroup()
&& _additional.canRestrictParticipant(participant)) {

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "api/api_credits.h"
#include "api/api_peer_photo.h"
#include "api/api_statistics.h"
#include "api/api_user_names.h"
#include "main/main_session.h"
#include "ui/boxes/confirm_box.h"
@@ -33,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/tabbed_selector.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "data/components/credits.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_peer.h"
@@ -45,6 +47,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/admin_log/history_admin_log_section.h"
#include "info/bot/earn/info_bot_earn_widget.h"
#include "info/channel_statistics/boosts/info_boosts_widget.h"
#include "info/channel_statistics/earn/earn_format.h"
#include "info/channel_statistics/earn/earn_icons.h"
#include "info/channel_statistics/earn/info_channel_earn_widget.h"
#include "info/profile/info_profile_values.h"
#include "info/info_memento.h"
#include "lang/lang_keys.h"
@@ -351,7 +356,8 @@ private:
void fillPendingRequestsButton();
void fillBotUsernamesButton();
void fillBotBalanceButton();
void fillBotCurrencyButton();
void fillBotCreditsButton();
void fillBotEditIntroButton();
void fillBotEditCommandsButton();
void fillBotEditSettingsButton();
@@ -1173,7 +1179,8 @@ void Controller::fillManageSection() {
::AddSkip(container, 0);
fillBotUsernamesButton();
fillBotBalanceButton();
fillBotCurrencyButton();
fillBotCreditsButton();
fillBotEditIntroButton();
fillBotEditCommandsButton();
fillBotEditSettingsButton();
@@ -1582,7 +1589,7 @@ void Controller::fillBotUsernamesButton() {
{ &st::menuIconLinks });
}
void Controller::fillBotBalanceButton() {
void Controller::fillBotCurrencyButton() {
Expects(_isBot);
struct State final {
@@ -1591,20 +1598,88 @@ void Controller::fillBotBalanceButton() {
auto &lifetime = _controls.buttonsLayout->lifetime();
const auto state = lifetime.make_state<State>();
const auto format = [=](uint64 balance) {
return Info::ChannelEarn::MajorPart(balance)
+ Info::ChannelEarn::MinorPart(balance);
};
const auto was = _peer->session().credits().balanceCurrency(
_peer->id);
if (was) {
state->balance = format(was);
}
const auto wrap = _controls.buttonsLayout->add(
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
_controls.buttonsLayout,
EditPeerInfoBox::CreateButton(
_controls.buttonsLayout,
tr::lng_manage_peer_bot_balance(),
tr::lng_manage_peer_bot_balance_currency(),
state->balance.value(),
[controller = _navigation->parentController(), peer = _peer] {
controller->showSection(Info::ChannelEarn::Make(peer));
},
st::manageGroupButton,
{})));
wrap->toggle(!state->balance.current().isEmpty(), anim::type::instant);
const auto button = wrap->entity();
{
const auto currencyLoad
= button->lifetime().make_state<Api::EarnStatistics>(_peer);
currencyLoad->request(
) | rpl::start_with_error_done([=](const QString &error) {
}, [=] {
const auto balance = currencyLoad->data().currentBalance;
if (balance) {
wrap->toggle(true, anim::type::normal);
}
state->balance = format(balance);
}, button->lifetime());
}
{
const auto icon = Ui::CreateChild<Ui::RpWidget>(button);
icon->resize(st::menuIconLinks.size());
const auto image = Ui::Earn::MenuIconCurrency(icon->size());
icon->paintRequest() | rpl::start_with_next([=] {
auto p = QPainter(icon);
p.drawImage(0, 0, image);
}, icon->lifetime());
button->sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
icon->moveToLeft(
button->st().iconLeft,
(size.height() - icon->height()) / 2);
}, icon->lifetime());
}
}
void Controller::fillBotCreditsButton() {
Expects(_isBot);
struct State final {
rpl::variable<QString> balance;
};
auto &lifetime = _controls.buttonsLayout->lifetime();
const auto state = lifetime.make_state<State>();
if (const auto balance = _peer->session().credits().balance(_peer->id)) {
state->balance = Lang::FormatCountDecimal(balance);
}
const auto wrap = _controls.buttonsLayout->add(
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
_controls.buttonsLayout,
EditPeerInfoBox::CreateButton(
_controls.buttonsLayout,
tr::lng_manage_peer_bot_balance_credits(),
state->balance.value(),
[controller = _navigation->parentController(), peer = _peer] {
controller->showSection(Info::BotEarn::Make(peer));
},
st::manageGroupButton,
{})));
wrap->toggle(false, anim::type::instant);
wrap->toggle(!state->balance.current().isEmpty(), anim::type::instant);
const auto button = wrap->entity();
{
@@ -1614,46 +1689,22 @@ void Controller::fillBotBalanceButton() {
if (data.balance) {
wrap->toggle(true, anim::type::normal);
}
state->balance = QString::number(data.balance);
state->balance = Lang::FormatCountDecimal(data.balance);
});
}
{
constexpr auto kSizeShift = 3;
constexpr auto kStrokeWidth = 5;
const auto icon = Ui::CreateChild<Ui::RpWidget>(button);
icon->resize(Size(st::menuIconLinks.width() - kSizeShift));
auto colorized = [&] {
auto f = QFile(Ui::Premium::Svg());
if (!f.open(QIODevice::ReadOnly)) {
return QString();
}
return QString::fromUtf8(
f.readAll()).replace(u"#fff"_q, u"#ffffff00"_q);
}();
colorized.replace(
u"stroke=\"none\""_q,
u"stroke=\"%1\""_q.arg(st::menuIconColor->c.name()));
colorized.replace(
u"stroke-width=\"1\""_q,
u"stroke-width=\"%1\""_q.arg(kStrokeWidth));
const auto svg = icon->lifetime().make_state<QSvgRenderer>(
colorized.toUtf8());
svg->setViewBox(svg->viewBox() + Margins(kStrokeWidth));
const auto starSize = Size(icon->height());
icon->paintRequest(
) | rpl::start_with_next([=] {
const auto image = Ui::Earn::MenuIconCredits();
icon->resize(image.size() / style::DevicePixelRatio());
icon->paintRequest() | rpl::start_with_next([=] {
auto p = QPainter(icon);
svg->render(&p, Rect(starSize));
p.drawImage(0, 0, image);
}, icon->lifetime());
button->sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
icon->moveToLeft(
button->st().iconLeft + kSizeShift / 2.,
button->st().iconLeft,
(size.height() - icon->height()) / 2);
}, icon->lifetime());
}

View File

@@ -12,12 +12,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h"
#include "boxes/gift_premium_box.h"
#include "boxes/peer_list_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/share_box.h"
#include "core/application.h"
#include "core/ui_integration.h" // Core::MarkedTextContext.
#include "data/components/credits.h"
#include "data/data_changes.h"
#include "data/data_channel.h"
#include "data/data_forum_topic.h"
#include "data/data_histories.h"
#include "data/data_peer.h"
#include "data/data_session.h"
@@ -51,6 +53,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "styles/style_boxes.h"
#include "styles/style_credits.h"
#include "styles/style_dialogs.h"
#include "styles/style_giveaway.h"
#include "styles/style_info.h"
#include "styles/style_layers.h" // st::boxDividerLabel.
@@ -264,8 +267,9 @@ private:
class SingleRowController final : public PeerListController {
public:
SingleRowController(
not_null<PeerData*> peer,
rpl::producer<QString> status);
not_null<Data::Thread*> thread,
rpl::producer<QString> status,
Fn<void()> clicked);
void prepare() override;
void loadMoreRows() override;
@@ -273,8 +277,10 @@ public:
Main::Session &session() const override;
private:
const not_null<PeerData*> _peer;
const not_null<Main::Session*> _session;
const base::weak_ptr<Data::Thread> _thread;
rpl::producer<QString> _status;
Fn<void()> _clicked;
rpl::lifetime _lifetime;
};
@@ -956,12 +962,11 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
Ui::AddSkip(content);
Ui::AddSkip(content);
const auto &stUser = st::boostReplaceUserpic;
const auto photoSize = st::boostReplaceUserpic.photoSize;
const auto session = &row->peer()->session();
content->add(object_ptr<Ui::CenterWrap<>>(
content,
object_ptr<Ui::UserpicButton>(content, channel, stUser))
)->setAttribute(Qt::WA_TransparentForMouseEvents);
Settings::SubscriptionUserpic(content, channel, photoSize)));
Ui::AddSkip(content);
Ui::AddSkip(content);
@@ -1145,36 +1150,59 @@ int Controller::descriptionTopSkipMin() const {
}
SingleRowController::SingleRowController(
not_null<PeerData*> peer,
rpl::producer<QString> status)
: _peer(peer)
, _status(std::move(status)) {
not_null<Data::Thread*> thread,
rpl::producer<QString> status,
Fn<void()> clicked)
: _session(&thread->session())
, _thread(thread)
, _status(std::move(status))
, _clicked(std::move(clicked)) {
}
void SingleRowController::prepare() {
auto row = std::make_unique<PeerListRow>(_peer);
const auto strong = _thread.get();
if (!strong) {
return;
}
const auto topic = strong->asTopic();
auto row = topic
? ChooseTopicBoxController::MakeRow(topic)
: std::make_unique<PeerListRow>(strong->peer());
const auto raw = row.get();
std::move(
_status
) | rpl::start_with_next([=](const QString &status) {
raw->setCustomStatus(status);
delegate()->peerListUpdateRow(raw);
}, _lifetime);
if (_status) {
std::move(
_status
) | rpl::start_with_next([=](const QString &status) {
raw->setCustomStatus(status);
delegate()->peerListUpdateRow(raw);
}, _lifetime);
}
delegate()->peerListAppendRow(std::move(row));
delegate()->peerListRefreshRows();
if (topic) {
topic->destroyed() | rpl::start_with_next([=] {
while (delegate()->peerListFullRowsCount()) {
delegate()->peerListRemoveRow(delegate()->peerListRowAt(0));
}
delegate()->peerListRefreshRows();
}, _lifetime);
}
}
void SingleRowController::loadMoreRows() {
}
void SingleRowController::rowClicked(not_null<PeerListRow*> row) {
ShowPeerInfoSync(row->peer());
if (const auto onstack = _clicked) {
onstack();
} else {
ShowPeerInfoSync(row->peer());
}
}
Main::Session &SingleRowController::session() const {
return _peer->session();
return *_session;
}
} // namespace
@@ -1187,14 +1215,29 @@ bool IsExpiredLink(const Api::InviteLink &data, TimeId now) {
void AddSinglePeerRow(
not_null<Ui::VerticalLayout*> container,
not_null<PeerData*> peer,
rpl::producer<QString> status) {
rpl::producer<QString> status,
Fn<void()> clicked) {
AddSinglePeerRow(
container,
peer->owner().history(peer),
std::move(status),
std::move(clicked));
}
void AddSinglePeerRow(
not_null<Ui::VerticalLayout*> container,
not_null<Data::Thread*> thread,
rpl::producer<QString> status,
Fn<void()> clicked) {
const auto delegate = container->lifetime().make_state<
PeerListContentDelegateSimple
>();
const auto controller = container->lifetime().make_state<
SingleRowController
>(peer, std::move(status));
controller->setStyleOverrides(&st::peerListSingleRow);
>(thread, std::move(status), std::move(clicked));
controller->setStyleOverrides(thread->asTopic()
? &st::chooseTopicList
: &st::peerListSingleRow);
const auto content = container->add(object_ptr<PeerListContent>(
container,
controller));

View File

@@ -16,6 +16,10 @@ namespace Api {
struct InviteLink;
} // namespace Api
namespace Data {
class Thread;
} // namespace Data
namespace Main {
class Session;
} // namespace Main
@@ -31,7 +35,14 @@ class BoxContent;
void AddSinglePeerRow(
not_null<Ui::VerticalLayout*> container,
not_null<PeerData*> peer,
rpl::producer<QString> status);
rpl::producer<QString> status,
Fn<void()> clicked = nullptr);
void AddSinglePeerRow(
not_null<Ui::VerticalLayout*> container,
not_null<Data::Thread*> thread,
rpl::producer<QString> status,
Fn<void()> clicked = nullptr);
void AddPermanentLinkBlock(
std::shared_ptr<Ui::Show> show,

View File

@@ -1129,11 +1129,14 @@ void ShowEditPeerPermissionsBox(
disabledByAdminRights,
tr::lng_rights_permission_cant_edit(tr::now));
if (const auto channel = peer->asChannel()) {
if (channel->isPublic()
|| (channel->isMegagroup() && channel->linkedChat())) {
if (channel->isPublic()) {
result.emplace(
Flag::ChangeInfo | Flag::PinMessages,
tr::lng_rights_permission_unavailable(tr::now));
} else if (channel->isMegagroup() && channel->linkedChat()) {
result.emplace(
Flag::ChangeInfo | Flag::PinMessages,
tr::lng_rights_permission_in_discuss(tr::now));
}
}
return result;

View File

@@ -7,27 +7,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/peers/edit_peer_requests_box.h"
#include "ui/effects/ripple_animation.h"
#include "api/api_invite_links.h"
#include "apiwrap.h"
#include "base/unixtime.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/peers/edit_participants_box.h" // SubscribeToMigration
#include "boxes/peers/edit_peer_invite_link.h" // PrepareRequestedRowStatus
#include "boxes/peers/prepare_short_info_box.h" // PrepareShortInfoBox
#include "history/view/history_view_requests_bar.h" // kRecentRequestsLimit
#include "data/data_peer.h"
#include "data/data_user.h"
#include "data/data_chat.h"
#include "boxes/peers/edit_peer_requests_box.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "base/unixtime.h"
#include "data/data_user.h"
#include "history/view/history_view_requests_bar.h" // kRecentRequestsLimit
#include "info/info_controller.h"
#include "info/info_memento.h"
#include "info/requests_list/info_requests_list_widget.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "mtproto/sender.h"
#include "ui/effects/ripple_animation.h"
#include "ui/painter.h"
#include "ui/round_rect.h"
#include "ui/text/text_utilities.h"
#include "ui/painter.h"
#include "lang/lang_keys.h"
#include "window/window_session_controller.h"
#include "apiwrap.h"
#include "api/api_invite_links.h"
#include "styles/style_boxes.h"
namespace {
@@ -262,14 +265,10 @@ RequestsBoxController::~RequestsBoxController() = default;
void RequestsBoxController::Start(
not_null<Window::SessionNavigation*> navigation,
not_null<PeerData*> peer) {
auto controller = std::make_unique<RequestsBoxController>(
navigation,
peer->migrateToOrMe());
const auto initBox = [=](not_null<PeerListBox*> box) {
box->addButton(tr::lng_close(), [=] { box->closeBox(); });
};
navigation->parentController()->show(
Box<PeerListBox>(std::move(controller), initBox));
navigation->showSection(
std::make_shared<Info::Memento>(
peer->migrateToOrMe(),
Info::Section::Type::RequestsList));
}
Main::Session &RequestsBoxController::session() const {
@@ -289,6 +288,58 @@ std::unique_ptr<PeerListRow> RequestsBoxController::createSearchRow(
return nullptr;
}
std::unique_ptr<PeerListRow> RequestsBoxController::createRestoredRow(
not_null<PeerData*> peer) {
if (const auto user = peer->asUser()) {
return createRow(user, _dates[user]);
}
return nullptr;
}
auto RequestsBoxController::saveState() const
-> std::unique_ptr<PeerListState> {
auto result = PeerListController::saveState();
auto my = std::make_unique<SavedState>();
my->dates = _dates;
my->offsetDate = _offsetDate;
my->offsetUser = _offsetUser;
my->allLoaded = _allLoaded;
my->wasLoading = (_loadRequestId != 0);
if (const auto search = searchController()) {
my->searchState = search->saveState();
}
result->controllerState = std::move(my);
return result;
}
void RequestsBoxController::restoreState(
std::unique_ptr<PeerListState> state) {
auto typeErasedState = state
? state->controllerState.get()
: nullptr;
if (const auto my = dynamic_cast<SavedState*>(typeErasedState)) {
if (const auto requestId = base::take(_loadRequestId)) {
_api.request(requestId).cancel();
}
_dates = std::move(my->dates);
_offsetDate = my->offsetDate;
_offsetUser = my->offsetUser;
_allLoaded = my->allLoaded;
if (const auto search = searchController()) {
search->restoreState(std::move(my->searchState));
}
if (my->wasLoading) {
loadMoreRows();
}
PeerListController::restoreState(std::move(state));
if (delegate()->peerListFullRowsCount() || _allLoaded) {
refreshDescription();
delegate()->peerListRefreshRows();
}
}
}
void RequestsBoxController::prepare() {
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
delegate()->peerListSetTitle(_peer->isBroadcast()
@@ -356,9 +407,7 @@ void RequestsBoxController::refreshDescription() {
}
void RequestsBoxController::rowClicked(not_null<PeerListRow*> row) {
_navigation->parentController()->show(PrepareShortInfoBox(
row->peer(),
_navigation));
_navigation->showPeerInfo(row->peer());
}
void RequestsBoxController::rowElementClicked(
@@ -405,6 +454,7 @@ void RequestsBoxController::appendRow(
not_null<UserData*> user,
TimeId date) {
if (!delegate()->peerListFindRow(user->id.value)) {
_dates.emplace(user, date);
if (auto row = createRow(user, date)) {
delegate()->peerListAppendRow(std::move(row));
setDescriptionText(QString());
@@ -503,6 +553,7 @@ std::unique_ptr<PeerListRow> RequestsBoxController::createRow(
const auto search = static_cast<RequestsBoxSearchController*>(
searchController());
date = search->dateForUser(user);
_dates.emplace(user, date);
}
return std::make_unique<Row>(_helper.get(), user, date);
}
@@ -574,6 +625,36 @@ TimeId RequestsBoxSearchController::dateForUser(not_null<UserData*> user) {
return {};
}
auto RequestsBoxSearchController::saveState() const
-> std::unique_ptr<SavedStateBase> {
auto result = std::make_unique<SavedState>();
result->query = _query;
result->offsetDate = _offsetDate;
result->offsetUser = _offsetUser;
result->allLoaded = _allLoaded;
result->wasLoading = (_requestId != 0);
return result;
}
void RequestsBoxSearchController::restoreState(
std::unique_ptr<SavedStateBase> state) {
if (auto my = dynamic_cast<SavedState*>(state.get())) {
if (auto requestId = base::take(_requestId)) {
_api.request(requestId).cancel();
}
_cache.clear();
_queries.clear();
_allLoaded = my->allLoaded;
_offsetDate = my->offsetDate;
_offsetUser = my->offsetUser;
_query = my->query;
if (my->wasLoading) {
searchOnServer();
}
}
}
bool RequestsBoxSearchController::searchInCache() {
const auto i = _cache.find(_query);
if (i != _cache.cend()) {

View File

@@ -35,15 +35,32 @@ public:
Main::Session &session() const override;
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
void rowElementClicked(not_null<PeerListRow*> row, int element) override;
void rowElementClicked(
not_null<PeerListRow*> row,
int element) override;
void loadMoreRows() override;
std::unique_ptr<PeerListRow> createSearchRow(
not_null<PeerData*> peer) override;
std::unique_ptr<PeerListRow> createRestoredRow(
not_null<PeerData*> peer) override;
std::unique_ptr<PeerListState> saveState() const override;
void restoreState(std::unique_ptr<PeerListState> state) override;
private:
class RowHelper;
struct SavedState : SavedStateBase {
using SearchStateBase = PeerListSearchController::SavedStateBase;
std::unique_ptr<SearchStateBase> searchState;
base::flat_map<not_null<UserData*>, TimeId> dates;
TimeId offsetDate = 0;
UserData *offsetUser = nullptr;
bool allLoaded = false;
bool wasLoading = false;
};
static std::unique_ptr<PeerListSearchController> CreateSearchController(
not_null<PeerData*> peer);
@@ -63,6 +80,8 @@ private:
not_null<PeerData*> _peer;
MTP::Sender _api;
base::flat_map<not_null<UserData*>, TimeId> _dates;
TimeId _offsetDate = 0;
UserData *_offsetUser = nullptr;
mtpRequestId _loadRequestId = 0;
@@ -82,7 +101,17 @@ public:
void removeFromCache(not_null<UserData*> user);
[[nodiscard]] TimeId dateForUser(not_null<UserData*> user);
std::unique_ptr<SavedStateBase> saveState() const override;
void restoreState(std::unique_ptr<SavedStateBase> state) override;
private:
struct SavedState : SavedStateBase {
QString query;
TimeId offsetDate = 0;
UserData *offsetUser = nullptr;
bool allLoaded = false;
bool wasLoading = false;
};
struct Item {
not_null<UserData*> user;
TimeId date = 0;

View File

@@ -534,15 +534,16 @@ void PeerShortInfoCover::handleStreamingUpdate(
v::match(update.data, [&](Information &update) {
streamingReady(std::move(update));
}, [&](const PreloadedVideo &update) {
}, [&](const UpdateVideo &update) {
}, [](PreloadedVideo) {
}, [&](UpdateVideo update) {
_videoPosition = update.position;
_widget->update();
}, [&](const PreloadedAudio &update) {
}, [&](const UpdateAudio &update) {
}, [&](const WaitingForData &update) {
}, [&](MutedByOther) {
}, [&](Finished) {
}, [](PreloadedAudio) {
}, [](UpdateAudio) {
}, [](WaitingForData) {
}, [](SpeedEstimate) {
}, [](MutedByOther) {
}, [](Finished) {
});
}
@@ -774,6 +775,10 @@ void PeerShortInfoBox::prepareRows() {
result->setContextCopyText(contextCopyText);
return result;
};
addInfoOneLine(
tr::lng_settings_channel_label(),
channelValue(),
tr::lng_context_copy_link(tr::now));
addInfoOneLine(
tr::lng_info_link_label(),
linkValue(),
@@ -835,6 +840,13 @@ rpl::producer<QString> PeerShortInfoBox::nameValue() const {
}) | rpl::distinct_until_changed();
}
rpl::producer<TextWithEntities> PeerShortInfoBox::channelValue() const {
return _fields.value(
) | rpl::map([](const PeerShortInfoFields &fields) {
return Ui::Text::Link(fields.channelName, fields.channelLink);
}) | rpl::distinct_until_changed();
}
rpl::producer<TextWithEntities> PeerShortInfoBox::linkValue() const {
return _fields.value(
) | rpl::map([](const PeerShortInfoFields &fields) {

View File

@@ -37,6 +37,8 @@ enum class PeerShortInfoType {
struct PeerShortInfoFields {
QString name;
QString channelName;
QString channelLink;
QString phone;
QString link;
TextWithEntities about;
@@ -169,6 +171,7 @@ private:
int fillRoundedTopHeight();
[[nodiscard]] rpl::producer<QString> nameValue() const;
[[nodiscard]] rpl::producer<TextWithEntities> channelValue() const;
[[nodiscard]] rpl::producer<TextWithEntities> linkValue() const;
[[nodiscard]] rpl::producer<QString> phoneValue() const;
[[nodiscard]] rpl::producer<QString> usernameValue() const;

View File

@@ -202,6 +202,7 @@ void ProcessFullPhoto(
return peer->session().changes().peerFlagsValue(
peer,
(UpdateFlag::Name
| UpdateFlag::PersonalChannel
| UpdateFlag::PhoneNumber
| UpdateFlag::Username
| UpdateFlag::About
@@ -209,8 +210,20 @@ void ProcessFullPhoto(
) | rpl::map([=] {
const auto user = peer->asUser();
const auto username = peer->username();
const auto channelId = user ? user->personalChannelId() : 0;
const auto channel = channelId
? user->owner().channel(channelId).get()
: nullptr;
const auto channelUsername = channel
? channel->username()
: QString();
const auto hasChannel = !channelUsername.isEmpty();
return PeerShortInfoFields{
.name = peer->name(),
.channelName = hasChannel ? channel->name() : QString(),
.channelLink = (hasChannel
? channel->session().createInternalLinkFull(channelUsername)
: QString()),
.phone = user ? Ui::FormatPhone(user->phone()) : QString(),
.link = ((user || username.isEmpty())
? QString()

View File

@@ -77,7 +77,7 @@ bool operator==(const Descriptor &a, const Descriptor &b) {
struct Preload {
Descriptor descriptor;
std::shared_ptr<Data::DocumentMedia> media;
std::weak_ptr<ChatHelpers::Show> show;
std::weak_ptr<Main::SessionShow> show;
};
[[nodiscard]] std::vector<Preload> &Preloads() {

View File

@@ -8,27 +8,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/report_messages_box.h"
#include "api/api_report.h"
#include "core/application.h"
#include "data/data_peer.h"
#include "data/data_photo.h"
#include "lang/lang_keys.h"
#include "ui/boxes/report_box.h"
#include "ui/boxes/report_box_graphics.h"
#include "ui/layers/generic_box.h"
#include "ui/rect.h"
#include "ui/vertical_list.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/fields/input_field.h"
#include "window/window_controller.h"
#include "window/window_session_controller.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_layers.h"
#include "styles/style_settings.h"
namespace {
[[nodiscard]] object_ptr<Ui::BoxContent> Report(
[[nodiscard]] object_ptr<Ui::BoxContent> ReportPhoto(
not_null<PeerData*> peer,
std::variant<
v::null_t,
MessageIdsList,
not_null<PhotoData*>,
StoryId> data,
not_null<PhotoData*> photo,
const style::ReportBox *stOverride) {
const auto source = v::match(data, [](const MessageIdsList &ids) {
return Ui::ReportSource::Message;
}, [&](not_null<PhotoData*> photo) {
const auto source = [&] {
return peer->isUser()
? (photo->hasVideo()
? Ui::ReportSource::ProfileVideo
@@ -40,19 +43,14 @@ namespace {
: (photo->hasVideo()
? Ui::ReportSource::ChannelVideo
: Ui::ReportSource::ChannelPhoto);
}, [&](StoryId id) {
return Ui::ReportSource::Story;
}, [](v::null_t) {
Unexpected("Bad source report.");
return Ui::ReportSource::Bot;
});
}();
const auto st = stOverride ? stOverride : &st::defaultReportBox;
return Box([=](not_null<Ui::GenericBox*> box) {
const auto show = box->uiShow();
Ui::ReportReasonBox(box, *st, source, [=](Ui::ReportReason reason) {
show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
Ui::ReportDetailsBox(box, *st, [=](const QString &text) {
Api::SendReport(show, peer, reason, text, data);
Api::SendPhotoReport(show, peer, reason, text, photo);
show->hideLayer();
});
}));
@@ -62,64 +60,151 @@ namespace {
} // namespace
object_ptr<Ui::BoxContent> ReportItemsBox(
not_null<PeerData*> peer,
MessageIdsList ids) {
return Report(peer, ids, nullptr);
}
object_ptr<Ui::BoxContent> ReportProfilePhotoBox(
not_null<PeerData*> peer,
not_null<PhotoData*> photo) {
return Report(peer, photo, nullptr);
return ReportPhoto(peer, photo, nullptr);
}
void ShowReportPeerBox(
not_null<Window::SessionController*> window,
not_null<PeerData*> peer) {
struct State {
QPointer<Ui::BoxContent> reasonBox;
QPointer<Ui::BoxContent> detailsBox;
MessageIdsList ids;
};
const auto state = std::make_shared<State>();
const auto chosen = [=](Ui::ReportReason reason) {
const auto send = [=](const QString &text) {
window->clearChooseReportMessages();
Api::SendReport(
window->uiShow(),
peer,
reason,
text,
std::move(state->ids));
if (const auto strong = state->reasonBox.data()) {
strong->closeBox();
void ShowReportMessageBox(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
const std::vector<MsgId> &ids,
const std::vector<StoryId> &stories,
const style::ReportBox *stOverride) {
const auto report = Api::CreateReportMessagesOrStoriesCallback(
show,
peer);
auto performRequest = [=](
const auto &repeatRequest,
Data::ReportInput reportInput) -> void {
report(reportInput, [=](const Api::ReportResult &result) {
if (!result.error.isEmpty()) {
if (result.error == u"MESSAGE_ID_REQUIRED"_q) {
const auto widget = show->toastParent();
const auto window = Core::App().findWindow(widget);
const auto controller = window
? window->sessionController()
: nullptr;
if (controller) {
const auto callback = [=](std::vector<MsgId> ids) {
auto copy = reportInput;
copy.ids = std::move(ids);
repeatRequest(repeatRequest, std::move(copy));
};
controller->showChooseReportMessages(
peer,
reportInput,
std::move(callback));
}
} else {
show->showToast(result.error);
}
return;
}
if (const auto strong = state->detailsBox.data()) {
strong->closeBox();
if (!result.options.empty() || result.commentOption) {
show->show(Box([=](not_null<Ui::GenericBox*> box) {
box->setTitle(
rpl::single(
result.title.isEmpty()
? reportInput.optionText
: result.title));
for (const auto &option : result.options) {
const auto button = Ui::AddReportOptionButton(
box->verticalLayout(),
option.text,
stOverride);
button->setClickedCallback([=] {
auto copy = reportInput;
copy.optionId = option.id;
copy.optionText = option.text;
repeatRequest(repeatRequest, std::move(copy));
});
}
if (const auto commentOption = result.commentOption) {
constexpr auto kReportReasonLengthMax = 512;
const auto &st = stOverride
? stOverride
: &st::defaultReportBox;
Ui::AddReportDetailsIconButton(box);
Ui::AddSkip(box->verticalLayout());
Ui::AddSkip(box->verticalLayout());
const auto details = box->addRow(
object_ptr<Ui::InputField>(
box,
st->field,
Ui::InputField::Mode::MultiLine,
commentOption->optional
? tr::lng_report_details_optional()
: tr::lng_report_details_non_optional(),
QString()));
Ui::AddSkip(box->verticalLayout());
Ui::AddSkip(box->verticalLayout());
{
const auto container = box->verticalLayout();
auto label = object_ptr<Ui::FlatLabel>(
container,
tr::lng_report_details_message_about(),
st::boxDividerLabel);
label->setTextColorOverride(st->dividerFg->c);
using namespace Ui;
const auto widget = container->add(
object_ptr<PaddingWrap<>>(
container,
std::move(label),
st::defaultBoxDividerLabelPadding));
const auto background
= CreateChild<BoxContentDivider>(
widget,
st::boxDividerHeight,
st->dividerBg,
RectPart::Top | RectPart::Bottom);
background->lower();
widget->sizeValue(
) | rpl::start_with_next([=](const QSize &s) {
background->resize(s);
}, background->lifetime());
}
details->setMaxLength(kReportReasonLengthMax);
box->setFocusCallback([=] {
details->setFocusFast();
});
const auto submit = [=] {
if (!commentOption->optional
&& details->empty()) {
details->showError();
details->setFocus();
return;
}
auto copy = reportInput;
copy.optionId = commentOption->id;
copy.comment = details->getLastText();
repeatRequest(repeatRequest, std::move(copy));
};
details->submits(
) | rpl::start_with_next(submit, details->lifetime());
box->addButton(tr::lng_report_button(), submit);
} else {
box->addButton(
tr::lng_close(),
[=] { show->hideLayer(); });
}
if (!reportInput.optionId.isNull()) {
box->addLeftButton(
tr::lng_create_group_back(),
[=] { box->closeBox(); });
}
}));
} else if (result.successful) {
constexpr auto kToastDuration = crl::time(4000);
show->showToast(
tr::lng_report_thanks(tr::now),
kToastDuration);
show->hideLayer();
}
};
if (reason == Ui::ReportReason::Fake
|| reason == Ui::ReportReason::Other) {
state->ids = {};
state->detailsBox = window->show(
Box(Ui::ReportDetailsBox, st::defaultReportBox, send));
return;
}
window->showChooseReportMessages(peer, reason, [=](
MessageIdsList ids) {
state->ids = std::move(ids);
state->detailsBox = window->show(
Box(Ui::ReportDetailsBox, st::defaultReportBox, send));
});
};
state->reasonBox = window->show(Box(
Ui::ReportReasonBox,
st::defaultReportBox,
(peer->isBroadcast()
? Ui::ReportSource::Channel
: peer->isUser()
? Ui::ReportSource::Bot
: Ui::ReportSource::Group),
chosen));
performRequest(performRequest, { .ids = ids, .stories = stories });
}

View File

@@ -12,20 +12,22 @@ class object_ptr;
namespace Ui {
class BoxContent;
class Show;
} // namespace Ui
namespace Window {
class SessionController;
} // namespace Main
namespace style {
struct ReportBox;
} // namespace style
class PeerData;
[[nodiscard]] object_ptr<Ui::BoxContent> ReportItemsBox(
not_null<PeerData*> peer,
MessageIdsList ids);
[[nodiscard]] object_ptr<Ui::BoxContent> ReportProfilePhotoBox(
not_null<PeerData*> peer,
not_null<PhotoData*> photo);
void ShowReportPeerBox(
not_null<Window::SessionController*> window,
not_null<PeerData*> peer);
void ShowReportMessageBox(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
const std::vector<MsgId> &ids,
const std::vector<StoryId> &stories,
const style::ReportBox *stOverride = nullptr);

View File

@@ -272,10 +272,13 @@ void SendCreditsBox(
state->confirmButtonBusy = true;
session->api().request(
MTPpayments_SendStarsForm(
MTP_flags(0),
MTP_long(form->formId),
form->inputInvoice)
).done([=](auto result) {
).done([=](const MTPpayments_PaymentResult &result) {
result.match([&](const MTPDpayments_paymentResult &data) {
session->api().applyUpdates(data.vupdates());
}, [](const MTPDpayments_paymentVerificationNeeded &data) {
});
if (weak) {
state->confirmButtonBusy = false;
box->closeBox();
@@ -311,41 +314,22 @@ void SendCreditsBox(
AddChildToWidgetCenter(button.data(), loadingAnimation);
loadingAnimation->showOn(state->confirmButtonBusy.value());
}
{
auto buttonText = tr::lng_credits_box_out_confirm(
lt_count,
rpl::single(form->invoice.amount) | tr::to_count(),
lt_emoji,
rpl::single(CreditsEmojiSmall(session)),
Ui::Text::RichLangValue);
const auto buttonLabel = Ui::CreateChild<Ui::FlatLabel>(
button,
rpl::single(QString()),
st::creditsBoxButtonLabel);
std::move(
buttonText
) | rpl::start_with_next([=](const TextWithEntities &text) {
buttonLabel->setMarkedText(
text,
Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = [=] { buttonLabel->update(); },
});
}, buttonLabel->lifetime());
buttonLabel->setTextColorOverride(
box->getDelegate()->style().button.textFg->c);
button->sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
buttonLabel->moveToLeft(
(size.width() - buttonLabel->width()) / 2,
(size.height() - buttonLabel->height()) / 2);
}, buttonLabel->lifetime());
buttonLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
state->confirmButtonBusy.value(
) | rpl::start_with_next([=](bool busy) {
buttonLabel->setVisible(!busy);
}, buttonLabel->lifetime());
}
SetButtonMarkedLabel(
button,
rpl::combine(
tr::lng_credits_box_out_confirm(
lt_count,
rpl::single(form->invoice.amount) | tr::to_count(),
lt_emoji,
rpl::single(CreditsEmojiSmall(session)),
Ui::Text::RichLangValue),
state->confirmButtonBusy.value()
) | rpl::map([](TextWithEntities &&text, bool busy) {
return busy ? TextWithEntities() : std::move(text);
}),
session,
st::creditsBoxButtonLabel,
box->getDelegate()->style().button.textFg->c);
const auto buttonWidth = st::boxWidth
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
@@ -405,4 +389,73 @@ TextWithEntities CreditsEmojiSmall(not_null<Main::Session*> session) {
QString(QChar(0x2B50)));
}
not_null<FlatLabel*> SetButtonMarkedLabel(
not_null<RpWidget*> button,
rpl::producer<TextWithEntities> text,
Fn<std::any(Fn<void()> update)> context,
const style::FlatLabel &st,
std::optional<QColor> textFg) {
const auto buttonLabel = Ui::CreateChild<Ui::FlatLabel>(
button,
rpl::single(QString()),
st);
rpl::duplicate(
text
) | rpl::filter([=](const TextWithEntities &text) {
return !text.text.isEmpty();
}) | rpl::start_with_next([=](const TextWithEntities &text) {
buttonLabel->setMarkedText(
text,
context([=] { buttonLabel->update(); }));
}, buttonLabel->lifetime());
if (textFg) {
buttonLabel->setTextColorOverride(textFg);
}
button->sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
buttonLabel->moveToLeft(
(size.width() - buttonLabel->width()) / 2,
(size.height() - buttonLabel->height()) / 2);
}, buttonLabel->lifetime());
buttonLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
buttonLabel->showOn(std::move(
text
) | rpl::map([=](const TextWithEntities &text) {
return !text.text.isEmpty();
}));
return buttonLabel;
}
not_null<FlatLabel*> SetButtonMarkedLabel(
not_null<RpWidget*> button,
rpl::producer<TextWithEntities> text,
not_null<Main::Session*> session,
const style::FlatLabel &st,
std::optional<QColor> textFg) {
return SetButtonMarkedLabel(button, text, [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = update,
};
}, st, textFg);
}
void SendStarGift(
not_null<Main::Session*> session,
std::shared_ptr<Payments::CreditsFormData> data,
Fn<void(std::optional<QString>)> done) {
session->api().request(MTPpayments_SendStarsForm(
MTP_long(data->formId),
data->inputInvoice
)).done([=](const MTPpayments_PaymentResult &result) {
result.match([&](const MTPDpayments_paymentResult &data) {
session->api().applyUpdates(data.vupdates());
}, [](const MTPDpayments_paymentVerificationNeeded &data) {
});
done(std::nullopt);
}).fail([=](const MTP::Error &error) {
done(error.type());
}).send();
}
} // namespace Ui

View File

@@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class HistoryItem;
namespace style {
struct FlatLabel;
} // namespace style
namespace Main {
class Session;
} // namespace Main
@@ -19,7 +23,9 @@ struct CreditsFormData;
namespace Ui {
class RpWidget;
class GenericBox;
class FlatLabel;
void SendCreditsBox(
not_null<Ui::GenericBox*> box,
@@ -32,4 +38,23 @@ void SendCreditsBox(
[[nodiscard]] TextWithEntities CreditsEmojiSmall(
not_null<Main::Session*> session);
not_null<FlatLabel*> SetButtonMarkedLabel(
not_null<RpWidget*> button,
rpl::producer<TextWithEntities> text,
Fn<std::any(Fn<void()> update)> context,
const style::FlatLabel &st,
std::optional<QColor> textFg = {});
not_null<FlatLabel*> SetButtonMarkedLabel(
not_null<RpWidget*> button,
rpl::producer<TextWithEntities> text,
not_null<Main::Session*> session,
const style::FlatLabel &st,
std::optional<QColor> textFg = {});
void SendStarGift(
not_null<Main::Session*> session,
std::shared_ptr<Payments::CreditsFormData> data,
Fn<void(std::optional<QString>)> done);
} // namespace Ui

View File

@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/message_field.h"
#include "menu/menu_send.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/field_autocomplete.h"
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "editor/photo_editor_layer_widget.h"
@@ -1266,13 +1267,17 @@ void SendFilesBox::setupCaption() {
: (_limits & SendFilesAllow::EmojiWithoutPremium);
};
const auto show = _show;
InitMessageFieldHandlers(
&show->session(),
show,
_caption.data(),
[=] { return show->paused(Window::GifPauseReason::Layer); },
allow,
&_st.files.caption);
InitMessageFieldHandlers({
.session = &show->session(),
.show = show,
.field = _caption.data(),
.customEmojiPaused = [=] {
return show->paused(Window::GifPauseReason::Layer);
},
.allowPremiumEmoji = allow,
.fieldStyle = &_st.files.caption,
});
setupCaptionAutocomplete();
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_caption,
@@ -1332,6 +1337,59 @@ void SendFilesBox::setupCaption() {
}, _caption->lifetime());
}
void SendFilesBox::setupCaptionAutocomplete() {
if (!_captionToPeer || !_caption) {
return;
}
const auto parent = getDelegate()->outerContainer();
ChatHelpers::InitFieldAutocomplete(_autocomplete, {
.parent = parent,
.show = _show,
.field = _caption.data(),
.peer = _captionToPeer,
.features = [=] {
auto result = ChatHelpers::ComposeFeatures();
result.autocompleteCommands = false;
result.suggestStickersByEmoji = false;
return result;
},
.sendMenuDetails = _sendMenuDetails,
});
const auto raw = _autocomplete.get();
const auto scheduled = std::make_shared<bool>();
const auto recountPostponed = [=] {
if (*scheduled) {
return;
}
*scheduled = true;
Ui::PostponeCall(raw, [=] {
*scheduled = false;
auto field = Ui::MapFrom(parent, this, _caption->geometry());
_autocomplete->setBoundings(QRect(
field.x() - _caption->x(),
st::defaultBox.margin.top(),
width(),
(field.y()
+ _st.files.caption.textMargins.top()
+ _st.files.caption.placeholderShift
+ _st.files.caption.placeholderFont->height
- st::defaultBox.margin.top())));
});
};
for (auto w = (QWidget*)_caption.data(); w; w = w->parentWidget()) {
base::install_event_filter(raw, w, [=](not_null<QEvent*> e) {
if (e->type() == QEvent::Move || e->type() == QEvent::Resize) {
recountPostponed();
}
return base::EventFilterResult::Continue;
});
if (w == parent) {
break;
}
}
}
void SendFilesBox::checkCharsLimitation() {
const auto limits = Data::PremiumLimits(&_show->session());
const auto caption = (_caption && !_caption->isHidden())
@@ -1647,6 +1705,14 @@ void SendFilesBox::updateControlsGeometry() {
_scroll->move(0, _titleHeight.current());
}
void SendFilesBox::showFinished() {
if (const auto raw = _autocomplete.get()) {
InvokeQueued(raw, [=] {
raw->raise();
});
}
}
void SendFilesBox::setInnerFocus() {
if (_caption && !_caption->isHidden()) {
_caption->setFocusFast();

View File

@@ -28,6 +28,7 @@ enum class SendType;
namespace ChatHelpers {
class TabbedPanel;
class Show;
class FieldAutocomplete;
} // namespace ChatHelpers
namespace Ui {
@@ -126,6 +127,8 @@ public:
_cancelledCallback = std::move(callback);
}
void showFinished() override;
~SendFilesBox();
protected:
@@ -206,6 +209,7 @@ private:
void refreshControls(bool initial = false);
void setupSendWayControls();
void setupCaption();
void setupCaptionAutocomplete();
void setupEmojiPanel();
void updateSendWayControls();
@@ -257,6 +261,7 @@ private:
SendFilesLimits _limits = {};
Fn<MenuDetails()> _sendMenuDetails;
Fn<void(MenuAction, MenuDetails)> _sendMenuCallback;
PeerData *_captionToPeer = nullptr;
SendFilesCheck _check;
SendFilesConfirmed _confirmedCallback;
@@ -268,6 +273,7 @@ private:
bool _invertCaption = false;
object_ptr<Ui::InputField> _caption = { nullptr };
std::unique_ptr<ChatHelpers::FieldAutocomplete> _autocomplete;
TextWithTags _prefilledCaptionText;
object_ptr<Ui::EmojiButton> _emojiToggle = { nullptr };
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;

View File

@@ -7,7 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/send_gif_with_caption_box.h"
#include "base/event_filter.h"
#include "boxes/premium_preview_box.h"
#include "chat_helpers/field_autocomplete.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
@@ -30,9 +32,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/controls/emoji_button.h"
#include "ui/controls/emoji_button_factory.h"
#include "ui/layers/generic_box.h"
#include "ui/rect.h"
#include "ui/vertical_list.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/rect.h"
#include "ui/ui_utility.h"
#include "ui/vertical_list.h"
#include "window/window_controller.h"
#include "window/window_session_controller.h"
#include "styles/style_boxes.h"
@@ -226,6 +229,7 @@ namespace {
void SendGifWithCaptionBox(
not_null<Ui::GenericBox*> box,
not_null<DocumentData*> document,
not_null<PeerData*> peer,
const SendMenu::Details &details,
Fn<void(Api::SendOptions, TextWithTags)> done) {
const auto window = Core::App().findWindow(box);
@@ -255,6 +259,61 @@ void SendGifWithCaptionBox(
return true;
});
const auto sendMenuDetails = [=] { return details; };
struct Autocomplete {
std::unique_ptr<ChatHelpers::FieldAutocomplete> dropdown;
bool geometryUpdateScheduled = false;
};
const auto autocomplete = box->lifetime().make_state<Autocomplete>();
const auto outer = box->getDelegate()->outerContainer();
ChatHelpers::InitFieldAutocomplete(autocomplete->dropdown, {
.parent = outer,
.show = controller->uiShow(),
.field = input,
.peer = peer,
.features = [=] {
auto result = ChatHelpers::ComposeFeatures();
result.autocompleteCommands = false;
result.suggestStickersByEmoji = false;
return result;
},
.sendMenuDetails = sendMenuDetails,
});
const auto raw = autocomplete->dropdown.get();
const auto recountPostponed = [=] {
if (autocomplete->geometryUpdateScheduled) {
return;
}
autocomplete->geometryUpdateScheduled = true;
Ui::PostponeCall(raw, [=] {
autocomplete->geometryUpdateScheduled = false;
const auto from = input->parentWidget();
auto field = Ui::MapFrom(outer, from, input->geometry());
const auto &st = st::defaultComposeFiles;
autocomplete->dropdown->setBoundings(QRect(
field.x() - input->x(),
st::defaultBox.margin.top(),
input->width(),
(field.y()
+ st.caption.textMargins.top()
+ st.caption.placeholderShift
+ st.caption.placeholderFont->height
- st::defaultBox.margin.top())));
});
};
for (auto w = (QWidget*)input; w; w = w->parentWidget()) {
base::install_event_filter(raw, w, [=](not_null<QEvent*> e) {
if (e->type() == QEvent::Move || e->type() == QEvent::Resize) {
recountPostponed();
}
return base::EventFilterResult::Continue;
});
if (w == outer) {
break;
}
}
const auto send = [=](Api::SendOptions options) {
done(std::move(options), input->getTextWithTags());
};
@@ -264,8 +323,15 @@ void SendGifWithCaptionBox(
SendMenu::SetupMenuAndShortcuts(
confirm,
controller->uiShow(),
[=] { return details; },
sendMenuDetails,
SendMenu::DefaultCallback(controller->uiShow(), send));
box->setShowFinishedCallback([=] {
if (const auto raw = autocomplete->dropdown.get()) {
InvokeQueued(raw, [=] {
raw->raise();
});
}
});
box->addButton(tr::lng_cancel(), [=] {
box->closeBox();
});

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class PeerData;
class DocumentData;
namespace Api {
@@ -24,6 +25,7 @@ class GenericBox;
void SendGifWithCaptionBox(
not_null<Ui::GenericBox*> box,
not_null<DocumentData*> document,
not_null<PeerData*> peer,
const SendMenu::Details &details,
Fn<void(Api::SendOptions, TextWithTags)> done);

View File

@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_account.h"
#include "ui/boxes/confirm_box.h"
#include "apiwrap.h"
#include "ui/widgets/chat_filters_tabs_strip.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/multi_select.h"
#include "ui/widgets/scroll_area.h"
@@ -39,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/share_message_phrase_factory.h"
#include "data/business/data_shortcut_messages.h"
#include "data/data_channel.h"
#include "data/data_chat_filters.h"
#include "data/data_game.h"
#include "data/data_histories.h"
#include "data/data_user.h"
@@ -81,11 +83,14 @@ public:
void activateSkipColumn(int direction);
void activateSkipPage(int pageHeight, int direction);
void updateFilter(QString filter = QString());
[[nodiscard]] bool isFilterEmpty() const;
void selectActive();
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
rpl::producer<> searchRequests() const;
void applyChatFilter(FilterId id);
protected:
void visibleTopBottomUpdated(
int visibleTop,
@@ -166,7 +171,9 @@ private:
int _upon = -1;
int _visibleTop = 0;
std::unique_ptr<Dialogs::IndexedList> _chatsIndexed;
std::unique_ptr<Dialogs::IndexedList> _defaultChatsIndexed;
std::unique_ptr<Dialogs::IndexedList> _customChatsIndexed;
not_null<Dialogs::IndexedList*> _chatsIndexed;
QString _filter;
std::vector<not_null<Dialogs::Row*>> _filtered;
@@ -240,13 +247,12 @@ void ShareBox::prepareCommentField() {
}, field->lifetime());
if (const auto show = uiShow(); show->valid()) {
InitMessageFieldHandlers(
_descriptor.session,
Main::MakeSessionShow(show, _descriptor.session),
field,
nullptr,
nullptr,
_descriptor.stLabel);
InitMessageFieldHandlers({
.session = _descriptor.session,
.show = Main::MakeSessionShow(show, _descriptor.session),
.field = field,
.fieldStyle = _descriptor.stLabel,
});
}
field->setSubmitSettings(Core::App().settings().sendSubmitWay());
@@ -283,6 +289,10 @@ void ShareBox::prepare() {
_select->setQueryChangedCallback([=](const QString &query) {
applyFilterUpdate(query);
if (_chatsFilters) {
updateScrollSkips();
scrollToY(0);
}
});
_select->setItemRemovedCallback([=](uint64 itemId) {
if (const auto peer = _descriptor.session->data().peerLoaded(PeerId(itemId))) {
@@ -338,10 +348,32 @@ void ShareBox::prepare() {
{ .suggestCustomEmoji = true });
_select->raise();
{
const auto chatsFilters = AddChatFiltersTabsStrip(
this,
_descriptor.session,
[this](FilterId id) {
_inner->applyChatFilter(id);
scrollToY(0);
});
chatsFilters->lower();
chatsFilters->heightValue() | rpl::start_with_next([this](int h) {
updateScrollSkips();
scrollToY(0);
}, lifetime());
_select->heightValue() | rpl::start_with_next([=](int h) {
chatsFilters->moveToLeft(0, h);
}, chatsFilters->lifetime());
_chatsFilters = chatsFilters;
}
}
int ShareBox::getTopScrollSkip() const {
return _select->isHidden() ? 0 : _select->height();
return (_select->isHidden() ? 0 : _select->height())
+ ((_chatsFilters && _inner && _inner->isFilterEmpty())
? _chatsFilters->height()
: 0);
}
int ShareBox::getBottomScrollSkip() const {
@@ -672,9 +704,10 @@ ShareBox::Inner::Inner(
, _descriptor(descriptor)
, _show(std::move(show))
, _st(_descriptor.st ? *_descriptor.st : st::shareBoxList)
, _chatsIndexed(
, _defaultChatsIndexed(
std::make_unique<Dialogs::IndexedList>(
Dialogs::SortMode::Add)) {
Dialogs::SortMode::Add))
, _chatsIndexed(_defaultChatsIndexed.get()) {
_rowsTop = st::shareRowsTop;
_rowHeight = st::shareRowHeight;
setAttribute(Qt::WA_OpaquePaintEvent);
@@ -692,7 +725,7 @@ ShareBox::Inner::Inner(
const auto self = _descriptor.session->user();
const auto selfHistory = self->owner().history(self);
if (_descriptor.filterCallback(selfHistory)) {
_chatsIndexed->addToEnd(selfHistory);
_defaultChatsIndexed->addToEnd(selfHistory);
}
const auto addList = [&](not_null<Dialogs::IndexedList*> list) {
for (const auto &row : list->all()) {
@@ -700,7 +733,7 @@ ShareBox::Inner::Inner(
if (!history->peer->isSelf()
&& (history->asForum()
|| _descriptor.filterCallback(history))) {
_chatsIndexed->addToEnd(history);
_defaultChatsIndexed->addToEnd(history);
}
}
}
@@ -723,7 +756,7 @@ ShareBox::Inner::Inner(
_descriptor.session->changes().realtimeNameUpdates(
) | rpl::start_with_next([=](const Data::NameUpdate &update) {
_chatsIndexed->peerNameChanged(
_defaultChatsIndexed->peerNameChanged(
update.peer,
update.oldFirstLetters);
}, lifetime());
@@ -838,6 +871,8 @@ void ShareBox::Inner::updateChatName(not_null<Chat*> chat) {
? tr::lng_saved_messages(tr::now)
: peer->isRepliesChat()
? tr::lng_replies_messages(tr::now)
: peer->isVerifyCodes()
? tr::lng_verification_codes(tr::now)
: peer->name();
chat->name.setText(_st.item.nameStyle, text, Ui::NameTextOptions());
}
@@ -1330,6 +1365,10 @@ void ShareBox::Inner::updateFilter(QString filter) {
}
}
bool ShareBox::Inner::isFilterEmpty() const {
return _filter.isEmpty();
}
rpl::producer<Ui::ScrollToRequest> ShareBox::Inner::scrollToRequests() const {
return _scrollToRequests.events();
}
@@ -1338,6 +1377,30 @@ rpl::producer<> ShareBox::Inner::searchRequests() const {
return _searchRequests.events();
}
void ShareBox::Inner::applyChatFilter(FilterId id) {
if (!id) {
_chatsIndexed = _defaultChatsIndexed.get();
} else {
_customChatsIndexed = std::make_unique<Dialogs::IndexedList>(
Dialogs::SortMode::Add);
_chatsIndexed = _customChatsIndexed.get();
const auto addList = [&](not_null<Dialogs::IndexedList*> list) {
for (const auto &row : list->all()) {
if (const auto history = row->history()) {
if (history->asForum()
|| _descriptor.filterCallback(history)) {
_customChatsIndexed->addToEnd(history);
}
}
}
};
const auto &data = _descriptor.session->data();
addList(data.chatsFilters().chatsList(id)->indexed());
}
update();
}
void ShareBox::Inner::peopleReceived(
const QString &query,
const QVector<MTPPeer> &my,

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