Compare commits

...

510 Commits

Author SHA1 Message Date
John Preston
b0981ea8e3 Beta version 5.2.6.
- Fix launching on X11. (Linux)
2024-07-29 11:02:25 +03:00
Ilya Fedin
a141d01a23 Fix macOS packaged action 2024-07-27 22:59:48 +02:00
John Preston
6a000207ee Beta version 5.2.5.
- Fix media viewer context menu.
- Fix blockquotes layout in messages.
2024-07-27 07:48:35 +02:00
John Preston
ee6edf9caa Force venue icon format. 2024-07-27 07:45:59 +02:00
John Preston
37907636e6 Fix build with Xcode. 2024-07-27 07:44:51 +02:00
John Preston
3f0f3a3c11 Focus existing location picker. 2024-07-26 17:28:04 +02:00
John Preston
db0856f71c Support t.me/username?profile links. 2024-07-26 17:28:03 +02:00
John Preston
86cdda2277 Fix text with blockquotes geometry counting. 2024-07-26 17:28:03 +02:00
John Preston
3888e8084a Update tg_owt, lib_webrtc and patches. 2024-07-26 13:02:38 +02:00
Ilya Fedin
517b456670 Add hit test for window context menu 2024-07-26 10:19:55 +02:00
John Preston
77d6e19214 Beta version 5.2.4.
- Allow opening several web apps.
- Send location marks and venues.
2024-07-25 10:33:57 +03:00
John Preston
fb64452495 Fix build on Linux. 2024-07-25 10:33:57 +03:00
John Preston
24fabf2590 Fix build on Windows. 2024-07-24 18:43:18 +02:00
John Preston
9b2847a11d Update submodules. 2024-07-24 18:15:28 +02:00
Ilya Fedin
c18e8fd777 Enable exceptions for C on Linux 2024-07-24 18:12:30 +02:00
Ilya Fedin
f4afa762d8 Replace -fstack-protector-all with -fstack-protector-strong to avoid slowdown of functions not using stack 2024-07-24 18:12:30 +02:00
Ilya Fedin
5e1fb6ebbf Add -fasynchronous-unwind-tables and -mno-omit-leaf-frame-pointer for better debugging on Linux 2024-07-24 18:12:30 +02:00
Ilya Fedin
2c7922ce7b Get release CFLAGS from Dockerfile on Linux 2024-07-24 18:12:30 +02:00
Ilya Fedin
ac78ae823c Reduce portal autostart dialog modality to parent window 2024-07-24 18:10:56 +02:00
John Preston
1ef6f462f6 Preserve link preview settings on reschedule. 2024-07-24 18:06:08 +02:00
John Preston
c81f406759 Use xz repository from GitHub.
Fixes #28189.
2024-07-24 18:06:07 +02:00
John Preston
889ec0c731 Replace \r\n with \n on paste.
Fixes #28181.
2024-07-24 18:25:33 +03:00
John Preston
677fbdd84e Fix webrtc camera usage on Linux. 2024-07-24 17:56:47 +03:00
John Preston
1ebe3255e0 Fix share focus in IV. 2024-07-24 16:09:04 +02:00
John Preston
c70866a995 Fix child native window focus. 2024-07-24 16:09:04 +02:00
John Preston
fd982b90db Implement separate instances for web apps. 2024-07-24 13:46:22 +02:00
John Preston
9461095c88 Fix build with GCC. 2024-07-19 13:01:08 +03:00
John Preston
a2fa1a52e2 Pass VideoCaptureOptions on Linux. 2024-07-19 11:58:26 +02:00
John Preston
a847969e9c Attempt to fix bad input field layout on Qt 6. 2024-07-19 11:58:26 +02:00
John Preston
4d647e64b7 Fix chat links with '-' in slug.
Fixes https://bugs.telegram.org/c/41902.
2024-07-19 11:58:26 +02:00
John Preston
f8b756d447 Improve bot checkout error messages. 2024-07-19 11:58:26 +02:00
John Preston
484c647b5b Fix bot about text layout on wide windows. 2024-07-19 11:58:26 +02:00
John Preston
730c968b1e Use always 1:1 ratio in limits boxes.
Otherwise "Free" label doesn't fit in 2:20 shareable folders.
2024-07-19 11:58:26 +02:00
John Preston
8a6a749296 Show error on attempt to scan QR in bot app.
Fixes #26886.
2024-07-19 11:58:26 +02:00
John Preston
2f22a8f46b Add Windows on ARM to build scripts. 2024-07-19 11:58:26 +02:00
John Preston
f123a9e16c Add openh264 to Dockerfile. 2024-07-19 11:58:20 +02:00
John Preston
11c45b0342 Update submodules. 2024-07-19 11:57:44 +02:00
John Preston
2cd6bfef06 Fix H265 in webrtc. 2024-07-19 11:57:41 +02:00
John Preston
61ca619db4 Fix build on macOS. 2024-07-19 11:56:57 +02:00
John Preston
675ee9088f Fix build on Windows. 2024-07-19 11:56:57 +02:00
John Preston
28a6aa45b9 Update msys2 and patches. 2024-07-19 11:56:57 +02:00
John Preston
08ec9e6bfd Fix build on macOS with new dependencies. 2024-07-19 11:56:54 +02:00
John Preston
ee9f99a754 Add some Windows on ARM special cases. 2024-07-19 11:56:34 +02:00
John Preston
2412183b83 Build most dependencies for Windows on ARM. 2024-07-19 11:56:34 +02:00
John Preston
e83704982f Set initial location correctly. 2024-07-19 11:56:34 +02:00
John Preston
6f86acf712 Use system reverse geocoding on macOS. 2024-07-19 11:56:34 +02:00
John Preston
c22698084f Location search cancel by X button. 2024-07-19 11:56:34 +02:00
John Preston
8c55364afa Allow editing business location in Settings. 2024-07-19 11:56:34 +02:00
John Preston
2c3ef13b01 Update tg_owt to M123. 2024-07-19 11:56:25 +02:00
John Preston
03454ca3b4 Fix sticker quote reply layout. 2024-07-19 11:20:54 +02:00
John Preston
8a92c89f39 Add fsqr promo footer. 2024-07-19 11:20:54 +02:00
John Preston
b83b403b75 Pass correct peer to venue search. 2024-07-19 11:20:54 +02:00
John Preston
8aac07b3c0 Show empty venue search states. 2024-07-19 11:20:54 +02:00
John Preston
b4dfc25df5 Implement venues search. 2024-07-19 11:20:54 +02:00
John Preston
917d1841c1 Try better webview focusing. 2024-07-19 11:20:54 +02:00
John Preston
8ce10d5503 Send chosen venues. 2024-07-19 11:20:54 +02:00
John Preston
a130bb1be6 Search for venues by location. 2024-07-19 11:20:54 +02:00
John Preston
de52ac6b28 Resolve different addresses. 2024-07-19 11:20:54 +02:00
John Preston
310837c9e1 Add venues list and chosen place name. 2024-07-19 11:20:54 +02:00
John Preston
8e6d7bb190 Improve location picker design. 2024-07-19 11:20:53 +02:00
John Preston
025ab40687 Implement location sending on macOS. 2024-07-19 11:20:53 +02:00
John Preston
2a5071b66c Initial location sending on Windows. 2024-07-19 11:20:53 +02:00
23rd
2a63496054 Extended conditions to ability to view channel message statistics. 2024-07-18 01:54:05 +03:00
23rd
a52d4eb4e8 Fixed peer in list of credits history entries. 2024-07-17 18:11:22 +03:00
23rd
4f7a124f3e Added phrases to credits history entries when peer type is premium bot. 2024-07-17 10:04:14 +02:00
23rd
4b9eb37bd5 Added phrases for single winner of giveaway in channels. 2024-07-17 10:04:14 +02:00
23rd
1d5e4040f4 Added api support of flag to view credits stats from full channel. 2024-07-17 10:04:13 +02:00
23rd
6818b8d8dc Fixed row name in table for credits history entries in earn box. 2024-07-17 10:04:13 +02:00
23rd
3f0b962ae5 Fixed color reset for chat filters on saving. 2024-07-17 10:04:13 +02:00
23rd
8ac1ad3484 Updated Qt to 6.2.9 on macOS. 2024-07-17 10:04:13 +02:00
John Preston
c6e1cf639e Update API scheme to layer 184. 2024-07-17 10:04:13 +02:00
Ilya Fedin
5b9278eced Switch Docker to distro-provided cmake 2024-07-15 09:21:47 +02:00
John Preston
03d4dd00d4 Delete selection on Ctrl+Backspace.
Fixes #28143.
2024-07-12 10:32:59 +02:00
John Preston
f7d698b9ff Fix last seen within week/month. 2024-07-12 08:45:45 +02:00
John Preston
46b69a938b Revert "Register tg:// scheme on initial launch."
This reverts commit 8cbeadc68a.
2024-07-12 08:45:45 +02:00
John Preston
ebba58217c Don't set focus to shown third section.
I hope it fixes #28142.
2024-07-12 08:45:45 +02:00
John Preston
94ad8f9bc3 Don't register collapsed row click as userpic.
Fixes #28149.
2024-07-12 08:45:44 +02:00
John Preston
6effac7915 Migrate games to AttachWebView. 2024-07-12 08:45:44 +02:00
John Preston
78093173a9 Version 5.2.3.
- Fix crash in bot star stats page.
- Bug fixes and other minor improvements.
2024-07-07 09:01:58 +04:00
John Preston
a01d48f063 Update submodules. 2024-07-06 22:21:04 +04:00
John Preston
149c69c9f5 Use a separate string for Your Stars in Settings. 2024-07-06 22:13:44 +04:00
John Preston
df277b366b Fix build on Windows. 2024-07-06 13:31:44 +04:00
John Preston
f20475f07e Show forbidden icon on disabled webview button. 2024-07-06 13:31:27 +04:00
John Preston
b6664625ea Fix assigning text after formatted text.
Fixes #28115.
2024-07-06 13:31:27 +04:00
John Preston
1028219276 Allow chats list preview for narrow photos. 2024-07-06 11:08:42 +04:00
John Preston
219671a3bc Fix archive in Main Menu context menu. 2024-07-06 11:08:42 +04:00
John Preston
8c97e915ec Create .mm source blanks for macOS modules. 2024-07-06 11:08:42 +04:00
John Preston
b648548001 Don't insert "data:image.." after image paste cancel. 2024-07-06 11:08:42 +04:00
23rd
b377c02ad3 Added support of min boost level for channel ads to feature list. 2024-07-06 11:08:41 +04:00
23rd
66d6b461f3 Fixed support type of credits history entry for ads in earn section. 2024-07-06 11:08:41 +04:00
John Preston
054a6db3ae Fix warnings on Windows in submodules. 2024-07-06 11:08:36 +04:00
Ilya Fedin
bf7042df44 Enable warnings as errors on Windows 2024-07-06 11:05:28 +04:00
Ilya Fedin
aa140b2919 Fix warnings on Windows 2024-07-06 11:05:28 +04:00
Ilya Fedin
8d0d9bb0bd Delay clearing transient parent until the pip window is really exposed 2024-07-06 09:17:04 +04:00
Daniel Novomeský
9ca9904732 Upgrade libjxl to v0.10.3 2024-07-05 21:30:00 +04:00
Daniel Novomeský
e6e1b9446d Upgrade libjxl on Linux to 0.10.3 2024-07-03 20:04:32 +04:00
John Preston
a507d28b49 Version 5.2.2.
- Fix topics search in topic groups.
- Fix Instant View pages content updating.
2024-07-02 19:03:07 +04:00
John Preston
ec2faca145 Fix couple of crashes in secondary windows. 2024-07-02 18:08:24 +04:00
John Preston
17bb430006 Register tg:// scheme on first launch.
Also, allow disabling tg:// re-registration on update.
2024-07-02 13:34:52 +04:00
John Preston
8cbeadc68a Register tg:// scheme on initial launch. 2024-07-02 13:22:33 +04:00
John Preston
3947056654 Use cached views count from not modified page. 2024-07-02 10:40:39 +04:00
John Preston
ad7d1fddf0 Reload IV on each open, take hash into account.
Fixes #28111.
2024-07-02 10:27:26 +04:00
John Preston
ab20f8eb31 Fix topics search in topic groups.
Fixes #28110.
2024-07-02 09:53:56 +04:00
John Preston
f75429cbaa Version 5.2.1: Shorten webview socket path.
Fixes #28106.
2024-07-01 21:35:39 +04:00
John Preston
dc8c4a8332 Version 5.2.1: No auto-enable system password unlock on macOS. 2024-07-01 21:03:05 +04:00
John Preston
096530c96a Version 5.2.1: Fix drafts / translations crash on macOS. 2024-07-01 19:21:27 +04:00
23rd
ed0850d823 Renamed files for bot and channel earn sections with different names. 2024-07-01 15:40:39 +03:00
John Preston
4a2173deaf Version 5.2.1.
- Fix crash when opening topic in a new window.
- Fix crash in topic search scope dropdown.
- Fix crash in video player.
- Fix feeze and crash in Instant View (Windows).
- Allow unlock by Apple Watch or System Password (macOS).
2024-07-01 15:01:35 +04:00
John Preston
f40a584905 Support unlock with Watch/SystemPassword. 2024-07-01 15:01:35 +04:00
John Preston
e54204b136 Don't allow forwarding local items by drag. 2024-07-01 13:20:45 +04:00
John Preston
715dff0a3e Fix a crash and a memory leak in video player.
Fixes #28104.
2024-07-01 11:03:19 +04:00
23rd
558daa3382 Fixed align of icons in list of top-up credit options. 2024-07-01 10:50:52 +04:00
GitHub Action
7265297b19 Update User-Agent for DNS to Chrome 126.0.0.0. 2024-07-01 10:49:45 +04:00
John Preston
1fac6db8bd Fix crash in topic search context menu destruction. 2024-07-01 10:46:34 +04:00
John Preston
c9bd776d1e Fix freeze in IV due to cyclic focus request. 2024-07-01 10:38:41 +04:00
John Preston
179e81478e Ignore filter switching in separate window archive. 2024-07-01 09:21:18 +04:00
John Preston
9ef74c510c Fix crash in opening topics in a new window. 2024-07-01 09:07:45 +04:00
Ilya Fedin
36f6917bd3 Query QWindow::windowState in media viewer 2024-07-01 07:56:55 +04:00
John Preston
707951accb Version 5.2: Fix build with GCC. 2024-06-30 23:59:03 +04:00
John Preston
7eb98b50ec Version 5.2.
- Pay for content with Telegram Stars.
- Enable local passcode unlock by Windows Hello and Touch ID.
- Allow opening forums, topics and archive in a separate window.
2024-06-30 21:21:51 +04:00
John Preston
65f7bdb914 Update API scheme on layer 183. 2024-06-30 21:16:11 +04:00
John Preston
876c47c436 Add 'section_separator_color' to webview palette. 2024-06-30 21:16:11 +04:00
John Preston
e8f16840de Fix wrong search focusing.
Fixes #28052.
2024-06-30 21:16:11 +04:00
John Preston
dd57ad567f Don't break by '/', '.' in the middle of the word.
Fixes #27999.
2024-06-30 21:16:11 +04:00
John Preston
54934fb835 Fix problems with message effect preview. 2024-06-30 21:16:11 +04:00
John Preston
bd49887607 Fix paid media price tag background. 2024-06-30 21:16:11 +04:00
John Preston
aaa72b7c30 Improve system unlock button area. 2024-06-30 21:16:10 +04:00
John Preston
4cafacc8db Add message effects promo section. 2024-06-30 21:16:10 +04:00
John Preston
374b3c68ac Fix cancel search with a similar channel open.
Fixes #28079.
2024-06-30 21:16:10 +04:00
John Preston
69d21f73ef Separate window for forums/topics/sublists/archive. 2024-06-30 21:16:10 +04:00
23rd
6dc3bd65e8 Improved spell checking on macOS. 2024-06-30 21:16:10 +04:00
23rd
deb9aa435b Improved first display of personal channel in user profiles. 2024-06-30 21:16:10 +04:00
23rd
68cb568898 Fixed build with Qt6. 2024-06-30 21:16:10 +04:00
23rd
1ed81b1c9c Improved process of mtp updates for revenue credits in earn section. 2024-06-30 21:16:10 +04:00
23rd
a95fb5b28d Slightly improved withdraw phrases to fit small buttons. 2024-06-30 21:16:10 +04:00
23rd
4f7b5ca7da Added process of server notification for disabled withdrawal feature. 2024-06-30 21:16:10 +04:00
23rd
29b7673b88 Simplified handle of incoming credit history entries. 2024-06-30 21:16:10 +04:00
23rd
66f375d2c6 Fixed endless memory copying in list of credits history entries. 2024-06-30 21:16:10 +04:00
John Preston
77d214d2a5 Support paid_media_allowed flag in ChannelFull. 2024-06-30 21:16:10 +04:00
John Preston
09e6077e97 Suggest global search of cashtags. 2024-06-30 21:16:10 +04:00
John Preston
631d6abb06 Improve formatting in message field. 2024-06-30 21:16:10 +04:00
John Preston
c833b8a1b0 Make purchased media badge better looking. 2024-06-30 21:16:10 +04:00
John Preston
b58c03f0de Fix build with Xcode. 2024-06-30 21:16:10 +04:00
John Preston
1988435cdf Add a Windows Hello / Touch ID system unlock. 2024-06-30 21:16:10 +04:00
John Preston
7e704d9529 Show price in my/purchased paid media. 2024-06-30 21:16:10 +04:00
John Preston
a39a8dbd2c Show paid media from transactions history. 2024-06-30 21:16:10 +04:00
John Preston
90dfae52f5 Show nice thumbnails for paid albums. 2024-06-30 21:16:10 +04:00
John Preston
dfc422b505 Show paid media albums with context. 2024-06-30 21:16:10 +04:00
John Preston
54cc12cf22 Show media payments as outgoing. 2024-06-30 21:16:10 +04:00
John Preston
d81e832ae6 Use lang key for about stars link. 2024-06-30 21:16:10 +04:00
John Preston
1399d2501d Show first paid media in transactions history. 2024-06-30 21:16:09 +04:00
John Preston
57254ca259 Show a credits emoji in chats list preview. 2024-06-30 21:16:09 +04:00
John Preston
043e3ae97e Use star emoji fallback for credits custom emoji. 2024-06-30 21:16:09 +04:00
John Preston
68b9a8bc6a Fix file reference refresh for paid media. 2024-06-30 21:16:09 +04:00
23rd
157d5c743b Renamed file for data of credits earn statistics. 2024-06-30 21:16:09 +04:00
23rd
54d0290ba2 Added warning to channel earn section when user has no cloud password. 2024-06-30 21:16:09 +04:00
23rd
0fc2df8eec Added transaction link and date to details of credits history entries. 2024-06-30 21:16:09 +04:00
23rd
d9caf15d1d Added initial ability to reinvest existing credits for channels. 2024-06-30 21:16:09 +04:00
23rd
b674826392 Added special widget for credits input to section of channel earn. 2024-06-30 21:16:09 +04:00
23rd
37181f9d0a Moved out special widget for credits input to single place. 2024-06-30 21:16:09 +04:00
23rd
93aebc747d Moved out special input for credits to td_ui. 2024-06-30 21:16:09 +04:00
23rd
a84ac933dd Added support of api update to history lists in section of channel earn. 2024-06-30 21:16:09 +04:00
23rd
b79c306bfe Added list of credits history to section of channel earn. 2024-06-30 21:16:09 +04:00
23rd
57d62423b3 Moved out custom slider class with natural width to single place. 2024-06-30 21:16:09 +04:00
23rd
f4674389d5 Added chart of credits revenue to section of channel earn. 2024-06-30 21:16:09 +04:00
23rd
63c4c5064f Added support of api updates for overview in channel earn section. 2024-06-30 21:16:09 +04:00
23rd
ae1f364730 Added rpl support to label with icon in section of channel earn. 2024-06-30 21:16:09 +04:00
23rd
c6e322de86 Added credits oveview to section of channel earn. 2024-06-30 21:16:09 +04:00
23rd
68bf6f991c Moved out util for wrapping credits icon to widget. 2024-06-30 21:16:09 +04:00
23rd
b15f5f8596 Added credits data to saved state of channel earn section. 2024-06-30 21:16:09 +04:00
23rd
27a71a8dcd Renamed BotEarnStatistics to CreditsEarnStatistics. 2024-06-30 21:16:08 +04:00
John Preston
2044f8f9ad Update API scheme on layer 183. 2024-06-30 21:16:08 +04:00
23rd
ffa4b1db87 Fixed withdrawal button for credits with minimal amount. 2024-06-30 21:16:08 +04:00
23rd
cad25ae644 Fixed unique row id in lists of credits history entries. 2024-06-30 21:16:08 +04:00
23rd
21094fe11b Slightly improved process of incoming credit history entries. 2024-06-30 21:16:08 +04:00
23rd
101dbdf243 Added support of MTP updates to lists of credits history entries. 2024-06-30 21:16:08 +04:00
John Preston
0dc92762bc Show link to message in transactions history. 2024-06-30 21:16:08 +04:00
John Preston
5fdaa6b91f Record bareMsgId in CreditsHistoryEntry. 2024-06-30 21:16:08 +04:00
John Preston
968d036834 Use Ui::NumberInput for the paid media price. 2024-06-30 21:16:08 +04:00
John Preston
d47c5df73d Nice price tag on sending media. 2024-06-30 21:16:08 +04:00
John Preston
6c1e7357c6 Nice unlock media stars, unlock done tooltip. 2024-06-30 21:16:08 +04:00
John Preston
479b63c33a Customize pay-by-stars box for paid media. 2024-06-30 21:16:08 +04:00
John Preston
950a946a16 Show correct chat preview for paid media. 2024-06-30 21:16:08 +04:00
John Preston
5f8da27c86 Display nice price tag, handle pay in albums. 2024-06-30 21:16:08 +04:00
John Preston
a9bd7803e6 Edit price on send, send single paid media. 2024-06-30 21:16:08 +04:00
John Preston
3ece9b1566 Update API scheme to layer 183. Paid media. 2024-06-30 21:16:08 +04:00
John Preston
e71a067f4b Fix build on Windows. 2024-06-30 21:16:08 +04:00
23rd
ebf456abe4 Added initial support of lists for credits history entries in bot earn. 2024-06-30 21:16:08 +04:00
23rd
3552da5ce7 Added additional row in table for some types of credits history entries. 2024-06-30 21:16:08 +04:00
23rd
b5bd0f53ad Added support of new statuses of credits history entries. 2024-06-30 21:16:08 +04:00
23rd
7d115b3fab Improved api support to credit history entries. 2024-06-30 21:16:08 +04:00
23rd
d0a030ab58 Added support of minimal amount of credits to withdrawal button. 2024-06-30 21:16:08 +04:00
23rd
712c06756e Added icon to label in withdrawal button from bot earn section. 2024-06-30 21:16:08 +04:00
23rd
301ffc15ef Added countdown label to withdrawal button in bot earn section. 2024-06-30 21:16:07 +04:00
23rd
3c4a711b5d Added withdrawal button to bot earn sections. 2024-06-30 21:16:07 +04:00
23rd
989145726d Made withdrawal button handler in earn sections more universal. 2024-06-30 21:16:07 +04:00
23rd
9eebd3b514 Added statistical chart and balance overview to bot earn section. 2024-06-30 21:16:07 +04:00
23rd
eb997ae9e3 Added initial implementation of filter for MTP updates. 2024-06-30 21:16:07 +04:00
23rd
db4c9b83f3 Added credits icon to statistical charts. 2024-06-30 21:16:07 +04:00
23rd
1196b6a3fb Moved out generating of credits icon to td_ui. 2024-06-30 21:16:07 +04:00
23rd
bef216bc93 Moved out colorizing of svg star to td_ui. 2024-06-30 21:16:07 +04:00
23rd
811d75e383 Improved display of different inner currencies in statistical charts. 2024-06-30 21:16:07 +04:00
23rd
049cde48ee Added initial api support of bot earn statistics. 2024-06-30 21:16:07 +04:00
23rd
cb65c50c19 Moved out deserialization of MTP statistical graph to separated file. 2024-06-30 21:16:07 +04:00
23rd
f23c9a61bc Moved out StatisticsRequestSender to separated file. 2024-06-30 21:16:07 +04:00
23rd
b5d5ff3cbb Improved naming of classes for channel earn. 2024-06-30 21:16:07 +04:00
23rd
c5ba0fa705 Added initial structure for bot earn data. 2024-06-30 21:16:07 +04:00
23rd
71893f4ef7 Added initial implementation of entry point to bot earn section. 2024-06-30 21:16:07 +04:00
23rd
4a60c57661 Added dummy files of bot earn section. 2024-06-30 21:16:07 +04:00
John Preston
fbbcc21198 Update API scheme to layer 182. 2024-06-30 21:16:07 +04:00
Ilya Fedin
d993386756 Fix debug-only openssl build on Windows 2024-06-30 11:31:57 +04:00
Ilya Fedin
30819509d3 Set shortcut context for global menu shortcuts 2024-06-30 10:11:40 +04:00
mrbesen
10c3fe0f63 add option to export html and json in global export 2024-06-27 10:49:39 +04:00
Ilya Fedin
3498a7f0ee Update submodules 2024-06-24 16:23:32 +04:00
Ilya Fedin
648b23b548 Do non-getting XCB requests synchronously 2024-06-24 16:23:32 +04:00
Ilya Fedin
ba89912834 Switch more XCB code to base::Platform::XCB::Connection 2024-06-24 16:23:32 +04:00
Ilya Fedin
c1bc7e6ab1 Simplify PipPanel::handleWaylandResize 2024-06-22 11:32:36 +04:00
Ilya Fedin
eea50ed6b0 Delay UpdatePowerSaveBlocker to show in media viewer 2024-06-22 11:32:36 +04:00
Ilya Fedin
006d6fe2c0 Use RpWidget::screenValue 2024-06-22 11:32:36 +04:00
Ilya Fedin
5180e0ec57 Use QWidget::setScreen with Qt 6 2024-06-22 11:32:36 +04:00
Ilya Fedin
490ec7949f Don't call hide/createWinId just to call show right after that in Window::Notifications::Default::HideAllButton 2024-06-22 11:32:36 +04:00
Ilya Fedin
671a15d763 Update skip taskbar hint on Linux without QWindow events 2024-06-22 11:32:36 +04:00
Ilya Fedin
d2d5226dc7 Update exposed state for connecting widgets without QWindow events 2024-06-22 11:32:36 +04:00
Ilya Fedin
b58ece3a38 Update lib_ui 2024-06-22 11:32:36 +04:00
Ilya Fedin
2b4a2b5b97 Fix a crash in Dialogs::Widget::setupStories 2024-06-22 11:32:36 +04:00
Ilya Fedin
382b175db2 Fix build on Linux 2024-06-22 11:32:36 +04:00
John Preston
2db81211c8 Fix configure on macOS. 2024-06-21 22:38:49 +04:00
John Preston
46157c99c4 Allow saving empty syntax highlight language. 2024-06-21 22:38:49 +04:00
John Preston
c3ed5224c2 Use tripple-enter to jump out of a block. 2024-06-21 22:38:49 +04:00
John Preston
acee7c7cfc More delay-load libraries on Windows with Qt 6. 2024-06-21 13:25:48 +04:00
John Preston
002fe9a72a Fix build. 2024-06-21 11:55:25 +04:00
John Preston
93b7c47cda Support building on Windows with Qt 6. 2024-06-21 11:36:18 +04:00
Ilya Fedin
053f8ad1c0 Ensure media viewer has native window handle before using it 2024-06-21 08:19:44 +04:00
Ilya Fedin
60ca6895db Update submodules 2024-06-19 10:33:33 +04:00
Ilya Fedin
fc5e9414b7 Subscribe to QEvent::WindowStateChange instead of QWindow::windowStateChanged 2024-06-19 10:33:33 +04:00
Ilya Fedin
f768e405fa Use RpWidget::shownValue instead of QWindow::visibleChanged 2024-06-19 10:33:33 +04:00
Ilya Fedin
a22cf8e303 Use RpWidget::windowActiveValue 2024-06-19 10:33:33 +04:00
Ilya Fedin
8a5797e1bd Re-create QSystemTrayIcon on SNI watcher owner change 2024-06-19 10:31:59 +04:00
Ilya Fedin
750ad600be Store media viewer geometry the same way as main window 2024-06-19 10:08:14 +04:00
Ilya Fedin
eaf4575eb8 Fix IV window geometry restoration 2024-06-19 10:08:14 +04:00
Ilya Fedin
b9677fe1db Update Qt to 6.7.2 on Linux 2024-06-18 22:01:23 +04:00
Ilya Fedin
4047f1733d Update submodules 2024-06-16 20:59:32 +04:00
John Preston
3d114131e0 Fix premium gifting recipient choosing. 2024-06-16 10:27:38 +04:00
John Preston
5810149a77 Improve chats search loading indication. 2024-06-16 10:27:38 +04:00
Ilya Fedin
032fe3e0fc Remove std::optional indrection for nullable XCB types 2024-06-16 09:54:04 +04:00
John Preston
81d7fcba7e Beta version 5.1.8: Fix build with GCC. 2024-06-15 23:03:55 +04:00
John Preston
4e9b5b0d33 Beta version 5.1.8: Fix build. 2024-06-15 22:45:21 +04:00
John Preston
90068f6261 Beta version 5.1.8: Update tg_owt on Linux. 2024-06-15 21:16:55 +04:00
John Preston
f37d056c14 Beta version 5.1.8.
- Support nice blockquotes and code blocks edition when composing messages.
- Support collapsing blockquotes and specifying syntax highlight language.
- Support nice spoiler animation in the message composing input field.
2024-06-15 21:06:23 +04:00
John Preston
f748988ae3 Update Qt patches. 2024-06-15 20:59:19 +04:00
John Preston
9a25d2c413 Simplify correct unicode characters counting. 2024-06-15 20:57:48 +04:00
John Preston
ec40292cbf Allow editing of collapsed quotes. 2024-06-15 20:57:30 +04:00
John Preston
5e8c3fb146 Allow editing syntax highlighting language. 2024-06-15 20:57:00 +04:00
John Preston
fa8ed186d8 Improve field fade conditions. 2024-06-15 20:56:45 +04:00
John Preston
c1f36d43d0 Support modern blockquote/pre display. 2024-06-15 20:56:44 +04:00
John Preston
501cae2200 Change InputField::font to InputField::style in styles. 2024-06-15 20:56:24 +04:00
John Preston
8ad5117495 Update lib_ui. 2024-06-15 20:55:09 +04:00
Ilya Fedin
d79da3d884 Remove old notification descturction workaround for old X11 systems
There seem to be no need in it anymore
2024-06-15 11:08:06 +04:00
Ilya Fedin
e4d88f829c Remove old window activation workaround for X11 systems
There seem to be no need in it anymore
2024-06-15 11:08:06 +04:00
Ilya Fedin
2673e1df53 Fix IV window on Linux 2024-06-15 11:04:45 +04:00
John Preston
99e88d74bc Version 5.1.7: Fix build with Qt 6. 2024-06-14 19:06:19 +04:00
John Preston
43ffc9d67c Version 5.1.7.
- Fix recently searched hashtags in chats search.
- Fix formatting shortcuts on macOS.
- Fix non-Telegram-Stars-invoice bot buttons with star emoji.
2024-06-14 18:36:28 +04:00
John Preston
053c462dc0 Fix recent hashtags in chats search. 2024-06-14 18:34:59 +04:00
John Preston
174b627a78 Fix markdown shortcuts on macOS / some Linux. 2024-06-14 17:39:16 +04:00
23rd
bb33d0b997 Replaced credits currency with icon only in buttons with Buy type. 2024-06-14 15:32:02 +03:00
pandaninjas
6f6fb3d1b6 Add xlsb to list of executable extensions 2024-06-14 13:49:15 +04:00
Ilya Fedin
3f216ad946 Enable parallelism for more dependencies 2024-06-13 21:35:34 +04:00
John Preston
c49eb7041f Version 5.1.6: Fix sticker effect filtering. 2024-06-13 20:55:49 +04:00
John Preston
a1d8202644 Version 5.1.6.
- Fix search in archived chats in single-column layout.
- Improve chat previews for topics, Saved Messages and groups.
- Fix formatting shortcuts on Linux.
- Fix options for Telegram Stars buying in case of large amounts.
2024-06-13 18:48:35 +04:00
23rd
82428aef28 Added support for quoted-printable encoding to contact media. 2024-06-13 18:44:33 +04:00
23rd
10f7b985c7 Added decimal separators to count of members in shared similar channels. 2024-06-13 18:44:33 +04:00
23rd
06075411a5 Fixed cases when list of top-up credit options is empty. 2024-06-13 18:44:33 +04:00
23rd
7e01b12825 Replaced credits currency with icon in buttons of reply keyboard. 2024-06-13 18:44:33 +04:00
John Preston
273119fc55 Fix preview messages load in migrated groups. 2024-06-13 18:43:58 +04:00
John Preston
e47e4ba338 Update submodules. 2024-06-13 18:08:43 +04:00
John Preston
cbcdeae200 Don't show topic buttons in topic preview. 2024-06-13 18:08:36 +04:00
John Preston
c585112e37 Fix jump to archive from search in chat. 2024-06-13 16:33:58 +04:00
John Preston
392df8b56f Fix search in archived chats and return back. 2024-06-13 16:24:41 +04:00
John Preston
0e6470a087 Don't restrict Saved Messages by premium-only privacy. 2024-06-13 15:47:40 +04:00
John Preston
6c0ea0eb9f Version 5.1.5.
- Return WebView on Windows.
2024-06-07 06:57:27 +04:00
John Preston
9229de2658 Return webview on Windows. 2024-06-07 06:57:06 +04:00
John Preston
2935721cd0 Version 5.1.4.
- Improve design of search in chat.
- Show vCard information for shared contacts.
- Allow scheduling media in topic groups.
- Several minor bugfixes.
2024-06-06 22:19:53 +04:00
John Preston
a48cd5f15a Don't show IV buttons on old Windows. 2024-06-06 22:17:30 +04:00
John Preston
9e77e80f92 Moderation on context menu album delete.
Fixes #28012.
2024-06-06 22:16:57 +04:00
John Preston
deb50ee528 Fix video full-window toggle in windowed mode. 2024-06-06 22:16:53 +04:00
John Preston
14909ae913 Fix empty space to the right of photo media. 2024-06-06 22:16:48 +04:00
John Preston
d5d9da7d0a Allow scheduling media in topic groups. 2024-06-06 22:16:43 +04:00
John Preston
86a048a021 Update libvpx to 1.14.1. 2024-06-06 22:16:38 +04:00
23rd
aafa8631e0 Moved out url for credits terms to lang pack. 2024-06-06 22:16:38 +04:00
Ilya Fedin
9176bf2e47 Update lib_webview 2024-06-06 21:08:54 +04:00
John Preston
72c667b153 Redesign search scope selection. 2024-06-06 09:12:23 +04:00
23rd
fe6f65b3ab Fixed text elision of peer name in section of chat preview. 2024-06-06 09:12:23 +04:00
23rd
5db2821f8c Removed unused refund api support for credits. 2024-06-06 09:12:23 +04:00
23rd
9e3e7265d2 Improved display of cached message preview in profile sections. 2024-06-06 09:12:23 +04:00
23rd
749b2e0e95 Added ability to see vcard if available to all types if contacts media. 2024-06-06 09:12:23 +04:00
Ilya Fedin
5b45397383 Update lib_webview 2024-06-06 09:12:03 +04:00
Ilya Fedin
61c17c0a93 libasound2 -> libasound2t64 2024-06-04 15:48:18 +04:00
John Preston
93e592472c Version 5.1.3.
- Rebuild version for macOS to fix the phrases.
2024-06-04 14:38:05 +04:00
John Preston
379a94db42 Version 5.1.2.
- Several bugs fixed including a couple of crashes.
2024-06-04 00:00:12 +04:00
23rd
4f4d216987 Fixed display of button in channel earn section when balance is low. 2024-06-03 23:00:48 +04:00
23rd
6400875d55 Replaced text drawing in menu from phone handler with Text::String. 2024-06-03 23:00:47 +04:00
John Preston
06d0e78b00 Fix a crash in forwarded sender avatars. 2024-06-03 22:59:14 +04:00
John Preston
9043c18725 Don't drop search on non-empty backspace. 2024-06-03 22:27:15 +04:00
Ilya Fedin
88e742927f File descriptor fixes for XDPOpenWithDialog
- Open it with O_CLOEXEC
- Remove unneseccary std::array complication
- Rely on Gio::UnixFDList to close it
2024-06-03 17:49:16 +04:00
Ilya Fedin
a6fcc6d51d Rely on media viewer code to close itself before ShowInFolder 2024-06-03 17:49:16 +04:00
John Preston
23a13ab54e Close chats search when selecting a folder. 2024-06-03 17:13:14 +04:00
John Preston
e8a929bdbd Fix bottom info display in media. 2024-06-03 15:03:41 +04:00
John Preston
eafc01e02b Fix small photo display in wide enough chat.
Fixes #28004.
2024-06-03 14:14:42 +04:00
John Preston
f817504d67 Fix formatting drop on zero cursor position. 2024-06-03 13:52:00 +04:00
John Preston
f91eb65239 Fix channel reactions editing. 2024-06-03 13:40:04 +04:00
John Preston
e978770fbd Fix crash in theme IV opening. 2024-06-03 12:48:11 +04:00
23rd
9c83b8bac5 Fixed emoji display in reply markup button from bar for pinned messages. 2024-06-02 23:11:09 +03:00
23rd
47ce34e987 Added initial support of vcard from media contacts. 2024-06-02 22:06:49 +03:00
John Preston
1656a9c3e2 Fix bot about title display. 2024-06-02 10:55:13 +04:00
John Preston
47e06cf385 Fix dropping invertCaption on reschedule. 2024-06-02 10:55:13 +04:00
John Preston
f61f649a7e Add a power saving setting for effects. 2024-06-02 10:55:13 +04:00
John Preston
a7bffe7abd Version 5.1.1: Fix misspelled attribute. 2024-06-01 23:58:31 +04:00
John Preston
51b866293f Version 5.1.1.
- Fix caption display on some media.
- Fix collapsed blockquotes rendering.
- Fully close search in chat by "Cancel" click.
- Allow editing caption placement and spoiler in topics.
- Disable effects on forwarded messages and inline results.
2024-06-01 23:40:20 +04:00
John Preston
bd20a3cfe4 Fix filtered premium sticker effects selection. 2024-06-01 23:35:04 +04:00
John Preston
86778aa4d9 Fix cancel search glitch with the new search. 2024-06-01 23:26:02 +04:00
John Preston
12eecec501 Disable effects for inline results sending. 2024-06-01 23:21:48 +04:00
John Preston
26345208a9 Fix caption disappearance on album sending. 2024-06-01 23:02:29 +04:00
John Preston
ee680ac1f1 Don't try adding effects to forwarded messages. 2024-06-01 22:49:51 +04:00
John Preston
bb79a07262 Move SendMenu additional actions to bottom. 2024-06-01 22:43:19 +04:00
John Preston
70fe649743 Hide media viewer on macOS when showing IV. 2024-06-01 22:31:43 +04:00
John Preston
400f0f8785 Empty line between description and ID. 2024-06-01 22:09:01 +04:00
John Preston
eac7bf1c48 Show "View as Messages" preview on forum preview. 2024-06-01 22:02:17 +04:00
John Preston
f4abe37dff Display peer IDs with delimeters. 2024-06-01 21:53:41 +04:00
John Preston
b7f165a259 Fix albums with wide captions. 2024-06-01 21:53:26 +04:00
John Preston
c1b95afd88 Fix media spoiler/caption-above edit in topics/scheduled. 2024-06-01 21:17:09 +04:00
John Preston
36766e7546 Fix collapsed blockquotes in the end of the text. 2024-06-01 21:16:31 +04:00
John Preston
59c016e4ce Fix search input not in the end of the query. 2024-06-01 20:39:31 +04:00
John Preston
e9e347fa6c Fix crash in stories privacy handling. 2024-06-01 20:34:16 +04:00
23rd
7aef0b0a83 Fixed frame size of video userpic in short info boxes on Retina. 2024-06-01 16:55:01 +03:00
23rd
d9572949f6 Replaced confirmation box for proxy links with generic box. 2024-06-01 16:02:59 +03:00
23rd
233e80d22d Fixed display of confirmation box for proxy links in correspond window. 2024-06-01 16:02:59 +03:00
John Preston
5c83858a50 Remove empty space below video without caption. 2024-06-01 10:58:48 +04:00
John Preston
c681569349 Fix info display in video with caption above. 2024-06-01 10:53:00 +04:00
John Preston
bb43afdd93 Fix search in chat close by "Cancel" link. 2024-06-01 10:52:48 +04:00
John Preston
3ba1941808 Version 5.1: Fix build on macOS. 2024-05-31 23:03:06 +04:00
John Preston
72d1b43453 Version 5.1.
- Send messages with effects.
- Move photo or video captions above the media.
- Chat preview on chat photo long press or Alt+Click.
2024-05-31 21:30:18 +04:00
John Preston
0c1b487956 Fix dragging of non-leader media. 2024-05-31 21:30:18 +04:00
23rd
ba611d0f2d Added initial api support of text phone entity in messages. 2024-05-31 18:58:21 +03:00
23rd
50ce847b31 Fixed display of info in media from chat preview. 2024-05-31 19:52:36 +04:00
23rd
dd0d88ccd3 Fixed userpic views in headers of forwarded messages with via bots. 2024-05-31 19:52:36 +04:00
John Preston
a1049ec7ce Add touchscreen preview to recent / channels. 2024-05-31 19:52:36 +04:00
John Preston
0fffeac8da Fix drag-n-drop forward of selected album. 2024-05-31 19:52:36 +04:00
John Preston
1f0acae151 Show frequent/recent selected when preview. 2024-05-31 19:52:36 +04:00
John Preston
521c17b76c Show chat previews in suggestions. 2024-05-31 19:52:36 +04:00
John Preston
f9f51b4e41 Show preview in Frequent Contacts. 2024-05-31 19:52:32 +04:00
John Preston
4e8895ddd9 Show "View as Messages" preview by Alt+Click. 2024-05-31 19:52:32 +04:00
John Preston
ad342a5324 Extract chat preview as a SessionController part. 2024-05-31 19:52:25 +04:00
John Preston
5cfd86b829 Use Ui::Text::String to show topic names.
Fixes #27956.
2024-05-31 19:52:22 +04:00
John Preston
27eb3e45be Allow only t.me links in factchecks. 2024-05-31 19:52:22 +04:00
John Preston
4953246c5d Support touchscreen pinned chats reordering. 2024-05-31 19:52:18 +04:00
John Preston
4df5372dab Support chat preview on touchscreens. 2024-05-31 19:52:12 +04:00
John Preston
40fbd415ef Support collapsible blockquotes in Ui::Text::String. 2024-05-30 21:35:10 +04:00
John Preston
974bf99921 Allow editing spoiler/caption-above in EditCaptionBox. 2024-05-30 21:35:10 +04:00
John Preston
8c0351be4e Allow editing caption above/below media. 2024-05-30 21:35:10 +04:00
John Preston
67f7816088 Allow sending photo/video captions above media. 2024-05-30 21:35:10 +04:00
John Preston
924d80ecba Use message text rendering for media captions. 2024-05-30 21:35:10 +04:00
23rd
d219bccf2b Fixed position of via bot header above reply in message view. 2024-05-30 21:35:10 +04:00
23rd
02bd2bca64 Removed display of credits button in settings when user has no credits. 2024-05-30 21:35:10 +04:00
23rd
57ecc2be1d Improved style of list of credits history entries for refunded entry. 2024-05-30 21:35:10 +04:00
23rd
d3a01b6235 Improved style of list of credits history entries for entry photo. 2024-05-30 21:35:10 +04:00
23rd
58c060c59d Improved style of box for credits history entries for refunded entry. 2024-05-30 21:35:10 +04:00
23rd
cd7507fb23 Respected accessibility of premium for credits purchasing. 2024-05-30 21:35:10 +04:00
23rd
9a5923676a Added posting of payment event to webview bot after credits sending. 2024-05-30 21:35:09 +04:00
John Preston
c0f3d263a3 Remove webpage length limit for factcheck. 2024-05-30 21:35:08 +04:00
John Preston
056ba644ed Fix empty search placeholder. 2024-05-30 21:35:08 +04:00
23rd
ebaffc333e Added initial api support of refund flag to credits history entries. 2024-05-30 21:35:08 +04:00
23rd
be099880d8 Added initial ability to claim refund credits from history entries list. 2024-05-30 21:35:08 +04:00
John Preston
885dcf0b28 Update API scheme on layer 181. 2024-05-30 21:35:08 +04:00
John Preston
a0d97f03cb Add factcheck footer support. 2024-05-30 21:35:08 +04:00
23rd
c942034ca4 Added link to terms to box of credits history entries. 2024-05-30 21:35:08 +04:00
23rd
0bd780b20f Added fireworks effect on success credit sending. 2024-05-30 21:35:08 +04:00
23rd
7d75c25214 Added box for small balance of credits. 2024-05-30 21:35:08 +04:00
23rd
5defb9fb17 Fixed display of credit spending. 2024-05-30 21:35:08 +04:00
23rd
0549c8f037 Moved out list of top-up options to single place. 2024-05-30 21:35:08 +04:00
23rd
3c246e1e92 Split functions for credits settings. 2024-05-30 21:35:08 +04:00
23rd
58da617e3f Added description and optional photo to credits receipts. 2024-05-30 21:35:08 +04:00
23rd
1edf0ed70b Moved out widget of photo for credits history entries to single place. 2024-05-30 21:35:08 +04:00
23rd
c27c567225 Added initial support for non-panel credits receipts. 2024-05-30 21:35:08 +04:00
23rd
93eff78cd6 Replaced all credits currency name at least with simple star. 2024-05-30 21:35:08 +04:00
23rd
a2a27e115c Moved out box for credits history entries to single place. 2024-05-30 21:35:08 +04:00
23rd
e9fb580ba4 Moved out credits currency to single place. 2024-05-30 21:35:08 +04:00
23rd
d73313479b Partly reverted "Processed payments form with API scheme on layer 181.". 2024-05-30 21:35:08 +04:00
23rd
e4e343b871 Removed unused Ui::IsCreditsInvoice. 2024-05-30 21:35:08 +04:00
23rd
dda6b92bec Added initial ability to process non-panel payment forms. 2024-05-30 21:35:07 +04:00
23rd
3dd894ad30 Improved SendCreditsBox for data from credits payment form. 2024-05-30 21:35:07 +04:00
23rd
f08ff92470 Added initial ability to provide data for non-panel payment forms. 2024-05-30 21:35:07 +04:00
23rd
923aaec085 Returned media to messages with credits invoice. 2024-05-30 21:35:07 +04:00
John Preston
1d3110228d Fix phrase in factcheck toast. 2024-05-30 21:35:07 +04:00
23rd
7194781bb8 Added api support for premium bot peer type for credits history entries. 2024-05-30 21:35:07 +04:00
John Preston
97a5e0c6ea Support limited formatting input in factcheck. 2024-05-30 21:35:07 +04:00
John Preston
a3ef36f9f7 Fix build on Windows. 2024-05-30 21:35:07 +04:00
John Preston
d13bf19b79 Show length limit when editing a factcheck. 2024-05-30 21:35:07 +04:00
John Preston
493f0450b4 Implement factcheck edition. 2024-05-30 21:35:07 +04:00
John Preston
74861a334d Show expand/collapse icon in factcheck. 2024-05-30 21:35:07 +04:00
John Preston
a87a221f26 Force red for factcheck. 2024-05-30 21:35:07 +04:00
John Preston
923a9ec6a8 Show toggle-able factcheck footer. 2024-05-30 21:35:07 +04:00
John Preston
b299881bf8 Track factcheck text and create media. 2024-05-30 21:35:07 +04:00
John Preston
5ee2bca616 Update API scheme on layer 181. 2024-05-30 21:35:07 +04:00
23rd
d1e914fb30 Added initial implementation of box for credits history entries. 2024-05-30 21:35:07 +04:00
23rd
43cb315f47 Replaced PeerId with BareId in credits history entries. 2024-05-30 21:35:07 +04:00
23rd
dcc52a7333 Moved out painting of userpic for credits entries to single place. 2024-05-30 21:35:07 +04:00
23rd
84cde1354d Slightly improved invoice view in messages for credits. 2024-05-30 21:35:07 +04:00
23rd
3d81414c71 Added initial implementation of box for sending credits. 2024-05-30 21:35:07 +04:00
23rd
69c48e2b5b Moved out credits balance widget to single place. 2024-05-30 21:35:07 +04:00
23rd
5ca9b74142 Fixed effect for successful payments in credits settings. 2024-05-30 21:35:07 +04:00
23rd
e11755af46 Added credits balance to main session. 2024-05-30 21:35:07 +04:00
23rd
174fb62c32 Fixed purchases of credits. 2024-05-30 21:35:07 +04:00
23rd
0e30e306ff Added second type of stars to animation of mini stars. 2024-05-30 21:35:07 +04:00
23rd
5e29f382cd Fixed build with Xcode. 2024-05-30 21:35:07 +04:00
John Preston
8eb24f620d Update API scheme on layer 181. 2024-05-30 21:35:07 +04:00
John Preston
5adde6c93a Fix build on Windows. 2024-05-30 21:35:07 +04:00
23rd
42d6d0d58a Added tooltip to balance label in credits settings for high values. 2024-05-30 21:35:07 +04:00
23rd
6336df2bd6 Slightly improved code style for decimal counts. 2024-05-30 21:35:07 +04:00
23rd
030d35ea7e Added initial implementation of balance in credits settings. 2024-05-30 21:35:07 +04:00
23rd
d81c3554cc Added multiple icons to credits topup options. 2024-05-30 21:35:07 +04:00
23rd
ca37ffa086 Added list of credit history entries to credits settings. 2024-05-30 21:35:07 +04:00
23rd
154fe63b43 Implemented list of credit history entries. 2024-05-30 21:35:06 +04:00
23rd
65384d54f1 Added random debug data to credits history. 2024-05-30 21:35:06 +04:00
23rd
2bf8cb84d0 Added api support of credits status and credits history. 2024-05-30 21:35:06 +04:00
23rd
f0a82de784 Fixed editing of last message with uploading media. 2024-05-30 21:35:06 +04:00
23rd
1a393ddebb Added star icons with gradient to main settings for premium buttons. 2024-05-30 21:35:06 +04:00
23rd
9b11b95c5b Added api support of invoice payments for credits. 2024-05-30 21:35:06 +04:00
23rd
d0bfee6963 Added initial list for topup options in settings section for credits. 2024-05-30 21:35:06 +04:00
23rd
f1636de572 Added initial api support for credits topup options. 2024-05-30 21:35:06 +04:00
23rd
b5eb195f43 Added initial dummy settings section for credits. 2024-05-30 21:35:06 +04:00
23rd
4a0bffe618 Added ability to provide custom gradient to star in premium top bar. 2024-05-30 21:35:06 +04:00
23rd
53d97b4146 Added ability to provide custom gradient to colored premium stars. 2024-05-30 21:35:06 +04:00
23rd
2a224c839e Added initial phrases for settings section for credits. 2024-05-30 21:35:06 +04:00
23rd
bc7aa91fbb Removed redundant constructor from Ui::Premium::TopBar. 2024-05-30 21:35:06 +04:00
23rd
ac2f35f12b Processed payments form with API scheme on layer 181. 2024-05-30 21:35:06 +04:00
John Preston
39e03c3ca7 Update API scheme to layer181. 2024-05-30 21:35:06 +04:00
John Preston
1ce49df123 Fix saved / replies userpics in chats search. 2024-05-30 21:35:06 +04:00
John Preston
1865fd382c Show loading placeholders in chats list. 2024-05-30 21:35:06 +04:00
John Preston
e00c6ecfb8 Show empty / placeholder in chats search. 2024-05-30 21:35:06 +04:00
John Preston
279db771cf Support dynamic search tabs with emoji. 2024-05-30 21:35:06 +04:00
John Preston
7b7438cd7b Make chats list tabs closer to each other. 2024-05-30 21:35:06 +04:00
John Preston
42d53e5543 Fix saved messages tags search. 2024-05-30 21:35:06 +04:00
John Preston
fce520c9c0 Clear saved messages tags on cancel search. 2024-05-30 21:35:06 +04:00
John Preston
b21bcb86cc Fix search from user in forums. 2024-05-30 21:35:06 +04:00
John Preston
7d61ab9412 Improve forum search support. 2024-05-30 21:35:06 +04:00
John Preston
701bf0d553 Fix searching with "from:" prefix. 2024-05-30 21:35:06 +04:00
John Preston
583bcca6a9 Fix forum search open by Ctrl+F. 2024-05-30 21:35:06 +04:00
John Preston
6a8edefc87 Fix some bugs in new chats search. 2024-05-30 21:35:06 +04:00
John Preston
dd5643ac67 Start chats search rewrite. 2024-05-30 21:35:06 +04:00
John Preston
787cf7853e Implement simple chats search bar. 2024-05-30 21:35:06 +04:00
John Preston
5bfbae3afc Update API scheme on layer 180. 2024-05-30 21:35:06 +04:00
John Preston
363b700f1f Fix chat preview and new emoji interactions. 2024-05-30 21:35:05 +04:00
John Preston
cde70b9807 Play effects in a separate layer over MainWidget. 2024-05-30 21:35:05 +04:00
John Preston
f7ab8a2174 Fix Chat / Effect previews in custom themes. 2024-05-30 21:35:05 +04:00
John Preston
a011a7c316 Fix sending scheduled effects. 2024-05-30 21:35:05 +04:00
John Preston
487fa9728a Fade in/out effect preview. 2024-05-30 21:35:05 +04:00
John Preston
8a58ded582 Show effect loading animation. 2024-05-30 21:35:05 +04:00
John Preston
ec5d8b7373 More robust effect icon loading. 2024-05-30 21:35:05 +04:00
John Preston
732b67ca04 Implement effects paywalls. 2024-05-30 21:35:05 +04:00
John Preston
d102d256a9 Implement effects search. 2024-05-30 21:35:05 +04:00
John Preston
bbb3a51b74 Fix effect selector for !_useTransparency case. 2024-05-30 21:35:05 +04:00
John Preston
d0d1ef9e66 Fix effect panel jump on expand. 2024-05-30 21:35:05 +04:00
John Preston
b01244fc42 Show correct sticker effects title. 2024-05-30 21:35:05 +04:00
John Preston
b92a05011f Show sticker effects in a StickerListWidget. 2024-05-30 21:35:05 +04:00
John Preston
5fb7992b04 Improve effect initial auto-play. 2024-05-30 21:35:05 +04:00
John Preston
f371cd1af2 Use nice fake message for effect preview. 2024-05-30 21:35:05 +04:00
John Preston
144109db05 Show effect preview before sending. 2024-05-30 21:35:05 +04:00
John Preston
e120ae6ae6 Pass effect to API for sending. 2024-05-30 21:35:05 +04:00
John Preston
d1106e5ae6 Check effects availability in all SendMenu-s. 2024-05-30 21:35:05 +04:00
John Preston
396ba9a984 Initial code of attaching effect selector. 2024-05-30 21:35:05 +04:00
John Preston
92133e7f50 Show effect animation with correct geometry. 2024-05-30 21:35:05 +04:00
John Preston
a19e71324b Implement basic effect animation. 2024-05-30 21:35:05 +04:00
John Preston
f762634036 Support effects API, show icon in info. 2024-05-30 21:35:05 +04:00
John Preston
ee4f83ffde Disable bottom-info reactions view. 2024-05-30 21:35:05 +04:00
John Preston
f8188f360a Update API scheme to layer 180. 2024-05-30 21:35:05 +04:00
John Preston
2bbc7406da Beta version 5.0.6.
- Fix chat preview with non-default themes.
- Fix chat preview crash when scrolling up.
- Jump to chat from preview only by Double-Click.
- Show chat preview with Force Touch on macOS.
2024-05-30 19:06:51 +04:00
John Preston
a9dd9aeb90 Use InnoSetup SignTool for setup signing.
Fixes #27583.
2024-05-30 19:03:12 +04:00
John Preston
aa4156d1e7 Ignore shortcuts when preview is shown. 2024-05-30 19:03:08 +04:00
John Preston
4f5594c8cc ChatNext jumps to first chat if no chat opened.
Fixes #27963.
2024-05-30 19:03:04 +04:00
John Preston
0527e9a0f7 Fix adding bot as admin to channel. 2024-05-30 17:02:28 +04:00
John Preston
28cbb02b20 Jump to chat only by Double-Click on preview. 2024-05-30 17:02:26 +04:00
John Preston
7d636820ac Show last updated topic preview on Alt+Click. 2024-05-30 17:02:23 +04:00
John Preston
e2b78b673b Fix unintentional search focus. 2024-05-30 17:02:19 +04:00
John Preston
a9a0fe7cf5 Fix RTL chat names in preview. 2024-05-30 17:02:13 +04:00
John Preston
2b9e7a6b25 Show preview on Force-Click on macOS. 2024-05-30 17:02:09 +04:00
John Preston
d6e827e982 Fix loading of chat preview messages. 2024-05-30 17:02:02 +04:00
John Preston
d2e6003521 Ignore right click on preview. 2024-05-30 17:01:59 +04:00
John Preston
465fc42718 Fix chat preview in non-default themes. 2024-05-29 09:43:40 +04:00
John Preston
0dd6ff9d9b Beta version 5.0.5.
- Long press on chat userpic to show quick preview.
- Alt+Click on chat to show quick preview.
- Show author userpics in forwarded messages.
2024-05-29 00:03:45 +04:00
23rd
8a6b1677f4 Added userpic views to headers of forwarded messages. 2024-05-28 21:42:00 +04:00
John Preston
470b3a2cbd Fix preview showing on fast userpic click. 2024-05-28 21:42:00 +04:00
John Preston
a60783eae3 Implement preview top and bottom. 2024-05-28 21:42:00 +04:00
John Preston
de73d8766c Open chat on exact clicked message from preview. 2024-05-28 21:42:00 +04:00
John Preston
cd7cfcdf2f Show chat preview on Alt+Click. 2024-05-28 21:42:00 +04:00
John Preston
aee62c7591 Fix migrated history in chat preview. 2024-05-28 21:42:00 +04:00
John Preston
0f524ac67d Don't stick to bottom while loading down. 2024-05-28 21:42:00 +04:00
John Preston
f223ae7eee Implement chats preview, show from unread. 2024-05-28 21:42:00 +04:00
John Preston
68df8448a2 Use arc_angles.h from lib_ui. 2024-05-28 21:42:00 +04:00
John Preston
da31fef1ae Show long-press preview of topics. 2024-05-28 21:41:59 +04:00
John Preston
4427ae4306 Empty preview widget on long userpic press. 2024-05-28 21:41:59 +04:00
John Preston
ef2aa05197 Allow HistoryView::ListWidget without SessionController. 2024-05-28 21:41:59 +04:00
John Preston
e5132e3fe8 Version 5.0.4
- Fix reply to last message by Ctrl+Up in topics.
- Some other bug fixes.
2024-05-28 20:32:48 +04:00
23rd
3b6870396c Added ability to hide every sponsored message to premium users. 2024-05-28 20:30:40 +04:00
23rd
f6b849e4f7 Added ability to add proxy from clipboard. 2024-05-28 20:30:40 +04:00
23rd
48e3802565 Improved labels for channel and group types with restricted content. 2024-05-28 20:30:40 +04:00
23rd
26ba7e57ce Fixed color of radial animation in audio files from shared media. 2024-05-28 20:30:40 +04:00
23rd
2605e754ff Added back button to cloud password step in intro. 2024-05-28 20:30:40 +04:00
23rd
9e85b1aa23 Fixed fake ability to hide webpage media with spoiler. 2024-05-28 20:30:40 +04:00
Ilya Fedin
88cd886ec8 Update lib_webview 2024-05-28 17:50:39 +04:00
John Preston
adc536b81d Fix Ctrl+Up reply in topics / comments. 2024-05-28 15:01:01 +04:00
553 changed files with 27392 additions and 7623 deletions

View File

@@ -69,7 +69,7 @@ jobs:
run: |
brew update
brew upgrade || true
brew install autoconf automake boost cmake ffmpeg@6 openal-soft openssl opus ninja pkg-config python qt yasm xz
brew install autoconf automake boost cmake ffmpeg@6 openal-soft openh264 openssl opus ninja pkg-config python qt yasm xz
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
xcodebuild -version > CACHE_KEY.txt
@@ -108,7 +108,7 @@ jobs:
run: |
cd $LibrariesPath
git clone --recursive --depth=1 $GIT/desktop-app/tg_owt.git
git clone --depth=1 --recursive --shallow-submodules $GIT/desktop-app/tg_owt.git
cd tg_owt
cmake -B build . -GNinja -DCMAKE_BUILD_TYPE=Debug

View File

@@ -169,6 +169,8 @@ jobs:
%TDESKTOP_BUILD_GENERATOR% ^
%TDESKTOP_BUILD_ARCH% ^
%TDESKTOP_BUILD_API% ^
-D CMAKE_C_FLAGS="/WX" ^
-D CMAKE_CXX_FLAGS="/WX" ^
-D DESKTOP_APP_DISABLE_AUTOUPDATE=OFF ^
-D DESKTOP_APP_DISABLE_CRASH_REPORTS=OFF ^
-D DESKTOP_APP_NO_PDB=ON ^

View File

@@ -57,14 +57,6 @@ include(cmake/validate_d3d_compiler.cmake)
include(cmake/target_prepare_qrc.cmake)
include(cmake/options.cmake)
if (NOT DESKTOP_APP_USE_PACKAGED)
if (WIN32)
set(qt_version 5.15.13)
elseif (APPLE)
set(qt_version 6.2.8)
endif()
endif()
include(cmake/external/qt/package.cmake)
set(desktop_app_skip_libs

View File

@@ -120,10 +120,13 @@ PRIVATE
api/api_common.h
api/api_confirm_phone.cpp
api/api_confirm_phone.h
api/api_credits.cpp
api/api_credits.h
api/api_earn.cpp
api/api_earn.h
api/api_editing.cpp
api/api_editing.h
api/api_filter_updates.h
api/api_global_privacy.cpp
api/api_global_privacy.h
api/api_hash.cpp
@@ -162,6 +165,10 @@ PRIVATE
api/api_single_message_search.h
api/api_statistics.cpp
api/api_statistics.h
api/api_statistics_data_deserialize.cpp
api/api_statistics_data_deserialize.h
api/api_statistics_sender.cpp
api/api_statistics_sender.h
api/api_text_entities.cpp
api/api_text_entities.h
api/api_toggling_media.cpp
@@ -299,6 +306,8 @@ PRIVATE
boxes/ringtones_box.h
boxes/self_destruction_box.cpp
boxes/self_destruction_box.h
boxes/send_credits_box.cpp
boxes/send_credits_box.h
boxes/send_files_box.cpp
boxes/send_files_box.h
boxes/sessions_box.cpp
@@ -441,6 +450,8 @@ PRIVATE
core/launcher.h
core/local_url_handlers.cpp
core/local_url_handlers.h
core/phone_click_handler.cpp
core/phone_click_handler.h
core/sandbox.cpp
core/sandbox.h
core/shortcuts.cpp
@@ -462,6 +473,10 @@ PRIVATE
data/business/data_business_info.h
data/business/data_shortcut_messages.cpp
data/business/data_shortcut_messages.h
data/components/factchecks.cpp
data/components/factchecks.h
data/components/location_pickers.cpp
data/components/location_pickers.h
data/components/recent_peers.cpp
data/components/recent_peers.h
data/components/scheduled_messages.cpp
@@ -539,6 +554,8 @@ PRIVATE
data/data_groups.h
data/data_histories.cpp
data/data_histories.h
data/data_history_messages.cpp
data/data_history_messages.h
data/data_lastseen_status.h
data/data_location.cpp
data/data_location.h
@@ -712,8 +729,6 @@ PRIVATE
history/view/media/history_view_dice.h
history/view/media/history_view_document.cpp
history/view/media/history_view_document.h
history/view/media/history_view_extended_preview.cpp
history/view/media/history_view_extended_preview.h
history/view/media/history_view_file.cpp
history/view/media/history_view_file.h
history/view/media/history_view_game.cpp
@@ -781,6 +796,8 @@ PRIVATE
history/view/history_view_about_view.h
history/view/history_view_bottom_info.cpp
history/view/history_view_bottom_info.h
history/view/history_view_chat_preview.cpp
history/view/history_view_chat_preview.h
history/view/history_view_contact_status.cpp
history/view/history_view_contact_status.h
history/view/history_view_context_menu.cpp
@@ -795,6 +812,8 @@ PRIVATE
history/view/history_view_emoji_interactions.h
history/view/history_view_empty_list_bubble.cpp
history/view/history_view_empty_list_bubble.h
history/view/history_view_fake_items.cpp
history/view/history_view_fake_items.h
history/view/history_view_group_call_bar.cpp
history/view/history_view_group_call_bar.h
history/view/history_view_item_preview.h
@@ -825,14 +844,14 @@ PRIVATE
history/view/history_view_send_action.h
history/view/history_view_service_message.cpp
history/view/history_view_service_message.h
history/view/history_view_spoiler_click_handler.cpp
history/view/history_view_spoiler_click_handler.h
history/view/history_view_sponsored_click_handler.cpp
history/view/history_view_sponsored_click_handler.h
history/view/history_view_sticker_toast.cpp
history/view/history_view_sticker_toast.h
history/view/history_view_sublist_section.cpp
history/view/history_view_sublist_section.h
history/view/history_view_text_helper.cpp
history/view/history_view_text_helper.h
history/view/history_view_transcribe_button.cpp
history/view/history_view_transcribe_button.h
history/view/history_view_translate_bar.cpp
@@ -873,6 +892,10 @@ PRIVATE
history/history_view_highlight_manager.h
history/history_widget.cpp
history/history_widget.h
info/bot/earn/info_bot_earn_list.cpp
info/bot/earn/info_bot_earn_list.h
info/bot/earn/info_bot_earn_widget.cpp
info/bot/earn/info_bot_earn_widget.h
info/channel_statistics/boosts/create_giveaway_box.cpp
info/channel_statistics/boosts/create_giveaway_box.h
info/channel_statistics/boosts/giveaway/giveaway_list_controllers.cpp
@@ -881,10 +904,10 @@ PRIVATE
info/channel_statistics/boosts/info_boosts_inner_widget.h
info/channel_statistics/boosts/info_boosts_widget.cpp
info/channel_statistics/boosts/info_boosts_widget.h
info/channel_statistics/earn/info_earn_inner_widget.cpp
info/channel_statistics/earn/info_earn_inner_widget.h
info/channel_statistics/earn/info_earn_widget.cpp
info/channel_statistics/earn/info_earn_widget.h
info/channel_statistics/earn/info_channel_earn_list.cpp
info/channel_statistics/earn/info_channel_earn_list.h
info/channel_statistics/earn/info_channel_earn_widget.cpp
info/channel_statistics/earn/info_channel_earn_widget.h
info/common_groups/info_common_groups_inner_widget.cpp
info/common_groups/info_common_groups_inner_widget.h
info/common_groups/info_common_groups_widget.cpp
@@ -1209,6 +1232,8 @@ PRIVATE
payments/payments_checkout_process.h
payments/payments_form.cpp
payments/payments_form.h
payments/payments_non_panel_process.cpp
payments/payments_non_panel_process.h
platform/linux/file_utilities_linux.cpp
platform/linux/file_utilities_linux.h
platform/linux/launcher_linux.cpp
@@ -1354,6 +1379,10 @@ PRIVATE
settings/settings_codes.h
settings/settings_common_session.cpp
settings/settings_common_session.h
settings/settings_credits.cpp
settings/settings_credits.h
settings/settings_credits_graphics.cpp
settings/settings_credits_graphics.h
settings/settings_experimental.cpp
settings/settings_experimental.h
settings/settings_folders.cpp
@@ -1445,10 +1474,14 @@ PRIVATE
ui/chat/choose_send_as.h
ui/chat/choose_theme_controller.cpp
ui/chat/choose_theme_controller.h
ui/controls/location_picker.cpp
ui/controls/location_picker.h
ui/controls/silent_toggle.cpp
ui/controls/silent_toggle.h
ui/controls/userpic_button.cpp
ui/controls/userpic_button.h
ui/effects/credits_graphics.cpp
ui/effects/credits_graphics.h
ui/effects/emoji_fly_animation.cpp
ui/effects/emoji_fly_animation.h
ui/effects/message_sending_animation_common.h
@@ -1464,6 +1497,10 @@ PRIVATE
ui/image/image_location.h
ui/image/image_location_factory.cpp
ui/image/image_location_factory.h
ui/text/format_song_document_name.cpp
ui/text/format_song_document_name.h
ui/widgets/label_with_custom_emoji.cpp
ui/widgets/label_with_custom_emoji.h
ui/countryinput.cpp
ui/countryinput.h
ui/dynamic_thumbnails.cpp
@@ -1477,10 +1514,6 @@ PRIVATE
ui/resize_area.h
ui/search_field_controller.cpp
ui/search_field_controller.h
ui/text/format_song_document_name.cpp
ui/text/format_song_document_name.h
ui/widgets/label_with_custom_emoji.cpp
ui/widgets/label_with_custom_emoji.h
ui/unread_badge.cpp
ui/unread_badge.h
window/main_window.cpp
@@ -1496,6 +1529,8 @@ PRIVATE
window/section_widget.h
window/window_adaptive.cpp
window/window_adaptive.h
window/window_chat_preview.cpp
window/window_chat_preview.h
window/window_connecting_widget.cpp
window/window_connecting_widget.h
window/window_controller.cpp
@@ -1515,6 +1550,8 @@ PRIVATE
window/window_peer_menu.cpp
window/window_peer_menu.h
window/window_section_common.h
window/window_separate_id.cpp
window/window_separate_id.h
window/window_session_controller.cpp
window/window_session_controller.h
window/window_session_controller_link_info.h
@@ -1581,6 +1618,7 @@ PRIVATE
qrc/telegram/animations.qrc
qrc/telegram/export.qrc
qrc/telegram/iv.qrc
qrc/telegram/picker.qrc
qrc/telegram/telegram.qrc
qrc/telegram/sounds.qrc
winrc/Telegram.rc
@@ -1804,12 +1842,49 @@ if (WIN32)
/DELAYLOAD:uxtheme.dll
/DELAYLOAD:crypt32.dll
/DELAYLOAD:bcrypt.dll
/DELAYLOAD:imm32.dll
/DELAYLOAD:netapi32.dll
/DELAYLOAD:imm32.dll
/DELAYLOAD:userenv.dll
/DELAYLOAD:wtsapi32.dll
/DELAYLOAD:propsys.dll
)
if (QT_VERSION GREATER 6)
if (NOT build_winarm)
target_link_options(Telegram PRIVATE
/DELAYLOAD:API-MS-Win-EventLog-Legacy-l1-1-0.dll
)
endif()
target_link_options(Telegram
PRIVATE
/DELAYLOAD:API-MS-Win-Core-Console-l1-1-0.dll
/DELAYLOAD:API-MS-Win-Core-Fibers-l2-1-0.dll
/DELAYLOAD:API-MS-Win-Core-Fibers-l2-1-1.dll
/DELAYLOAD:API-MS-Win-Core-File-l1-1-0.dll
/DELAYLOAD:API-MS-Win-Core-LibraryLoader-l1-2-0.dll
/DELAYLOAD:API-MS-Win-Core-Localization-l1-2-0.dll
/DELAYLOAD:API-MS-Win-Core-Memory-l1-1-0.dll
/DELAYLOAD:API-MS-Win-Core-Memory-l1-1-1.dll
/DELAYLOAD:API-MS-Win-Core-ProcessThreads-l1-1-0.dll
/DELAYLOAD:API-MS-Win-Core-Synch-l1-2-0.dll # Synchronization.lib
/DELAYLOAD:API-MS-Win-Core-SysInfo-l1-1-0.dll
/DELAYLOAD:API-MS-Win-Core-Timezone-l1-1-0.dll
/DELAYLOAD:API-MS-Win-Core-WinRT-l1-1-0.dll
/DELAYLOAD:API-MS-Win-Core-WinRT-Error-l1-1-0.dll
/DELAYLOAD:API-MS-Win-Core-WinRT-String-l1-1-0.dll
/DELAYLOAD:API-MS-Win-Security-CryptoAPI-l1-1-0.dll
# /DELAYLOAD:API-MS-Win-Shcore-Scaling-l1-1-1.dll # We shadowed GetDpiForMonitor
/DELAYLOAD:authz.dll # Authz.lib
/DELAYLOAD:comdlg32.dll
/DELAYLOAD:dwrite.dll # DWrite.lib
/DELAYLOAD:dxgi.dll # DXGI.lib
/DELAYLOAD:d3d9.dll # D3D9.lib
/DELAYLOAD:d3d11.dll # D3D11.lib
/DELAYLOAD:d3d12.dll # D3D12.lib
/DELAYLOAD:setupapi.dll # SetupAPI.lib
/DELAYLOAD:winhttp.dll
)
endif()
endif()
target_prepare_qrc(Telegram)

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 987 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -251,6 +251,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_caption_limit2#other" = "Make the caption shorter or subscribe to **Telegram Premium** to double the limit to **{count}** characters.";
"lng_caption_limit_reached#one" = "You've reached the media caption limit. Please make the caption shorter by {count} character.";
"lng_caption_limit_reached#other" = "You've reached the media caption limit. Please make the caption shorter by {count} characters.";
"lng_caption_move_up" = "Move Caption Up";
"lng_caption_move_down" = "Move Caption Down";
"lng_file_size_limit_title" = "File Too Large";
"lng_file_size_limit#one" = "{count} Gb";
@@ -302,6 +304,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_sure_ban_admin" = "This user is an admin. Are you sure you want to go ahead and restrict them?";
"lng_sure_enable_socks" = "Are you sure you want to enable this proxy?\n\nServer: {server}\nPort: {port}\n\nYou can change your proxy server later in the Settings (Connection Type).";
"lng_sure_enable" = "Enable";
"lng_proxy_box_title" = "Enable proxy";
"lng_proxy_box_server" = "Server";
"lng_proxy_box_port" = "Port";
"lng_proxy_box_secret" = "Secret";
"lng_proxy_box_status" = "Status";
"lng_proxy_box_username" = "Username";
"lng_proxy_box_password" = "Password";
"lng_proxy_invalid" = "The proxy link is invalid.";
"lng_proxy_unsupported" = "Your Telegram Desktop version doesn't support this proxy type or the proxy link is invalid. Please update Telegram Desktop to the latest version.";
@@ -561,6 +570,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_reaction_invoice" = "{reaction} to your invoice";
"lng_reaction_gif" = "{reaction} to your GIF";
"lng_effect_add_title" = "Add an animated effect";
"lng_effect_stickers_title" = "Effects from stickers";
"lng_effect_send" = "Send with Effect";
"lng_effect_none" = "No effects found.";
"lng_effect_premium" = "Subscribe to {link} to add this animated effect.";
"lng_effect_premium_link" = "Telegram Premium";
"lng_languages" = "Languages";
"lng_languages_none" = "No languages found.";
"lng_languages_count#one" = "{count} language";
@@ -667,6 +683,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_privacy_premium_link" = "Telegram Premium";
"lng_settings_passcode_disable" = "Disable Passcode";
"lng_settings_passcode_disable_sure" = "Are you sure you want to disable passcode?";
"lng_settings_use_winhello" = "Unlock with Windows Hello";
"lng_settings_use_winhello_about" = "You need to enter your passcode once before unlocking Telegram with Windows Hello.";
"lng_settings_use_touchid" = "Unlock with Touch ID";
"lng_settings_use_touchid_about" = "You need to enter your passcode once before unlocking Telegram with Touch ID.";
"lng_settings_use_applewatch" = "Unlock with Apple Watch";
"lng_settings_use_applewatch_about" = "You need to enter your passcode once before unlocking Telegram with Apple Watch.";
"lng_settings_use_systempwd" = "Unlock with System Password";
"lng_settings_use_systempwd_about" = "You need to enter your passcode once before unlocking Telegram with System Password.";
"lng_settings_password_disable" = "Disable Cloud Password";
"lng_settings_password_abort" = "Abort two-step verification setup";
"lng_settings_about_bio" = "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco";
@@ -769,6 +793,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_power_chat" = "Animations in Chats";
"lng_settings_power_chat_background" = "Background rotation";
"lng_settings_power_chat_spoiler" = "Animated spoiler effect";
"lng_settings_power_chat_effects" = "Effects in messages";
"lng_settings_power_calls" = "Animations in Calls";
"lng_settings_power_ui" = "Interface animations";
"lng_settings_power_auto" = "Save Power on Low Battery";
@@ -880,6 +905,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_gift_premium_users_confirm" = "Proceed";
"lng_settings_gift_premium_users_error#one" = "You can select maximum {count} user.";
"lng_settings_gift_premium_users_error#other" = "You can select maximum {count} users.";
"lng_settings_gift_premium_choose" = "Please choose at least one recipient.";
"lng_backgrounds_header" = "Choose Wallpaper";
"lng_theme_sure_keep" = "Keep this theme?";
@@ -974,6 +1000,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_passcode_ph" = "Your passcode";
"lng_passcode_submit" = "Submit";
"lng_passcode_logout" = "Log out";
"lng_passcode_winhello" = "You need to enter your passcode\nbefore you can use Windows Hello.";
"lng_passcode_touchid" = "You need to enter your passcode\nbefore you can use Touch ID.";
"lng_passcode_applewatch" = "You need to enter your passcode\nbefore you can use Watch to unlock.";
"lng_passcode_systempwd" = "You need to enter your passcode\nbefore you can use system password.";
"lng_passcode_winhello_unlock" = "Telegram wants to unlock with Windows Hello.";
"lng_passcode_touchid_unlock" = "unlock";
"lng_passcode_create_button" = "Save Passcode";
"lng_passcode_check_button" = "Submit";
"lng_passcode_change_button" = "Save Passcode";
@@ -1053,6 +1085,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_proxy_sponsor" = "Proxy sponsor";
"lng_proxy_sponsor_about" = "This channel is shown by your proxy server.\nTo remove this channel from your chats list,\ndisable the proxy in Telegram Settings.";
"lng_proxy_sponsor_warning" = "This proxy may display a sponsored channel in your chat list. This doesn't reveal any of your Telegram traffic.";
"lng_proxy_add_from_clipboard" = "Add proxy from clipboard";
"lng_proxy_add_from_clipboard_good_toast" = "Proxy was added from clipboard.";
"lng_proxy_add_from_clipboard_failed_toast" = "This is not a proxy link.";
"lng_proxy_add_from_clipboard_existing_toast" = "This proxy is already in the list.";
"lng_badge_psa_default" = "PSA";
"lng_about_psa_default" = "This message provides you with a public service announcement. To remove it from your chats list, right click it and select **Hide**.";
"lng_tooltip_psa_default" = "This message provides you with a public service announcement.";
@@ -1079,6 +1115,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_faq" = "Telegram FAQ";
"lng_settings_faq_link" = "https://telegram.org/faq#general-questions";
"lng_settings_features" = "Telegram Features";
"lng_settings_credits" = "Your Stars";
"lng_settings_logout" = "Log Out";
"lng_sure_logout" = "Are you sure you want to log out?";
@@ -1522,8 +1559,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_manage_peer_link_invite" = "Invite link";
"lng_manage_peer_link_expired" = "Expired link";
"lng_manage_private_group_title" = "Private";
"lng_manage_private_group_noforwards_title" = "Private restricted";
"lng_manage_public_group_title" = "Public";
"lng_manage_private_peer_title" = "Private";
"lng_manage_private_peer_noforwards_title" = "Private restricted";
"lng_manage_public_peer_title" = "Public";
"lng_manage_peer_send_title" = "Who can send new messages?";
"lng_manage_peer_send_only_members" = "Only members";
@@ -1537,6 +1576,7 @@ 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_edit_intro" = "Edit Intro";
"lng_manage_peer_bot_edit_commands" = "Edit Commands";
"lng_manage_peer_bot_edit_settings" = "Change Bot Settings";
@@ -1849,6 +1889,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_boost_apply#one" = "{from} boosted the group";
"lng_action_boost_apply#other" = "{from} boosted the group {count} times";
"lng_action_set_chat_intro" = "{from} added the message below for all empty chats. How?";
"lng_action_payment_refunded" = "{peer} refunded back {amount}";
"lng_similar_channels_title" = "Similar channels";
"lng_similar_channels_view_all" = "View all";
@@ -2162,6 +2203,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_premium_summary_about_translation" = "Real-time translation of channels and chats into other languages.";
"lng_premium_summary_subtitle_business" = "Telegram Business";
"lng_premium_summary_about_business" = "Upgrade your account with business features such as location, opening hours and quick replies.";
"lng_premium_summary_subtitle_effects" = "Message Effects";
"lng_premium_summary_about_effects" = "Add over 500 animated effects to private messages.";
"lng_premium_summary_bottom_subtitle" = "About Telegram Premium";
"lng_premium_summary_bottom_about" = "While the free version of Telegram already gives its users more than any other messaging application, **Telegram Premium** pushes its capabilities even further.\n\n**Telegram Premium** is a paid option, because most Premium Features require additional expenses from Telegram to third parties such as data center providers and server manufacturers. Contributions from **Telegram Premium** users allow us to cover such costs and also help Telegram stay free for everyone.";
"lng_premium_summary_button" = "Subscribe for {cost} per month";
@@ -2285,9 +2328,66 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_business_about_sponsored_link" = "Telegram Ad Platform {emoji}";
"lng_business_about_sponsored_url" = "https://ads.telegram.org";
"lng_credits_summary_title" = "Telegram Stars";
"lng_credits_summary_about" = "Buy Stars to unlock content and services in miniapps on Telegram.";
"lng_credits_summary_options_subtitle" = "Choose package";
"lng_credits_summary_options_credits#one" = "{count} Star";
"lng_credits_summary_options_credits#other" = "{count} Stars";
"lng_credits_summary_options_more" = "More Options";
"lng_credits_summary_options_about" = "By proceeding and purchasing Stars, you agree with the {link}.";
"lng_credits_summary_options_about_link" = "Terms and Conditions";
"lng_credits_summary_options_about_url" = "https://telegram.org/tos/stars";
"lng_credits_summary_history_tab_full" = "All Transactions";
"lng_credits_summary_history_tab_in" = "Incoming";
"lng_credits_summary_history_tab_out" = "Outgoing";
"lng_credits_summary_history_entry_inner_in" = "In-App Purchase";
"lng_credits_summary_balance" = "Balance";
"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**?";
"lng_credits_box_out_sure#other" = "Do you want to buy **\"{text}\"** in **{bot}** for **{count} Stars**?";
"lng_credits_box_out_media#one" = "Do you want to unlock {media} in {chat} for **{count} Star**?";
"lng_credits_box_out_media#other" = "Do you want to unlock {media} in {chat} for **{count} Stars**?";
"lng_credits_box_out_photo" = "a photo";
"lng_credits_box_out_photos#one" = "{count} photo";
"lng_credits_box_out_photos#other" = "{count} photos";
"lng_credits_box_out_video" = "a video";
"lng_credits_box_out_videos#one" = "{count} video";
"lng_credits_box_out_videos#other" = "{count} videos";
"lng_credits_box_out_both" = "{photo} and {video}";
"lng_credits_box_out_confirm#one" = "Confirm and Pay {emoji} {count} Star";
"lng_credits_box_out_confirm#other" = "Confirm and Pay {emoji} {count} Stars";
"lng_credits_box_out_about" = "Review the {link} for Stars.";
"lng_credits_media_done_title" = "Media Unlocked";
"lng_credits_media_done_text#one" = "**{count} Star** transferred to {chat}.";
"lng_credits_media_done_text#other" = "**{count} Stars** transferred to {chat}.";
"lng_credits_summary_in_toast_title" = "Stars Acquired";
"lng_credits_summary_in_toast_about#one" = "**{count}** Star added to your balance.";
"lng_credits_summary_in_toast_about#other" = "**{count}** Stars added to your balance.";
"lng_credits_box_history_entry_peer" = "Recipient";
"lng_credits_box_history_entry_peer_in" = "From";
"lng_credits_box_history_entry_via" = "Via";
"lng_credits_box_history_entry_play_market" = "Play Market";
"lng_credits_box_history_entry_app_store" = "App Store";
"lng_credits_box_history_entry_fragment" = "Fragment";
"lng_credits_box_history_entry_ads" = "Ads Platform";
"lng_credits_box_history_entry_premium_bot" = "Stars Top-Up";
"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_about" = "You can dispute this transaction {link}.";
"lng_credits_box_history_entry_about_link" = "here";
"lng_credits_small_balance_title#one" = "{count} Star Needed";
"lng_credits_small_balance_title#other" = "{count} Stars Needed";
"lng_credits_small_balance_about" = "Buy **Stars** and use them on **{bot}** and other miniapps.";
"lng_credits_purchase_blocked" = "Sorry, you can't purchase this item with Telegram Stars.";
"lng_location_title" = "Location";
"lng_location_about" = "Display the location of your business on your account.";
"lng_location_address" = "Enter Address";
"lng_location_set_map" = "Set Location on Map";
"lng_location_fallback" = "You can set your location on the map from your mobile device.";
"lng_hours_title" = "Business Hours";
@@ -2716,12 +2816,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_prizes_badge" = "x{amount}";
"lng_prizes_results_title" = "Winners Selected!";
"lng_prizes_results_title_one" = "Winner Selected!";
"lng_prizes_results_about#one" = "**{count}** winner of the {link} was randomly selected by Telegram.";
"lng_prizes_results_about#other" = "**{count}** winners of the {link} were randomly selected by Telegram.";
"lng_prizes_results_link" = "Giveaway";
"lng_prizes_results_winner" = "Winner";
"lng_prizes_results_winners" = "Winners";
"lng_prizes_results_more#one" = "and {count} more!";
"lng_prizes_results_more#other" = "and {count} more!";
"lng_prizes_results_one" = "The winner received their gift link in a private message.";
"lng_prizes_results_all" = "All winners received gift links in private messages.";
"lng_prizes_results_some" = "Some winners couldn't be selected.";
@@ -3057,6 +3160,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_bot_close_warning_sure" = "Close anyway";
"lng_bot_add_to_side_menu" = "{bot} asks your permission to be added as an option to your main menu so you can access it any time.";
"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_click_to_start" = "Click here to use this bot.";
"lng_typing" = "typing";
"lng_user_typing" = "{user} is typing";
@@ -3092,6 +3197,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_unread_bar_some" = "Unread messages";
"lng_maps_point" = "Location";
"lng_maps_select_on_map" = "Select on the Map";
"lng_maps_point_send" = "Send This Location";
"lng_maps_point_set" = "Set This Location";
"lng_maps_or_choose" = "Or choose a venue";
"lng_maps_places_in_area" = "Places in this area";
"lng_maps_no_places" = "No places found";
"lng_maps_choose_to_search" = "Choose location to see places nearby.";
"lng_maps_venues_source" = "Powered by Foursquare";
"lng_live_location" = "Live Location";
"lng_live_location_now" = "updated just now";
"lng_live_location_minutes#one" = "updated {count} minute ago";
@@ -3177,6 +3290,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_reply_msg" = "Reply";
"lng_context_quote_and_reply" = "Quote & Reply";
"lng_context_edit_msg" = "Edit";
"lng_context_add_factcheck" = "Add Fact Check";
"lng_context_edit_factcheck" = "Edit Fact Check";
"lng_context_forward_msg" = "Forward Message";
"lng_context_send_now_msg" = "Send now";
"lng_context_reschedule" = "Reschedule";
@@ -3247,6 +3362,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"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_factcheck_title" = "Fact Check";
"lng_factcheck_placeholder" = "Add Facts or Context";
"lng_factcheck_whats_this" = "what's this?";
"lng_factcheck_about" = "This clarification was provided by a fact checking agency assigned by the department of the government of your country ({country}) responsible for combatting misinformation.";
"lng_factcheck_add_done" = "Fact check added.";
"lng_factcheck_edit_done" = "Fact check edited.";
"lng_factcheck_remove_done" = "Fact check removed.";
"lng_factcheck_bottom" = "This clarification was provided by a fact checking agency assigned by the department of the government of your country ({country}) responsible for combatting misinformation.";
"lng_factcheck_links" = "Only **t.me/** links are allowed.";
"lng_paid_title" = "Paid Content";
"lng_paid_enter_cost" = "Enter Unlock Cost";
"lng_paid_cost_placeholder" = "Stars to Unlock";
"lng_paid_about" = "Users will have to transfer this amount of Stars to your channel in order to view this media. {link}";
"lng_paid_about_link" = "More about stars >";
"lng_paid_about_link_url" = "https://telegram.org/blog/telegram-stars";
"lng_paid_price" = "Unlock for {price}";
"lng_translate_show_original" = "Show Original";
"lng_translate_bar_to" = "Translate to {name}";
@@ -3368,6 +3503,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_add_contact" = "Create";
"lng_add_contact_button" = "New contact";
"lng_contacts_header" = "Contacts";
"lng_menu_not_contact" = "This number is not on Telegram";
"lng_contacts_hidden_stories" = "Hidden Stories";
"lng_contacts_stories_status#one" = "{count} story";
"lng_contacts_stories_status#other" = "{count} stories";
@@ -3513,6 +3649,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_formatting_link_text" = "Text";
"lng_formatting_link_url" = "URL";
"lng_formatting_link_create" = "Create";
"lng_formatting_code_title" = "Code Language";
"lng_formatting_code_language" = "Language for syntax highlighting.";
"lng_formatting_code_auto" = "Auto-Detect";
"lng_text_copied" = "Text copied to clipboard.";
"lng_code_copied" = "Block copied to clipboard.";
@@ -3620,6 +3759,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_payments_card_declined" = "Your card was declined.";
"lng_payments_payment_failed" = "Payment failed. Your card has not been billed.";
"lng_payments_precheckout_failed" = "The bot couldn't process your payment. Your card has not been billed.";
"lng_payments_precheckout_timeout" = "The bot didn't respond in time. Your card has not been billed.";
"lng_payments_precheckout_stars_failed" = "The bot couldn't process your payment.";
"lng_payments_precheckout_stars_timeout" = "The bot didn't respond in time.";
"lng_payments_already_paid" = "You have already paid for this item.";
"lng_payments_terms_title" = "Terms of Service";
@@ -4063,6 +4205,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_restricted_send_polls_all" = "Posting polls isn't allowed in this group.";
"lng_restricted_send_public_polls" = "Sorry, public polls can't be forwarded to channels.";
"lng_restricted_send_paid_media" = "Sorry, paid media can't be sent to this channel.";
"lng_restricted_send_voice_messages" = "{user} restricted sending of voice messages to them.";
"lng_restricted_send_video_messages" = "{user} restricted sending of video messages to them.";
@@ -5090,6 +5233,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_channel_earn_history_return" = "Refund";
"lng_channel_earn_history_return_about" = "Refunded back";
"lng_channel_earn_history_pending" = "Pending";
"lng_channel_earn_history_failed" = "Failed";
"lng_channel_earn_history_show_more#one" = "Show {count} More Transaction";
"lng_channel_earn_history_show_more#other" = "Show {count} More Transactions";
"lng_channel_earn_off" = "Switch Off Ads";
@@ -5112,6 +5256,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_channel_earn_chart_revenue" = "Ad revenue";
"lng_channel_earn_chart_overriden_detail_currency" = "Revenue in TON";
"lng_channel_earn_chart_overriden_detail_usd" = "Revenue in USD";
"lng_channel_earn_currency_history" = "TON Transactions";
"lng_channel_earn_credits_history" = "Stars Transactions";
"lng_channel_earn_out_check_password_about" = "You can withdraw only if you have:";
"lng_bot_earn_title" = "Stars Balance";
"lng_bot_earn_chart_revenue" = "Revenue";
"lng_bot_earn_overview_title" = "Proceeds overview";
"lng_bot_earn_available" = "Available balance";
"lng_bot_earn_total" = "Total lifetime proceeds";
"lng_bot_earn_balance_title" = "Available balance";
"lng_bot_earn_balance_about" = "Stars from your total balance become available for spending on ads and rewards 21 days after they are earned.";
"lng_bot_earn_balance_about_url" = "https://telegram.org/tos/stars";
"lng_bot_earn_balance_button#one" = "Withdraw {emoji} {count}";
"lng_bot_earn_balance_button#other" = "Withdraw {emoji} {count}";
"lng_bot_earn_balance_button_all" = "Withdraw all stars";
"lng_bot_earn_balance_button_locked" = "Withdraw";
"lng_bot_earn_balance_button_buy_ads" = "Buy Ads";
"lng_bot_earn_learn_credits_out_about" = "You can withdraw Stars using Fragment, or use Stars to advertise your bot. {link}";
"lng_bot_earn_out_ph" = "Enter amount to withdraw";
"lng_bot_earn_balance_password_title" = "Two-step verification";
"lng_bot_earn_balance_password_description" = "Please enter your password to collect.";
"lng_bot_earn_credits_out_minimal" = "You cannot withdraw less then {link}.";
"lng_bot_earn_credits_out_minimal_link#one" = "{count} star";
"lng_bot_earn_credits_out_minimal_link#other" = "{count} stars";
"lng_contact_add" = "Add";
"lng_contact_send_message" = "message";
@@ -5161,6 +5329,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_font_system" = "System font";
"lng_font_not_found" = "Font not found.";
"lng_search_tab_my_messages" = "My Messages";
"lng_search_tab_this_topic" = "This Topic";
"lng_search_tab_this_chat" = "This Chat";
"lng_search_tab_this_channel" = "This Channel";
"lng_search_tab_this_group" = "This Group";
"lng_search_tab_public_posts" = "Public Posts";
"lng_search_tab_no_results" = "No Results";
"lng_search_tab_no_results_text" = "There were no results for \"{query}\".";
"lng_search_tab_no_results_retry" = "Try another hashtag.";
"lng_search_tab_by_hashtag" = "Enter a hashtag to find messages containing it.";
"lng_contact_details_button" = "View Contact";
"lng_contact_details_title" = "Contact details";
"lng_contact_details_phone" = "Phone";
"lng_contact_details_phone_main" = "Main Phone";
"lng_contact_details_phone_home" = "Home Phone";
"lng_contact_details_phone_mobile" = "Mobile Phone";
"lng_contact_details_phone_work" = "Work Phone";
"lng_contact_details_phone_other" = "Other Phone";
"lng_contact_details_email" = "Email";
"lng_contact_details_address" = "Address";
"lng_contact_details_url" = "URL";
"lng_contact_details_note" = "Note";
"lng_contact_details_birthday" = "Birthday";
"lng_contact_details_organization" = "Organization";
// Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program...";

View File

@@ -0,0 +1,120 @@
:root {
--font-sans: -apple-system, BlinkMacSystemFont, avenir next, avenir, Segoe UI Variable Text, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, tahoma, arial, sans-serif;
}
html {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
body {
font-family: var(--font-sans);
width: 100%;
height: 100%;
padding: 0;
margin: 0;
background-color: var(--td-window-bg);
color: var(--td-window-fg);
}
html.custom_scroll ::-webkit-scrollbar {
border-radius: 5px !important;
border: 3px solid transparent !important;
background-color: var(--td-scroll-bg) !important;
background-clip: content-box !important;
width: 10px !important;
}
html.custom_scroll ::-webkit-scrollbar:hover {
background-color: var(--td-scroll-bg-over) !important;
}
html.custom_scroll ::-webkit-scrollbar-thumb {
border-radius: 5px !important;
border: 3px solid transparent !important;
background-color: var(--td-scroll-bar-bg) !important;
background-clip: content-box !important;
}
html.custom_scroll ::-webkit-scrollbar-thumb:hover {
background-color: var(--td-scroll-bar-bg-over) !important;
}
#map {
position: relative;
width: 100%;
height: 100%;
}
#marker {
pointer-events: none;
display: none;
z-index: 2;
position: absolute;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
}
#marker_drop {
margin-bottom: 0px;
transition: margin 160ms ease-in-out;
}
#marker_drop.moving {
margin-bottom: 24px;
}
#marker_shadow {
position: absolute;
}
#search_venues {
position: absolute;
left: 50%;
transform: translateX(-50%);
z-index: 2;
top: -30px;
transition: top 200ms ease-in-out;
}
#search_venues.shown {
top: 6px;
}
#search_venues_inner {
position: relative;
overflow: hidden;
font-size: 13px;
font-weight: 500;
background: var(--td-window-bg);
color: var(--td-window-active-text-fg);
cursor: pointer;
border-radius: 14px;
padding: 5px 12px 6px;
box-shadow: 0 0 3px 0px var(--td-history-to-down-shadow);
}
#search_venues_inner:hover {
background: var(--td-window-bg-over);
}
#search_venues_content {
position: relative;
z-index: 2;
}
#search_venues_content:before {
content: var(--td-lng-maps-places-in-area);
}
#search_venues_inner .ripple .inner {
position: absolute;
border-radius: 50%;
transform: scale(0);
opacity: 1;
animation: ripple 650ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
background-color: var(--td-window-bg-ripple);
}
#search_venues_inner .ripple.hiding {
animation: fadeOut 200ms linear forwards;
}
@keyframes ripple {
to {
transform: scale(2);
}
}
@keyframes fadeOut {
to {
opacity: 0;
}
}

View File

@@ -0,0 +1,199 @@
var LocationPicker = {
startZoom: 14,
flySpeed: 2.4,
notify: function(message) {
if (window.external && window.external.invoke) {
window.external.invoke(JSON.stringify(message));
}
},
frameKeyDown: function (e) {
const keyW = (e.key === 'w')
|| (e.code === 'KeyW')
|| (e.keyCode === 87);
const keyQ = (e.key === 'q')
|| (e.code === 'KeyQ')
|| (e.keyCode === 81);
const keyM = (e.key === 'm')
|| (e.code === 'KeyM')
|| (e.keyCode === 77);
if ((e.metaKey || e.ctrlKey) && (keyW || keyQ || keyM)) {
e.preventDefault();
LocationPicker.notify({
event: 'keydown',
modifier: e.ctrlKey ? 'ctrl' : 'cmd',
key: keyW ? 'w' : keyQ ? 'q' : 'm',
});
} else if (e.key === 'Escape' || e.keyCode === 27) {
e.preventDefault();
LocationPicker.notify({
event: 'keydown',
key: 'escape',
});
}
},
isNight: function() {
var html = document.getElementsByTagName('html')[0];
return html.style.getPropertyValue('--td-night') == '1';
},
lightPreset: function() {
return LocationPicker.isNight() ? 'night' : 'day';
},
updateStyles: function (styles) {
if (LocationPicker.styles !== styles) {
LocationPicker.styles = styles;
document.getElementsByTagName('html')[0].style = styles;
LocationPicker.map.setConfigProperty(
'basemap',
'lightPreset',
LocationPicker.lightPreset());
}
},
init: function (params) {
mapboxgl.accessToken = params.token;
if (params.protocol) {
mapboxgl.config.API_URL = params.protocol + '://domain/api.mapbox.com';
}
var options = { container: 'map', config: {
basemap: { lightPreset: LocationPicker.lightPreset() }
} };
var center = params.center;
if (center) {
center = [center[1], center[0]];
options.center = center;
options.zoom = LocationPicker.startZoom;
} else if (params.bounds) {
options.bounds = params.bounds;
center = new mapboxgl.LngLatBounds(params.bounds).getCenter();
} else {
center = [0, 0];
}
LocationPicker.map = new mapboxgl.Map(options);
LocationPicker.createMarker(center);
LocationPicker.trackMovement();
LocationPicker.initSearchVenueRipple();
},
marker: function() {
return document.getElementById('marker_drop');
},
createMarker: function(center) {
document.getElementById('marker').style.display = 'flex';
},
clearMovingTimer: function() {
if (LocationPicker.clearMovingTimeoutId) {
clearTimeout(LocationPicker.clearMovingTimeoutId);
LocationPicker.clearMovingTimeoutId = 0;
}
},
startMovingTimer: function(done) {
LocationPicker.clearMovingTimer();
LocationPicker.clearMovingTimeoutId = setTimeout(done, 500);
},
trackMovement: function() {
LocationPicker.map.on('movestart', function() {
LocationPicker.marker().classList.add('moving');
LocationPicker.clearMovingTimer();
LocationPicker.toggleSearchVenues(false);
LocationPicker.notify({ event: 'move_start' });
});
LocationPicker.map.on('moveend', function() {
LocationPicker.startMovingTimer(function() {
LocationPicker.marker().classList.remove('moving');
LocationPicker.notify({
event: 'move_end',
latitude: LocationPicker.map.getCenter().lat,
longitude: LocationPicker.map.getCenter().lng
});
});
});
},
narrowTo: function (point) {
LocationPicker.map.flyTo({
center: [point[1], point[0]],
zoom: LocationPicker.startZoom,
speed: LocationPicker.flySpeed,
});
},
send: function () {
LocationPicker.notify({
event: 'send',
latitude: LocationPicker.map.getCenter().lat,
longitude: LocationPicker.map.getCenter().lng
});
},
addRipple: function (button, x, y) {
const ripple = document.createElement('span');
ripple.classList.add('ripple');
const inner = document.createElement('span');
inner.classList.add('inner');
var rect = button.getBoundingClientRect();
x -= rect.x;
y -= rect.y;
const mx = button.clientWidth - x;
const my = button.clientHeight - y;
const sq1 = x * x + y * y;
const sq2 = mx * mx + y * y;
const sq3 = x * x + my * my;
const sq4 = mx * mx + my * my;
const radius = Math.sqrt(Math.max(sq1, sq2, sq3, sq4));
inner.style.width = inner.style.height = `${2 * radius}px`;
inner.style.left = `${x - radius}px`;
inner.style.top = `${y - radius}px`;
inner.classList.add('inner');
ripple.addEventListener('animationend', function (e) {
if (e.animationName === 'fadeOut') {
ripple.remove();
}
});
ripple.appendChild(inner);
button.appendChild(ripple);
},
stopRipples: function (button) {
const id = button.id ? button.id : button;
button = document.getElementById(id);
const ripples = button.getElementsByClassName('ripple');
for (var i = 0; i < ripples.length; ++i) {
const ripple = ripples[i];
if (!ripple.classList.contains('hiding')) {
ripple.classList.add('hiding');
}
}
},
initSearchVenueRipple: function() {
var button = document.getElementById('search_venues_inner');
button.addEventListener('mousedown', function (e) {
LocationPicker.addRipple(e.currentTarget, e.clientX, e.clientY);
LocationPicker.searchVenuesPressed = true;
});
button.addEventListener('mouseup', function (e) {
const id = e.currentTarget.id;
setTimeout(function () {
LocationPicker.stopRipples(id);
}, 0);
if (LocationPicker.searchVenuesPressed) {
LocationPicker.searchVenuesPressed = false;
LocationPicker.toggleSearchVenues(false);
LocationPicker.notify({
event: 'search_venues',
latitude: LocationPicker.map.getCenter().lat,
longitude: LocationPicker.map.getCenter().lng
});
}
});
button.addEventListener('mouseleave', function (e) {
LocationPicker.stopRipples(e.currentTarget);
LocationPicker.searchVenuesPressed = false;
});
},
toggleSearchVenues: function(shown) {
var button = document.getElementById('search_venues');
button.classList.toggle('shown', shown);
},
};

View File

@@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/picker">
<file alias="picker.css">../../picker_html/picker.css</file>
<file alias="picker.js">../../picker_html/picker.js</file>
</qresource>
</RCC>

View File

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

View File

@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 5,0,3,0
PRODUCTVERSION 5,0,3,0
FILEVERSION 5,2,6,0
PRODUCTVERSION 5,2,6,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.0.3.0"
VALUE "FileVersion", "5.2.6.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "5.0.3.0"
VALUE "ProductVersion", "5.2.6.0"
END
END
BLOCK "VarFileInfo"

View File

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

View File

@@ -155,6 +155,7 @@ int main(int argc, char *argv[])
QString remove;
int version = 0;
[[maybe_unused]] bool targetwin64 = false;
[[maybe_unused]] bool targetwinarm = false;
[[maybe_unused]] bool targetarmac = false;
QFileInfoList files;
for (int i = 0; i < argc; ++i) {
@@ -165,6 +166,7 @@ int main(int argc, char *argv[])
if (remove.isEmpty()) remove = info.canonicalPath() + "/";
} else if (string("-target") == argv[i] && i + 1 < argc) {
targetwin64 = (string("win64") == argv[i + 1]);
targetwinarm = (string("winarm") == argv[i + 1]);
} else if (string("-arch") == argv[i] && i + 1 < argc) {
targetarmac = (string("arm64") == argv[i + 1]);
if (!targetarmac && string("x86_64") != argv[i + 1]) {
@@ -493,7 +495,7 @@ int main(int argc, char *argv[])
cout << "Signature verified!\n";
RSA_free(pbKey);
#ifdef Q_OS_WIN
QString outName((targetwin64 ? QString("tx64upd%1") : QString("tupdate%1")).arg(AlphaVersion ? AlphaVersion : version));
QString outName((targetwinarm ? QString("tarm64upd%1") : targetwin64 ? QString("tx64upd%1") : QString("tupdate%1")).arg(AlphaVersion ? AlphaVersion : version));
#elif defined Q_OS_MAC
QString outName((targetarmac ? QString("tarmacupd%1") : QString("tmacupd%1")).arg(AlphaVersion ? AlphaVersion : version));
#else

View File

@@ -571,8 +571,8 @@ void _generateDump(EXCEPTION_POINTERS* pExceptionPointers) {
}
if (!hDumpFile || hDumpFile == INVALID_HANDLE_VALUE) {
WCHAR wstrPath[maxFileLen];
DWORD wstrPathLen;
if (wstrPathLen = GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen)) {
DWORD wstrPathLen = GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen);
if (wstrPathLen) {
wsprintf(wstrPath + wstrPathLen, L"\\%s\\", _programName);
hDumpFile = _generateDumpFileAtPath(wstrPath);
}

View File

@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item_components.h"
#include "inline_bots/bot_attach_web_view.h"
#include "payments/payments_checkout_process.h"
#include "payments/payments_non_panel_process.h"
#include "main/main_session.h"
#include "mainwidget.h"
#include "mainwindow.h"
@@ -126,11 +127,7 @@ void SendBotCallbackData(
UrlClickHandler::Open(link);
return;
}
const auto scoreLink = AppendShareGameScoreUrl(
session,
link,
item->fullId());
BotGameUrlClickHandler(bot, scoreLink).onClick({
BotGameUrlClickHandler(bot, link).onClick({
Qt::LeftButton,
QVariant::fromValue(ClickHandlerContext{
.itemId = item->fullId(),
@@ -335,7 +332,8 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
Payments::Mode::Payment,
crl::guard(controller, [=](auto) {
controller->widget()->activate();
}));
}),
Payments::ProcessNonPanelPaymentFormFactory(controller, item));
} break;
case ButtonType::Url: {
@@ -490,20 +488,23 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
case ButtonType::WebView: {
if (const auto bot = item->getMessageBot()) {
bot->session().attachWebView().request(
controller,
Api::SendAction(bot->owner().history(bot)),
bot,
{ .text = button->text, .url = button->data });
bot->session().attachWebView().open({
.bot = bot,
.context = { .controller = controller },
.button = { .text = button->text, .url = button->data },
.source = InlineBots::WebViewSourceButton{ .simple = false },
});
}
} break;
case ButtonType::SimpleWebView: {
if (const auto bot = item->getMessageBot()) {
bot->session().attachWebView().requestSimple(
controller,
bot,
{ .text = button->text, .url = button->data });
bot->session().attachWebView().open({
.bot = bot,
.context = { .controller = controller },
.button = {.text = button->text, .url = button->data },
.source = InlineBots::WebViewSourceButton{ .simple = true },
});
}
} break;
}

View File

@@ -20,13 +20,20 @@ namespace Api {
inline constexpr auto kScheduledUntilOnlineTimestamp = TimeId(0x7FFFFFFE);
struct SendOptions {
uint64 price = 0;
PeerData *sendAs = nullptr;
TimeId scheduled = 0;
BusinessShortcutId shortcutId = 0;
EffectId effectId = 0;
bool silent = false;
bool handleSupportSwitch = false;
bool invertCaption = false;
bool hideViaBot = false;
crl::time ttlSeconds = 0;
friend inline bool operator==(
const SendOptions &,
const SendOptions &) = default;
};
[[nodiscard]] SendOptions DefaultSendWhenOnlineOptions();
@@ -49,6 +56,10 @@ struct SendAction {
MsgId replaceMediaOf = 0;
[[nodiscard]] MTPInputReplyTo mtpReplyTo() const;
friend inline bool operator==(
const SendAction &,
const SendAction &) = default;
};
struct MessageToSend {

View File

@@ -97,8 +97,10 @@ void ConfirmPhone::resolve(
box->resendRequests(
) | rpl::start_with_next([=] {
_api.request(MTPauth_ResendCode(
MTP_flags(0),
MTP_string(phone),
MTP_string(phoneHash)
MTP_string(phoneHash),
MTPstring() // reason
)).done([=] {
if (boxWeak) {
boxWeak->callDone();

View File

@@ -0,0 +1,306 @@
/*
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_credits.h"
#include "api/api_statistics_data_deserialize.h"
#include "api/api_updates.h"
#include "apiwrap.h"
#include "base/unixtime.h"
#include "data/data_channel.h"
#include "data/data_document.h"
#include "data/data_peer.h"
#include "data/data_photo.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
namespace Api {
namespace {
constexpr auto kTransactionsLimit = 100;
[[nodiscard]] Data::CreditsHistoryEntry HistoryFromTL(
const MTPStarsTransaction &tl,
not_null<PeerData*> peer) {
using HistoryPeerTL = MTPDstarsTransactionPeer;
using namespace Data;
const auto owner = &peer->owner();
const auto photo = tl.data().vphoto()
? owner->photoFromWeb(*tl.data().vphoto(), ImageLocation())
: nullptr;
auto extended = std::vector<CreditsHistoryMedia>();
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()) {
const auto photo = owner->processPhoto(*inner);
if (!photo->isNull()) {
extended.push_back(CreditsHistoryMedia{
.type = CreditsHistoryMediaType::Photo,
.id = photo->id,
});
}
}
}, [&](const MTPDmessageMediaDocument &document) {
if (const auto inner = document.vdocument()) {
const auto document = owner->processDocument(*inner);
if (document->isAnimation()
|| document->isVideoFile()
|| document->isGifv()) {
extended.push_back(CreditsHistoryMedia{
.type = CreditsHistoryMediaType::Video,
.id = document->id,
});
}
}
}, [&](const auto &) {});
}
}
const auto barePeerId = tl.data().vpeer().match([](
const HistoryPeerTL &p) {
return peerFromMTP(p.vpeer());
}, [](const auto &) {
return PeerId(0);
}).value;
return Data::CreditsHistoryEntry{
.id = qs(tl.data().vid()),
.title = qs(tl.data().vtitle().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,
.peerType = tl.data().vpeer().match([](const HistoryPeerTL &) {
return Data::CreditsHistoryEntry::PeerType::Peer;
}, [](const MTPDstarsTransactionPeerPlayMarket &) {
return Data::CreditsHistoryEntry::PeerType::PlayMarket;
}, [](const MTPDstarsTransactionPeerFragment &) {
return Data::CreditsHistoryEntry::PeerType::Fragment;
}, [](const MTPDstarsTransactionPeerAppStore &) {
return Data::CreditsHistoryEntry::PeerType::AppStore;
}, [](const MTPDstarsTransactionPeerUnsupported &) {
return Data::CreditsHistoryEntry::PeerType::Unsupported;
}, [](const MTPDstarsTransactionPeerPremiumBot &) {
return Data::CreditsHistoryEntry::PeerType::PremiumBot;
}, [](const MTPDstarsTransactionPeerAds &) {
return Data::CreditsHistoryEntry::PeerType::Ads;
}),
.refunded = tl.data().is_refund(),
.pending = tl.data().is_pending(),
.failed = tl.data().is_failed(),
.successDate = tl.data().vtransaction_date()
? base::unixtime::parse(tl.data().vtransaction_date()->v)
: QDateTime(),
.successLink = qs(tl.data().vtransaction_url().value_or_empty()),
.in = (int64(tl.data().vstars().v) >= 0),
};
}
[[nodiscard]] Data::CreditsStatusSlice StatusFromTL(
const MTPpayments_StarsStatus &status,
not_null<PeerData*> peer) {
peer->owner().processUsers(status.data().vusers());
peer->owner().processChats(status.data().vchats());
return Data::CreditsStatusSlice{
.list = ranges::views::all(
status.data().vhistory().v
) | ranges::views::transform([&](const MTPStarsTransaction &tl) {
return HistoryFromTL(tl, peer);
}) | ranges::to_vector,
.balance = status.data().vbalance().v,
.allLoaded = !status.data().vnext_offset().has_value(),
.token = qs(status.data().vnext_offset().value_or_empty()),
};
}
} // namespace
CreditsTopupOptions::CreditsTopupOptions(not_null<PeerData*> peer)
: _peer(peer)
, _api(&peer->session().api().instance()) {
}
rpl::producer<rpl::no_value, QString> CreditsTopupOptions::request() {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
using TLOption = MTPStarsTopupOption;
_api.request(MTPpayments_GetStarsTopupOptions(
)).done([=](const MTPVector<TLOption> &result) {
_options = ranges::views::all(
result.v
) | ranges::views::transform([](const TLOption &option) {
return Data::CreditTopupOption{
.credits = option.data().vstars().v,
.product = qs(
option.data().vstore_product().value_or_empty()),
.currency = qs(option.data().vcurrency()),
.amount = option.data().vamount().v,
.extended = option.data().is_extended(),
};
}) | ranges::to_vector;
consumer.put_done();
}).fail([=](const MTP::Error &error) {
consumer.put_error_copy(error.type());
}).send();
return lifetime;
};
}
CreditsStatus::CreditsStatus(not_null<PeerData*> peer)
: _peer(peer)
, _api(&peer->session().api().instance()) {
}
void CreditsStatus::request(
const Data::CreditsStatusSlice::OffsetToken &token,
Fn<void(Data::CreditsStatusSlice)> done) {
if (_requestId) {
return;
}
using TLResult = MTPpayments_StarsStatus;
_requestId = _api.request(MTPpayments_GetStarsStatus(
_peer->isSelf() ? MTP_inputPeerSelf() : _peer->input
)).done([=](const TLResult &result) {
_requestId = 0;
done(StatusFromTL(result, _peer));
}).fail([=] {
_requestId = 0;
done({});
}).send();
}
CreditsHistory::CreditsHistory(not_null<PeerData*> peer, bool in, bool out)
: _peer(peer)
, _flags((in == out)
? HistoryTL::Flags(0)
: HistoryTL::Flags(0)
| (in ? HistoryTL::Flag::f_inbound : HistoryTL::Flags(0))
| (out ? HistoryTL::Flag::f_outbound : HistoryTL::Flags(0)))
, _api(&peer->session().api().instance()) {
}
void CreditsHistory::request(
const Data::CreditsStatusSlice::OffsetToken &token,
Fn<void(Data::CreditsStatusSlice)> done) {
if (_requestId) {
return;
}
_requestId = _api.request(MTPpayments_GetStarsTransactions(
MTP_flags(_flags),
_peer->isSelf() ? MTP_inputPeerSelf() : _peer->input,
MTP_string(token),
MTP_int(kTransactionsLimit)
)).done([=](const MTPpayments_StarsStatus &result) {
_requestId = 0;
done(StatusFromTL(result, _peer));
}).fail([=] {
_requestId = 0;
done({});
}).send();
}
Data::CreditTopupOptions CreditsTopupOptions::options() const {
return _options;
}
rpl::producer<not_null<PeerData*>> PremiumPeerBot(
not_null<Main::Session*> session) {
const auto username = session->appConfig().get<QString>(
u"premium_bot_username"_q,
QString());
if (username.isEmpty()) {
return rpl::never<not_null<PeerData*>>();
}
if (const auto p = session->data().peerByUsername(username)) {
return rpl::single<not_null<PeerData*>>(p);
}
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
const auto api = lifetime.make_state<MTP::Sender>(&session->mtp());
api->request(MTPcontacts_ResolveUsername(
MTP_string(username)
)).done([=](const MTPcontacts_ResolvedPeer &result) {
session->data().processUsers(result.data().vusers());
session->data().processChats(result.data().vchats());
const auto botPeer = session->data().peerLoaded(
peerFromMTP(result.data().vpeer()));
if (!botPeer) {
return consumer.put_done();
}
consumer.put_next(not_null{ botPeer });
}).send();
return lifetime;
};
}
CreditsEarnStatistics::CreditsEarnStatistics(not_null<PeerData*> peer)
: StatisticsRequestSender(peer)
, _isUser(peer->isUser()) {
}
rpl::producer<rpl::no_value, QString> CreditsEarnStatistics::request() {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
const auto finish = [=](const QString &url) {
makeRequest(MTPpayments_GetStarsRevenueStats(
MTP_flags(0),
(_isUser ? user()->input : channel()->input)
)).done([=](const MTPpayments_StarsRevenueStats &result) {
const auto &data = result.data();
const auto &status = data.vstatus().data();
_data = Data::CreditsEarnStatistics{
.revenueGraph = StatisticalGraphFromTL(
data.vrevenue_graph()),
.currentBalance = status.vcurrent_balance().v,
.availableBalance = status.vavailable_balance().v,
.overallRevenue = status.voverall_revenue().v,
.usdRate = data.vusd_rate().v,
.isWithdrawalEnabled = status.is_withdrawal_enabled(),
.nextWithdrawalAt = status.vnext_withdrawal_at()
? base::unixtime::parse(
status.vnext_withdrawal_at()->v)
: QDateTime(),
.buyAdsUrl = url,
};
consumer.put_done();
}).fail([=](const MTP::Error &error) {
consumer.put_error_copy(error.type());
}).send();
};
makeRequest(
MTPpayments_GetStarsRevenueAdsAccountUrl(
(_isUser ? user()->input : channel()->input))
).done([=](const MTPpayments_StarsRevenueAdsAccountUrl &result) {
finish(qs(result.data().vurl()));
}).fail([=](const MTP::Error &error) {
finish({});
}).send();
return lifetime;
};
}
Data::CreditsEarnStatistics CreditsEarnStatistics::data() const {
return _data;
}
} // namespace Api

View File

@@ -0,0 +1,93 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "api/api_statistics_sender.h"
#include "data/data_credits.h"
#include "data/data_credits_earn.h"
#include "mtproto/sender.h"
namespace Main {
class Session;
} // namespace Main
class UserData;
namespace Api {
class CreditsTopupOptions final {
public:
CreditsTopupOptions(not_null<PeerData*> peer);
[[nodiscard]] rpl::producer<rpl::no_value, QString> request();
[[nodiscard]] Data::CreditTopupOptions options() const;
private:
const not_null<PeerData*> _peer;
Data::CreditTopupOptions _options;
MTP::Sender _api;
};
class CreditsStatus final {
public:
CreditsStatus(not_null<PeerData*> peer);
void request(
const Data::CreditsStatusSlice::OffsetToken &token,
Fn<void(Data::CreditsStatusSlice)> done);
private:
const not_null<PeerData*> _peer;
mtpRequestId _requestId = 0;
MTP::Sender _api;
};
class CreditsHistory final {
public:
CreditsHistory(not_null<PeerData*> peer, bool in, bool out);
void request(
const Data::CreditsStatusSlice::OffsetToken &token,
Fn<void(Data::CreditsStatusSlice)> done);
private:
using HistoryTL = MTPpayments_GetStarsTransactions;
const not_null<PeerData*> _peer;
const HistoryTL::Flags _flags;
mtpRequestId _requestId = 0;
MTP::Sender _api;
};
class CreditsEarnStatistics final : public StatisticsRequestSender {
public:
explicit CreditsEarnStatistics(not_null<PeerData*>);
[[nodiscard]] rpl::producer<rpl::no_value, QString> request();
[[nodiscard]] Data::CreditsEarnStatistics data() const;
private:
Data::CreditsEarnStatistics _data;
bool _isUser = false;
mtpRequestId _requestId = 0;
};
[[nodiscard]] rpl::producer<not_null<PeerData*>> PremiumPeerBot(
not_null<Main::Session*> session);
} // namespace Api

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_cloud_password.h"
#include "apiwrap.h"
#include "ui/layers/generic_box.h"
#include "boxes/passcode_box.h"
#include "data/data_channel.h"
#include "data/data_session.h"
@@ -34,22 +35,33 @@ void RestrictSponsored(
}
void HandleWithdrawalButton(
not_null<ChannelData*> channel,
RewardReceiver receiver,
not_null<Ui::RippleButton*> button,
std::shared_ptr<Ui::Show> show) {
Expects(receiver.currencyReceiver
|| (receiver.creditsReceiver && receiver.creditsAmount));
struct State {
rpl::lifetime lifetime;
bool loading = false;
};
const auto channel = receiver.currencyReceiver;
const auto peer = receiver.creditsReceiver;
const auto state = button->lifetime().make_state<State>();
const auto session = &channel->session();
const auto session = (channel ? &channel->session() : &peer->session());
using ChannelOutUrl = MTPstats_BroadcastRevenueWithdrawalUrl;
using CreditsOutUrl = MTPpayments_StarsRevenueWithdrawalUrl;
session->api().cloudPassword().reload();
button->setClickedCallback([=] {
const auto processOut = [=] {
if (state->loading) {
return;
}
if (peer && !receiver.creditsAmount()) {
return;
}
state->loading = true;
state->lifetime = session->api().cloudPassword().state(
) | rpl::take(
@@ -58,10 +70,12 @@ void HandleWithdrawalButton(
state->loading = false;
auto fields = PasscodeBox::CloudFields::From(pass);
fields.customTitle
= tr::lng_channel_earn_balance_password_title();
fields.customDescription
= tr::lng_channel_earn_balance_password_description(tr::now);
fields.customTitle = channel
? tr::lng_channel_earn_balance_password_title()
: tr::lng_bot_earn_balance_password_title();
fields.customDescription = channel
? tr::lng_channel_earn_balance_password_description(tr::now)
: tr::lng_bot_earn_balance_password_description(tr::now);
fields.customSubmitButton = tr::lng_passcode_submit();
fields.customCheckCallback = crl::guard(button, [=](
const Core::CloudPasswordResult &result,
@@ -74,22 +88,63 @@ void HandleWithdrawalButton(
}
}
};
const auto fail = [=](const QString &error) {
show->showToast(error);
const auto fail = [=](const MTP::Error &error) {
show->showToast(error.type());
};
session->api().request(
MTPstats_GetBroadcastRevenueWithdrawalUrl(
channel->inputChannel,
result.result
)).done([=](const MTPstats_BroadcastRevenueWithdrawalUrl &r) {
done(qs(r.data().vurl()));
}).fail([=](const MTP::Error &error) {
fail(error.type());
}).send();
if (channel) {
session->api().request(
MTPstats_GetBroadcastRevenueWithdrawalUrl(
channel->inputChannel,
result.result
)).done([=](const ChannelOutUrl &r) {
done(qs(r.data().vurl()));
}).fail(fail).send();
} else if (peer) {
session->api().request(
MTPpayments_GetStarsRevenueWithdrawalUrl(
peer->input,
MTP_long(receiver.creditsAmount()),
result.result
)).done([=](const CreditsOutUrl &r) {
done(qs(r.data().vurl()));
}).fail(fail).send();
}
});
show->show(Box<PasscodeBox>(session, fields));
});
};
button->setClickedCallback([=] {
if (state->loading) {
return;
}
const auto fail = [=](const MTP::Error &error) {
auto box = PrePasswordErrorBox(
error.type(),
session,
TextWithEntities{
tr::lng_channel_earn_out_check_password_about(tr::now),
});
if (box) {
show->show(std::move(box));
state->loading = false;
} else {
processOut();
}
};
if (channel) {
session->api().request(
MTPstats_GetBroadcastRevenueWithdrawalUrl(
channel->inputChannel,
MTP_inputCheckPasswordEmpty()
)).fail(fail).send();
} else if (peer) {
session->api().request(
MTPpayments_GetStarsRevenueWithdrawalUrl(
peer->input,
MTP_long(std::numeric_limits<int64_t>::max()),
MTP_inputCheckPasswordEmpty()
)).fail(fail).send();
}
});
}

View File

@@ -21,8 +21,14 @@ void RestrictSponsored(
bool restricted,
Fn<void(QString)> failed);
struct RewardReceiver final {
ChannelData *currencyReceiver = nullptr;
PeerData *creditsReceiver = nullptr;
Fn<uint64()> creditsAmount;
};
void HandleWithdrawalButton(
not_null<ChannelData*> channel,
RewardReceiver receiver,
not_null<Ui::RippleButton*> button,
std::shared_ptr<Ui::Show> show);

View File

@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_histories.h"
#include "data/data_session.h"
#include "data/data_web_page.h"
#include "history/view/controls/history_view_compose_media_edit_manager.h"
#include "history/history.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
@@ -81,7 +82,8 @@ mtpRequestId EditMessage(
| ((!webpage.removed && !webpage.url.isEmpty())
? MTPmessages_EditMessage::Flag::f_media
: emptyFlag)
| ((!webpage.removed && !webpage.url.isEmpty() && webpage.invert)
| (((!webpage.removed && !webpage.url.isEmpty() && webpage.invert)
|| options.invertCaption)
? MTPmessages_EditMessage::Flag::f_invert_media
: emptyFlag)
| (!sentEntities.v.isEmpty()
@@ -151,9 +153,7 @@ mtpRequestId EditMessage(
const auto &text = item->originalText();
const auto webpage = (!item->media() || !item->media()->webpage())
? Data::WebPageDraft{ .removed = true }
: Data::WebPageDraft{
.id = item->media()->webpage()->id,
};
: Data::WebPageDraft::FromItem(item);
return EditMessage(
item,
text,
@@ -203,6 +203,7 @@ void RescheduleMessage(
not_null<HistoryItem*> item,
SendOptions options) {
const auto empty = [] {};
options.invertCaption = item->invertMedia();
EditMessage(item, options, empty, empty);
}
@@ -254,85 +255,85 @@ mtpRequestId EditTextMessage(
SendOptions options,
Fn<void(mtpRequestId requestId)> done,
Fn<void(const QString &error, mtpRequestId requestId)> fail,
std::optional<bool> spoilerMediaOverride) {
if (spoilerMediaOverride) {
const auto spoiler = *spoilerMediaOverride;
if (const auto media = item->media()) {
auto takeInputMedia = Fn<std::optional<MTPInputMedia>()>(nullptr);
auto takeFileReference = Fn<QByteArray()>(nullptr);
if (const auto photo = media->photo()) {
using Flag = MTPDinputMediaPhoto::Flag;
const auto flags = Flag()
| (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag())
| (spoiler ? Flag::f_spoiler : Flag());
takeInputMedia = [=] {
return MTP_inputMediaPhoto(
MTP_flags(flags),
photo->mtpInput(),
MTP_int(media->ttlSeconds()));
};
takeFileReference = [=] { return photo->fileReference(); };
} else if (const auto document = media->document()) {
using Flag = MTPDinputMediaDocument::Flag;
const auto flags = Flag()
| (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag())
| (spoiler ? Flag::f_spoiler : Flag());
takeInputMedia = [=] {
return MTP_inputMediaDocument(
MTP_flags(flags),
document->mtpInput(),
MTP_int(media->ttlSeconds()),
MTPstring()); // query
};
takeFileReference = [=] { return document->fileReference(); };
}
const auto usedFileReference = takeFileReference
? takeFileReference()
: QByteArray();
const auto origin = item->fullId();
const auto api = &item->history()->session().api();
const auto performRequest = [=](
const auto &repeatRequest,
mtpRequestId originalRequestId) -> mtpRequestId {
const auto handleReference = [=](
const QString &error,
mtpRequestId requestId) {
if (error.startsWith(u"FILE_REFERENCE_"_q)) {
api->refreshFileReference(origin, [=](const auto &) {
if (takeFileReference &&
(takeFileReference() != usedFileReference)) {
repeatRequest(
repeatRequest,
originalRequestId
? originalRequestId
: requestId);
} else {
fail(error, requestId);
}
});
} else {
fail(error, requestId);
}
};
const auto callback = [=](
Fn<void()> applyUpdates,
mtpRequestId requestId) {
applyUpdates();
done(originalRequestId ? originalRequestId : requestId);
};
const auto requestId = EditMessage(
item,
caption,
webpage,
options,
callback,
handleReference,
takeInputMedia ? takeInputMedia() : std::nullopt);
return originalRequestId ? originalRequestId : requestId;
bool spoilered) {
const auto media = item->media();
if (media
&& HistoryView::MediaEditManager::CanBeSpoilered(item)
&& spoilered != media->hasSpoiler()) {
auto takeInputMedia = Fn<std::optional<MTPInputMedia>()>(nullptr);
auto takeFileReference = Fn<QByteArray()>(nullptr);
if (const auto photo = media->photo()) {
using Flag = MTPDinputMediaPhoto::Flag;
const auto flags = Flag()
| (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag())
| (spoilered ? Flag::f_spoiler : Flag());
takeInputMedia = [=] {
return MTP_inputMediaPhoto(
MTP_flags(flags),
photo->mtpInput(),
MTP_int(media->ttlSeconds()));
};
return performRequest(performRequest, 0);
takeFileReference = [=] { return photo->fileReference(); };
} else if (const auto document = media->document()) {
using Flag = MTPDinputMediaDocument::Flag;
const auto flags = Flag()
| (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag())
| (spoilered ? Flag::f_spoiler : Flag());
takeInputMedia = [=] {
return MTP_inputMediaDocument(
MTP_flags(flags),
document->mtpInput(),
MTP_int(media->ttlSeconds()),
MTPstring()); // query
};
takeFileReference = [=] { return document->fileReference(); };
}
const auto usedFileReference = takeFileReference
? takeFileReference()
: QByteArray();
const auto origin = item->fullId();
const auto api = &item->history()->session().api();
const auto performRequest = [=](
const auto &repeatRequest,
mtpRequestId originalRequestId) -> mtpRequestId {
const auto handleReference = [=](
const QString &error,
mtpRequestId requestId) {
if (error.startsWith(u"FILE_REFERENCE_"_q)) {
api->refreshFileReference(origin, [=](const auto &) {
if (takeFileReference &&
(takeFileReference() != usedFileReference)) {
repeatRequest(
repeatRequest,
originalRequestId
? originalRequestId
: requestId);
} else {
fail(error, requestId);
}
});
} else {
fail(error, requestId);
}
};
const auto callback = [=](
Fn<void()> applyUpdates,
mtpRequestId requestId) {
applyUpdates();
done(originalRequestId ? originalRequestId : requestId);
};
const auto requestId = EditMessage(
item,
caption,
webpage,
options,
callback,
handleReference,
takeInputMedia ? takeInputMedia() : std::nullopt);
return originalRequestId ? originalRequestId : requestId;
};
return performRequest(performRequest, 0);
}
const auto callback = [=](Fn<void()> applyUpdates, mtpRequestId id) {

View File

@@ -56,6 +56,6 @@ mtpRequestId EditTextMessage(
SendOptions options,
Fn<void(mtpRequestId requestId)> done,
Fn<void(const QString &error, mtpRequestId requestId)> fail,
std::optional<bool> spoilerMediaOverride);
bool spoilered);
} // namespace Api

View File

@@ -0,0 +1,27 @@
/*
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 Api {
template <typename Type>
void PerformForUpdate(
const MTPUpdates &updates,
Fn<void(const Type &)> callback) {
updates.match([&](const MTPDupdates &updates) {
for (const auto &update : updates.vupdates().v) {
update.match([&](const Type &d) {
callback(d);
}, [](const auto &) {
});
}
}, [](const auto &) {
});
}
} // namespace Api

View File

@@ -68,6 +68,9 @@ void Polls::create(
if (action.options.shortcutId) {
sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
}
if (action.options.effectId) {
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
}
const auto sendAs = action.options.sendAs;
if (sendAs) {
sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
@@ -89,7 +92,8 @@ void Polls::create(
MTPVector<MTPMessageEntity>(),
MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, action.options.shortcutId)
Data::ShortcutIdToMTP(_session, action.options.shortcutId),
MTP_long(action.options.effectId)
), [=](const MTPUpdates &result, const MTP::Response &response) {
if (clearCloudDraft) {
history->finishSavingCloudDraft(

View File

@@ -62,6 +62,79 @@ void InnerFillMessagePostFlags(
}
}
void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) {
const auto history = action.history;
const auto peer = history->peer;
const auto session = &history->session();
const auto api = &session->api();
action.clearDraft = false;
action.generateLocal = false;
api->sendAction(action);
const auto randomId = base::RandomValue<uint64>();
auto flags = NewMessageFlags(peer);
auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (action.replyTo) {
flags |= MessageFlag::HasReplyInfo;
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
}
const auto silentPost = ShouldSendSilent(peer, action.options);
InnerFillMessagePostFlags(action.options, peer, flags);
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
const auto sendAs = action.options.sendAs;
if (sendAs) {
sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
}
const auto messagePostAuthor = peer->isBroadcast()
? session->user()->name()
: QString();
if (action.options.scheduled) {
flags |= MessageFlag::IsOrWasScheduled;
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
}
if (action.options.shortcutId) {
flags |= MessageFlag::ShortcutMessage;
sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
}
if (action.options.effectId) {
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
}
if (action.options.invertCaption) {
flags |= MessageFlag::InvertMedia;
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
}
auto &histories = history->owner().histories();
histories.sendPreparedMessage(
history,
action.replyTo,
randomId,
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(sendFlags),
peer->input,
Data::Histories::ReplyToPlaceholder(),
std::move(inputMedia),
MTPstring(),
MTP_long(randomId),
MTPReplyMarkup(),
MTPvector<MTPMessageEntity>(),
MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(session, action.options.shortcutId),
MTP_long(action.options.effectId)
), [=](const MTPUpdates &result, const MTP::Response &response) {
}, [=](const MTP::Error &error, const MTP::Response &response) {
api->sendMessageFail(error, peer, randomId);
});
api->finishForwarding(action);
}
template <typename MediaData>
void SendExistingMedia(
MessageToSend &&message,
@@ -133,6 +206,13 @@ void SendExistingMedia(
flags |= MessageFlag::ShortcutMessage;
sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
}
if (action.options.effectId) {
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
}
if (action.options.invertCaption) {
flags |= MessageFlag::InvertMedia;
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
}
session->data().registerMessageRandomId(randomId, newId);
@@ -144,6 +224,7 @@ void SendExistingMedia(
.date = HistoryItem::NewMessageDate(action.options),
.shortcutId = action.options.shortcutId,
.postAuthor = messagePostAuthor,
.effectId = action.options.effectId,
}, media, caption);
const auto performRequest = [=](const auto &repeatRequest) -> void {
@@ -165,7 +246,8 @@ void SendExistingMedia(
sentEntities,
MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(session, action.options.shortcutId)
Data::ShortcutIdToMTP(session, action.options.shortcutId),
MTP_long(action.options.effectId)
), [=](const MTPUpdates &result, const MTP::Response &response) {
}, [=](const MTP::Error &error, const MTP::Response &response) {
if (error.code() == 400
@@ -306,6 +388,13 @@ bool SendDice(MessageToSend &message) {
flags |= MessageFlag::ShortcutMessage;
sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
}
if (action.options.effectId) {
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
}
if (action.options.invertCaption) {
flags |= MessageFlag::InvertMedia;
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
}
session->data().registerMessageRandomId(randomId, newId);
@@ -317,6 +406,7 @@ bool SendDice(MessageToSend &message) {
.date = HistoryItem::NewMessageDate(action.options),
.shortcutId = action.options.shortcutId,
.postAuthor = messagePostAuthor,
.effectId = action.options.effectId,
}, TextWithEntities(), MTP_messageMediaDice(
MTP_int(0),
MTP_string(emoji)));
@@ -335,7 +425,8 @@ bool SendDice(MessageToSend &message) {
MTP_vector<MTPMessageEntity>(),
MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(session, action.options.shortcutId)
Data::ShortcutIdToMTP(session, action.options.shortcutId),
MTP_long(action.options.effectId)
), [=](const MTPUpdates &result, const MTP::Response &response) {
}, [=](const MTP::Error &error, const MTP::Response &response) {
api->sendMessageFail(error, peer, randomId, newId);
@@ -344,6 +435,33 @@ bool SendDice(MessageToSend &message) {
return true;
}
void SendLocation(SendAction action, float64 lat, float64 lon) {
SendSimpleMedia(
action,
MTP_inputMediaGeoPoint(
MTP_inputGeoPoint(
MTP_flags(0),
MTP_double(lat),
MTP_double(lon),
MTPint()))); // accuracy_radius
}
void SendVenue(SendAction action, Data::InputVenue venue) {
SendSimpleMedia(
action,
MTP_inputMediaVenue(
MTP_inputGeoPoint(
MTP_flags(0),
MTP_double(venue.lat),
MTP_double(venue.lon),
MTPint()), // accuracy_radius
MTP_string(venue.title),
MTP_string(venue.address),
MTP_string(venue.provider),
MTP_string(venue.id),
MTP_string(venue.venueType)));
}
void FillMessagePostFlags(
const SendAction &action,
not_null<PeerData*> peer,
@@ -430,6 +548,9 @@ void SendConfirmedFile(
flags |= MessageFlag::MediaIsUnread;
}
}
if (file->to.options.invertCaption) {
flags |= MessageFlag::InvertMedia;
}
const auto messageFromId = file->to.options.sendAs
? file->to.options.sendAs->id
@@ -493,6 +614,7 @@ void SendConfirmedFile(
edition.ttl = 0;
edition.mtpMedia = &media;
edition.textWithEntities = caption;
edition.invertMedia = file->to.options.invertCaption;
edition.useSameViews = true;
edition.useSameForwards = true;
edition.useSameMarkup = true;
@@ -510,6 +632,7 @@ void SendConfirmedFile(
.shortcutId = file->to.options.shortcutId,
.postAuthor = messagePostAuthor,
.groupedId = groupId,
.effectId = file->to.options.effectId,
}, caption, media);
}

View File

@@ -7,15 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Main {
class Session;
} // namespace Main
class History;
class PhotoData;
class DocumentData;
struct FilePrepareResult;
namespace Data {
struct InputVenue;
} // namespace Data
namespace Main {
class Session;
} // namespace Main
namespace Api {
struct MessageToSend;
@@ -33,6 +37,13 @@ void SendExistingPhoto(
bool SendDice(MessageToSend &message);
// We can't create Data::LocationPoint() and use it
// for a local sending message, because we can't request
// map thumbnail in messages history without access hash.
void SendLocation(SendAction action, float64 lat, float64 lon);
void SendVenue(SendAction action, Data::InputVenue venue);
void FillMessagePostFlags(
const SendAction &action,
not_null<PeerData*> peer,

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_statistics.h"
#include "api/api_statistics_data_deserialize.h"
#include "apiwrap.h"
#include "base/unixtime.h"
#include "data/data_channel.h"
@@ -15,33 +16,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_story.h"
#include "history/history.h"
#include "main/main_session.h"
#include "statistics/statistics_data_deserialize.h"
namespace Api {
namespace {
constexpr auto kCheckRequestsTimer = 10 * crl::time(1000);
[[nodiscard]] Data::StatisticalGraph StatisticalGraphFromTL(
const MTPStatsGraph &tl) {
return tl.match([&](const MTPDstatsGraph &d) {
using namespace Statistic;
const auto zoomToken = d.vzoom_token().has_value()
? qs(*d.vzoom_token()).toUtf8()
: QByteArray();
return Data::StatisticalGraph{
StatisticalChartFromJSON(qs(d.vjson().data().vdata()).toUtf8()),
zoomToken,
};
}, [&](const MTPDstatsGraphAsync &data) {
return Data::StatisticalGraph{
.zoomToken = qs(data.vtoken()).toUtf8(),
};
}, [&](const MTPDstatsGraphError &data) {
return Data::StatisticalGraph{ .error = qs(data.verror()) };
});
}
[[nodiscard]] Data::StatisticalValue StatisticalValueFromTL(
const MTPStatsAbsValueAndPrev &tl) {
const auto current = tl.data().vcurrent().v;
@@ -223,61 +201,6 @@ Statistics::Statistics(not_null<ChannelData*> channel)
: StatisticsRequestSender(channel) {
}
StatisticsRequestSender::StatisticsRequestSender(not_null<ChannelData *> channel)
: _channel(channel)
, _api(&_channel->session().api().instance())
, _timer([=] { checkRequests(); }) {
}
StatisticsRequestSender::~StatisticsRequestSender() {
for (const auto &[dcId, ids] : _requests) {
for (const auto id : ids) {
_channel->session().api().unregisterStatsRequest(dcId, id);
}
}
}
void StatisticsRequestSender::checkRequests() {
for (auto i = begin(_requests); i != end(_requests);) {
for (auto j = begin(i->second); j != end(i->second);) {
if (_api.pending(*j)) {
++j;
} else {
_channel->session().api().unregisterStatsRequest(
i->first,
*j);
j = i->second.erase(j);
}
}
if (i->second.empty()) {
i = _requests.erase(i);
} else {
++i;
}
}
if (_requests.empty()) {
_timer.cancel();
}
}
template <typename Request, typename, typename>
auto StatisticsRequestSender::makeRequest(Request &&request) {
const auto id = _api.allocateRequestId();
const auto dcId = _channel->owner().statsDcId(_channel);
if (dcId) {
_channel->session().api().registerStatsRequest(dcId, id);
_requests[dcId].emplace(id);
if (!_timer.isActive()) {
_timer.callEach(kCheckRequestsTimer);
}
}
return std::move(_api.request(
std::forward<Request>(request)
).toDC(
dcId ? MTP::ShiftDcId(dcId, MTP::kStatsDcShift) : 0
).overrideId(id));
}
rpl::producer<rpl::no_value, QString> Statistics::request() {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
@@ -747,11 +670,11 @@ Data::BoostStatus Boosts::boostStatus() const {
return _boostStatus;
}
EarnStatistics::EarnStatistics(not_null<ChannelData*> channel)
ChannelEarnStatistics::ChannelEarnStatistics(not_null<ChannelData*> channel)
: StatisticsRequestSender(channel) {
}
rpl::producer<rpl::no_value, QString> EarnStatistics::request() {
rpl::producer<rpl::no_value, QString> ChannelEarnStatistics::request() {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
@@ -795,7 +718,7 @@ rpl::producer<rpl::no_value, QString> EarnStatistics::request() {
};
}
void EarnStatistics::requestHistory(
void ChannelEarnStatistics::requestHistory(
const Data::EarnHistorySlice::OffsetToken &token,
Fn<void(Data::EarnHistorySlice)> done) {
if (_requestId) {
@@ -865,7 +788,7 @@ void EarnStatistics::requestHistory(
}).send();
}
Data::EarnStatistics EarnStatistics::data() const {
Data::EarnStatistics ChannelEarnStatistics::data() const {
return _data;
}

View File

@@ -7,45 +7,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/timer.h"
#include "api/api_statistics_sender.h"
#include "data/data_boosts.h"
#include "data/data_channel_earn.h"
#include "data/data_statistics.h"
#include "mtproto/sender.h"
class ChannelData;
class PeerData;
namespace Api {
class StatisticsRequestSender {
protected:
explicit StatisticsRequestSender(not_null<ChannelData*> channel);
~StatisticsRequestSender();
template <
typename Request,
typename = std::enable_if_t<!std::is_reference_v<Request>>,
typename = typename Request::Unboxed>
[[nodiscard]] auto makeRequest(Request &&request);
[[nodiscard]] MTP::Sender &api() {
return _api;
}
[[nodiscard]] not_null<ChannelData*> channel() {
return _channel;
}
private:
void checkRequests();
const not_null<ChannelData*> _channel;
MTP::Sender _api;
base::Timer _timer;
base::flat_map<MTP::DcId, base::flat_set<mtpRequestId>> _requests;
};
class Statistics final : public StatisticsRequestSender {
public:
explicit Statistics(not_null<ChannelData*> channel);
@@ -108,9 +79,9 @@ private:
};
class EarnStatistics final : public StatisticsRequestSender {
class ChannelEarnStatistics final : public StatisticsRequestSender {
public:
explicit EarnStatistics(not_null<ChannelData*> channel);
explicit ChannelEarnStatistics(not_null<ChannelData*> channel);
[[nodiscard]] rpl::producer<rpl::no_value, QString> request();
void requestHistory(

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
*/
#include "api/api_statistics_data_deserialize.h"
#include "data/data_statistics_chart.h"
#include "statistics/statistics_data_deserialize.h"
namespace Api {
Data::StatisticalGraph StatisticalGraphFromTL(const MTPStatsGraph &tl) {
return tl.match([&](const MTPDstatsGraph &d) {
using namespace Statistic;
const auto zoomToken = d.vzoom_token().has_value()
? qs(*d.vzoom_token()).toUtf8()
: QByteArray();
return Data::StatisticalGraph{
StatisticalChartFromJSON(qs(d.vjson().data().vdata()).toUtf8()),
zoomToken,
};
}, [&](const MTPDstatsGraphAsync &data) {
return Data::StatisticalGraph{
.zoomToken = qs(data.vtoken()).toUtf8(),
};
}, [&](const MTPDstatsGraphError &data) {
return Data::StatisticalGraph{ .error = qs(data.verror()) };
});
}
} // namespace Api

View File

@@ -0,0 +1,19 @@
/*
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 Data {
struct StatisticalGraph;
} // namespace Data
namespace Api {
[[nodiscard]] Data::StatisticalGraph StatisticalGraphFromTL(
const MTPStatsGraph &tl);
} // namespace Api

View File

@@ -0,0 +1,86 @@
/*
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_statistics_sender.h"
#include "apiwrap.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "main/main_session.h"
namespace Api {
StatisticsRequestSender::StatisticsRequestSender(
not_null<PeerData*> peer)
: _peer(peer)
, _channel(peer->asChannel())
, _user(peer->asUser())
, _api(&_peer->session().api().instance())
, _timer([=] { checkRequests(); }) {
}
MTP::Sender &StatisticsRequestSender::api() {
return _api;
}
not_null<ChannelData*> StatisticsRequestSender::channel() {
Expects(_channel);
return _channel;
}
not_null<UserData*> StatisticsRequestSender::user() {
Expects(_user);
return _user;
}
void StatisticsRequestSender::checkRequests() {
for (auto i = begin(_requests); i != end(_requests);) {
for (auto j = begin(i->second); j != end(i->second);) {
if (_api.pending(*j)) {
++j;
} else {
_peer->session().api().unregisterStatsRequest(
i->first,
*j);
j = i->second.erase(j);
}
}
if (i->second.empty()) {
i = _requests.erase(i);
} else {
++i;
}
}
if (_requests.empty()) {
_timer.cancel();
}
}
auto StatisticsRequestSender::ensureRequestIsRegistered()
-> StatisticsRequestSender::Registered {
const auto id = _api.allocateRequestId();
const auto dcId = _peer->owner().statsDcId(_peer);
if (dcId) {
_peer->session().api().registerStatsRequest(dcId, id);
_requests[dcId].emplace(id);
if (!_timer.isActive()) {
constexpr auto kCheckRequestsTimer = 10 * crl::time(1000);
_timer.callEach(kCheckRequestsTimer);
}
}
return StatisticsRequestSender::Registered{ id, dcId };
}
StatisticsRequestSender::~StatisticsRequestSender() {
for (const auto &[dcId, ids] : _requests) {
for (const auto id : ids) {
_peer->session().api().unregisterStatsRequest(dcId, id);
}
}
}
} // namespace Api

View File

@@ -0,0 +1,58 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/timer.h"
#include "mtproto/sender.h"
class ChannelData;
class PeerData;
class UserData;
namespace Api {
class StatisticsRequestSender {
protected:
explicit StatisticsRequestSender(not_null<PeerData*> peer);
~StatisticsRequestSender();
template <
typename Request,
typename = std::enable_if_t<!std::is_reference_v<Request>>,
typename = typename Request::Unboxed>
[[nodiscard]] auto makeRequest(Request &&request) {
const auto [id, dcId] = ensureRequestIsRegistered();
return std::move(_api.request(
std::forward<Request>(request)
).toDC(
dcId ? MTP::ShiftDcId(dcId, MTP::kStatsDcShift) : 0
).overrideId(id));
}
[[nodiscard]] MTP::Sender &api();
[[nodiscard]] not_null<ChannelData*> channel();
[[nodiscard]] not_null<UserData*> user();
private:
struct Registered final {
mtpRequestId id;
MTP::DcId dcId;
};
[[nodiscard]] Registered ensureRequestIsRegistered();
void checkRequests();
const not_null<PeerData*> _peer;
ChannelData * const _channel;
UserData * const _user;
MTP::Sender _api;
base::Timer _timer;
base::flat_map<MTP::DcId, base::flat_set<mtpRequestId>> _requests;
};
} // namespace Api

View File

@@ -178,7 +178,11 @@ EntitiesInText EntitiesFromMTP(
});
}
}, [&](const MTPDmessageEntityPhone &d) {
// Skipping phones.
result.push_back({
EntityType::Phone,
d.voffset().v,
d.vlength().v,
});
}, [&](const MTPDmessageEntityCashtag &d) {
result.push_back({
EntityType::Cashtag,
@@ -217,6 +221,7 @@ EntitiesInText EntitiesFromMTP(
EntityType::Blockquote,
d.voffset().v,
d.vlength().v,
d.is_collapsed() ? u"1"_q : QString(),
});
});
}
@@ -265,6 +270,9 @@ MTPVector<MTPMessageEntity> EntitiesToMTP(
case EntityType::Email: {
v.push_back(MTP_messageEntityEmail(offset, length));
} break;
case EntityType::Phone: {
v.push_back(MTP_messageEntityPhone(offset, length));
} break;
case EntityType::Hashtag: {
v.push_back(MTP_messageEntityHashtag(offset, length));
} break;
@@ -311,7 +319,13 @@ MTPVector<MTPMessageEntity> EntitiesToMTP(
MTP_string(entity.data())));
} break;
case EntityType::Blockquote: {
v.push_back(MTP_messageEntityBlockquote(offset, length));
using Flag = MTPDmessageEntityBlockquote::Flag;
const auto collapsed = !entity.data().isEmpty();
v.push_back(
MTP_messageEntityBlockquote(
MTP_flags(collapsed ? Flag::f_collapsed : Flag()),
offset,
length));
} break;
case EntityType::Spoiler: {
v.push_back(MTP_messageEntitySpoiler(offset, length));

View File

@@ -1137,7 +1137,9 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
MTPMessageReactions(),
MTPVector<MTPRestrictionReason>(),
MTP_int(d.vttl_period().value_or_empty()),
MTPint()), // quick_reply_shortcut_id
MTPint(), // quick_reply_shortcut_id
MTPlong(), // effect
MTPFactCheck()),
MessageFlags(),
NewMessageType::Unread);
} break;
@@ -1172,7 +1174,9 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
MTPMessageReactions(),
MTPVector<MTPRestrictionReason>(),
MTP_int(d.vttl_period().value_or_empty()),
MTPint()), // quick_reply_shortcut_id
MTPint(), // quick_reply_shortcut_id
MTPlong(), // effect
MTPFactCheck()),
MessageFlags(),
NewMessageType::Unread);
} break;
@@ -1692,7 +1696,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
const auto peerId = peerFromMTP(d.vpeer());
const auto msgId = d.vmsg_id().v;
if (const auto item = session().data().message(peerId, msgId)) {
item->applyEdition(d.vextended_media());
item->applyEdition(d.vextended_media().v);
}
} break;
@@ -2117,6 +2121,8 @@ void Updates::feedUpdate(const MTPUpdate &update) {
};
if (IsForceLogoutNotification(d)) {
Core::App().forceLogOut(&session().account(), text);
} else if (IsWithdrawalNotification(d)) {
return;
} else if (d.is_popup()) {
const auto &windows = session().windows();
if (!windows.empty()) {
@@ -2610,7 +2616,16 @@ void Updates::feedUpdate(const MTPUpdate &update) {
_session->data().stories().apply(data.vstealth_mode());
} break;
case mtpc_updateStarsBalance: {
const auto &data = update.c_updateStarsBalance();
_session->setCredits(data.vbalance().v);
} break;
}
}
bool IsWithdrawalNotification(const MTPDupdateServiceNotification &data) {
return qs(data.vtype()).startsWith(u"API_WITHDRAWAL_FEATURE_DISABLED_"_q);
}
} // namespace Api

View File

@@ -211,4 +211,7 @@ private:
};
[[nodiscard]] bool IsWithdrawalNotification(
const MTPDupdateServiceNotification &);
} // namespace Api

View File

@@ -55,7 +55,9 @@ void ViewsManager::removeIncremented(not_null<PeerData*> peer) {
_incremented.remove(peer);
}
void ViewsManager::pollExtendedMedia(not_null<HistoryItem*> item) {
void ViewsManager::pollExtendedMedia(
not_null<HistoryItem*> item,
bool force) {
if (!item->isRegular()) {
return;
}
@@ -63,14 +65,20 @@ void ViewsManager::pollExtendedMedia(not_null<HistoryItem*> item) {
const auto peer = item->history()->peer;
auto &request = _pollRequests[peer];
if (request.ids.contains(id) || request.sent.contains(id)) {
return;
if (!force || request.forced) {
return;
}
}
request.ids.emplace(id);
if (!request.id && !request.when) {
request.when = crl::now() + kPollExtendedMediaPeriod;
if (force) {
request.forced = true;
}
if (!_pollTimer.isActive()) {
_pollTimer.callOnce(kPollExtendedMediaPeriod);
const auto delay = force ? 1 : kPollExtendedMediaPeriod;
if (!request.id && (!request.when || force)) {
request.when = crl::now() + delay;
}
if (!_pollTimer.isActive() || force) {
_pollTimer.callOnce(delay);
}
}
@@ -160,9 +168,12 @@ void ViewsManager::sendPollRequests(
if (i->second.ids.empty()) {
i = _pollRequests.erase(i);
} else {
i->second.when = now + kPollExtendedMediaPeriod;
if (!_pollTimer.isActive()) {
_pollTimer.callOnce(kPollExtendedMediaPeriod);
const auto delay = i->second.forced
? 1
: kPollExtendedMediaPeriod;
i->second.when = now + delay;
if (!_pollTimer.isActive() || i->second.forced) {
_pollTimer.callOnce(delay);
}
++i;
}

View File

@@ -26,7 +26,7 @@ public:
void scheduleIncrement(not_null<HistoryItem*> item);
void removeIncremented(not_null<PeerData*> peer);
void pollExtendedMedia(not_null<HistoryItem*> item);
void pollExtendedMedia(not_null<HistoryItem*> item, bool force = false);
private:
struct PollExtendedMediaRequest {
@@ -34,6 +34,7 @@ private:
mtpRequestId id = 0;
base::flat_set<MsgId> ids;
base::flat_set<MsgId> sent;
bool forced = false;
};
void viewsIncrement();

View File

@@ -50,6 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h"
#include "data/data_chat_filters.h"
#include "data/data_histories.h"
#include "data/data_history_messages.h"
#include "core/core_cloud_password.h"
#include "core/application.h"
#include "base/unixtime.h"
@@ -2164,7 +2165,8 @@ void ApiWrap::saveDraftsToCloud() {
entities,
Data::WebPageForMTP(
cloudDraft->webpage,
textWithTags.text.isEmpty())
textWithTags.text.isEmpty()),
MTP_long(0) // effect
)).done([=](const MTPBool &result, const MTP::Response &response) {
const auto requestId = response.requestId;
history->finishSavingCloudDraft(
@@ -3078,6 +3080,46 @@ void ApiWrap::resolveJumpToHistoryDate(
}
}
void ApiWrap::requestHistory(
not_null<History*> history,
MsgId messageId,
SliceType slice) {
const auto peer = history->peer;
const auto key = HistoryRequest{
peer,
messageId,
slice,
};
if (_historyRequests.contains(key)) {
return;
}
const auto prepared = Api::PrepareHistoryRequest(peer, messageId, slice);
auto &histories = history->owner().histories();
const auto requestType = Data::Histories::RequestType::History;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
return request(
std::move(prepared)
).done([=](const Api::HistoryRequestResult &result) {
_historyRequests.remove(key);
auto parsed = Api::ParseHistoryResult(
peer,
messageId,
slice,
result);
history->messages().addSlice(
std::move(parsed.messageIds),
parsed.noSkipRange,
parsed.fullCount);
finish();
}).fail([=] {
_historyRequests.remove(key);
finish();
}).send();
});
_historyRequests.emplace(key);
}
void ApiWrap::requestSharedMedia(
not_null<PeerData*> peer,
MsgId topicRootId,
@@ -3342,6 +3384,9 @@ void ApiWrap::forwardMessages(
.date = HistoryItem::NewMessageDate(action.options),
.shortcutId = action.options.shortcutId,
.postAuthor = messagePostAuthor,
// forwarded messages don't have effects
//.effectId = action.options.effectId,
}, item);
_session->data().registerMessageRandomId(randomId, newId);
if (!localIds) {
@@ -3442,6 +3487,7 @@ void ApiWrap::sendSharedContact(
.date = HistoryItem::NewMessageDate(action.options),
.shortcutId = action.options.shortcutId,
.postAuthor = messagePostAuthor,
.effectId = action.options.effectId,
}, TextWithEntities(), MTP_messageMediaContact(
MTP_string(phone),
MTP_string(firstName),
@@ -3729,7 +3775,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
const auto anonymousPost = peer->amAnonymous();
const auto silentPost = ShouldSendSilent(peer, action.options);
FillMessagePostFlags(action, peer, flags);
if (exactWebPage && !ignoreWebPage && message.webPage.invert) {
if ((exactWebPage && !ignoreWebPage && message.webPage.invert)
|| action.options.invertCaption) {
flags |= MessageFlag::InvertMedia;
sendFlags |= MTPmessages_SendMessage::Flag::f_invert_media;
mediaFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
@@ -3775,6 +3822,10 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
sendFlags |= MTPmessages_SendMessage::Flag::f_quick_reply_shortcut;
mediaFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
}
if (action.options.effectId) {
sendFlags |= MTPmessages_SendMessage::Flag::f_effect;
mediaFlags |= MTPmessages_SendMedia::Flag::f_effect;
}
lastMessage = history->addNewLocalMessage({
.id = newId.msg,
.flags = flags,
@@ -3783,6 +3834,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
.date = HistoryItem::NewMessageDate(action.options),
.shortcutId = action.options.shortcutId,
.postAuthor = messagePostAuthor,
.effectId = action.options.effectId,
}, sending, media);
const auto done = [=](
const MTPUpdates &result,
@@ -3828,7 +3880,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
sentEntities,
MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
mtpShortcut
mtpShortcut,
MTP_long(action.options.effectId)
), done, fail);
} else {
histories.sendPreparedMessage(
@@ -3845,7 +3898,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
sentEntities,
MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
mtpShortcut
mtpShortcut,
MTP_long(action.options.effectId)
), done, fail);
}
isFirst = false;
@@ -4119,7 +4173,9 @@ void ApiWrap::sendMediaWithRandomId(
| (!sentEntities.v.isEmpty() ? Flag::f_entities : Flag(0))
| (options.scheduled ? Flag::f_schedule_date : Flag(0))
| (options.sendAs ? Flag::f_send_as : Flag(0))
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0));
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0))
| (options.effectId ? Flag::f_effect : Flag(0))
| (options.invertCaption ? Flag::f_invert_media : Flag(0));
auto &histories = history->owner().histories();
const auto peer = history->peer;
@@ -4132,14 +4188,19 @@ void ApiWrap::sendMediaWithRandomId(
MTP_flags(flags),
peer->input,
Data::Histories::ReplyToPlaceholder(),
media,
(options.price
? MTPInputMedia(MTP_inputMediaPaidMedia(
MTP_long(options.price),
MTP_vector<MTPInputMedia>(1, media)))
: media),
MTP_string(caption.text),
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities,
MTP_int(options.scheduled),
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, options.shortcutId)
Data::ShortcutIdToMTP(_session, options.shortcutId),
MTP_long(options.effectId)
), [=](const MTPUpdates &result, const MTP::Response &response) {
if (done) done(true);
if (updateRecentStickers) {
@@ -4151,6 +4212,82 @@ void ApiWrap::sendMediaWithRandomId(
});
}
void ApiWrap::sendMultiPaidMedia(
not_null<HistoryItem*> item,
not_null<SendingAlbum*> album,
Fn<void(bool)> done) {
Expects(album->options.price > 0);
const auto groupId = album->groupId;
const auto &options = album->options;
const auto randomId = album->items.front().randomId;
auto medias = album->items | ranges::view::transform([](
const SendingAlbum::Item &part) {
Assert(part.media.has_value());
return MTPInputMedia(part.media->data().vmedia());
}) | ranges::to<QVector<MTPInputMedia>>();
const auto history = item->history();
const auto replyTo = item->replyTo();
auto caption = item->originalText();
TextUtilities::Trim(caption);
auto sentEntities = Api::EntitiesToMTP(
_session,
caption.entities,
Api::ConvertOption::SkipLocal);
using Flag = MTPmessages_SendMedia::Flag;
const auto flags = Flag(0)
| (replyTo ? Flag::f_reply_to : Flag(0))
| (ShouldSendSilent(history->peer, options)
? Flag::f_silent
: Flag(0))
| (!sentEntities.v.isEmpty() ? Flag::f_entities : Flag(0))
| (options.scheduled ? Flag::f_schedule_date : Flag(0))
| (options.sendAs ? Flag::f_send_as : Flag(0))
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0))
| (options.effectId ? Flag::f_effect : Flag(0))
| (options.invertCaption ? Flag::f_invert_media : Flag(0));
auto &histories = history->owner().histories();
const auto peer = history->peer;
const auto itemId = item->fullId();
histories.sendPreparedMessage(
history,
replyTo,
randomId,
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(flags),
peer->input,
Data::Histories::ReplyToPlaceholder(),
MTP_inputMediaPaidMedia(
MTP_long(options.price),
MTP_vector<MTPInputMedia>(std::move(medias))),
MTP_string(caption.text),
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities,
MTP_int(options.scheduled),
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, options.shortcutId),
MTP_long(options.effectId)
), [=](const MTPUpdates &result, const MTP::Response &response) {
if (const auto album = _sendingAlbums.take(groupId)) {
const auto copy = (*album)->items;
for (const auto &part : copy) {
if (const auto item = history->owner().message(part.msgId)) {
item->destroy();
}
}
}
if (done) done(true);
}, [=](const MTP::Error &error, const MTP::Response &response) {
if (done) done(false);
sendMessageFail(error, peer, randomId, itemId);
});
}
void ApiWrap::sendAlbumWithUploaded(
not_null<HistoryItem*> item,
const MessageGroupId &groupId,
@@ -4204,8 +4341,11 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
if (!sample) {
_sendingAlbums.remove(groupId);
return;
} else if (album->options.price > 0) {
sendMultiPaidMedia(sample, album);
return;
} else if (medias.size() < 2) {
const auto &single = medias.front().c_inputSingleMedia();
const auto &single = medias.front().data();
sendMediaWithRandomId(
sample,
single.vmedia(),
@@ -4227,7 +4367,9 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
| (sendAs ? Flag::f_send_as : Flag(0))
| (album->options.shortcutId
? Flag::f_quick_reply_shortcut
: Flag(0));
: Flag(0))
| (album->options.effectId ? Flag::f_effect : Flag(0))
| (album->options.invertCaption ? Flag::f_invert_media : Flag(0));
auto &histories = history->owner().histories();
const auto peer = history->peer;
histories.sendPreparedMessage(
@@ -4241,7 +4383,8 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
MTP_vector<MTPInputSingleMedia>(medias),
MTP_int(album->options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, album->options.shortcutId)
Data::ShortcutIdToMTP(_session, album->options.shortcutId),
MTP_long(album->options.effectId)
), [=](const MTPUpdates &result, const MTP::Response &response) {
_sendingAlbums.remove(groupId);
}, [=](const MTP::Error &error, const MTP::Response &response) {

View File

@@ -274,6 +274,10 @@ public:
Fn<void(not_null<PeerData*>, MsgId)> callback);
using SliceType = Data::LoadDirection;
void requestHistory(
not_null<History*> history,
MsgId messageId,
SliceType slice);
void requestSharedMedia(
not_null<PeerData*> peer,
MsgId topicRootId,
@@ -511,7 +515,8 @@ private:
not_null<PeerData*> peer,
bool justClear,
bool revoke);
void applyAffectedMessages(const MTPmessages_AffectedMessages &result) const;
void applyAffectedMessages(
const MTPmessages_AffectedMessages &result) const;
void deleteAllFromParticipantSend(
not_null<ChannelData*> channel,
@@ -540,6 +545,10 @@ private:
Api::SendOptions options,
uint64 randomId,
Fn<void(bool)> done = nullptr);
void sendMultiPaidMedia(
not_null<HistoryItem*> item,
not_null<SendingAlbum*> album,
Fn<void(bool)> done = nullptr);
void getTopPromotionDelayed(TimeId now, TimeId next);
void topPromotionDone(const MTPhelp_PromoData &proxy);
@@ -645,6 +654,17 @@ private:
};
base::flat_set<SharedMediaRequest> _sharedMediaRequests;
struct HistoryRequest {
not_null<PeerData*> peer;
MsgId aroundId = 0;
SliceType sliceType = {};
friend inline auto operator<=>(
const HistoryRequest&,
const HistoryRequest&) = default;
};
base::flat_set<HistoryRequest> _historyRequests;
std::unique_ptr<DialogsLoadState> _dialogsLoadState;
TimeId _dialogsLoadTill = 0;
rpl::variable<bool> _dialogsLoadMayBlockByDate = false;

View File

@@ -100,6 +100,8 @@ void AboutBox::showVersionHistory() {
url += u"win/%1.zip"_q;
} else if (Platform::IsWindows64Bit()) {
url += u"win64/%1.zip"_q;
} else if (Platform::IsWindowsARM64()) {
url += u"winarm/%1.zip"_q;
} else if (Platform::IsMac()) {
url += u"mac/%1.zip"_q;
} else if (Platform::IsLinux()) {
@@ -155,6 +157,8 @@ QString currentVersionText() {
}
if (Platform::IsWindows64Bit()) {
result += " x64";
} else if (Platform::IsWindowsARM64()) {
result += " arm64";
}
return result;
}

View File

@@ -192,20 +192,27 @@ void ShowAddParticipantsError(
&& channel
&& !channel->isMegagroup()
&& channel->canAddAdmins()) {
const auto makeAdmin = [=] {
const auto makeAdmin = [=](Fn<void()> close) {
const auto user = forbidden.users.front();
const auto weak = std::make_shared<QPointer<EditAdminBox>>();
const auto close = [=](auto&&...) {
if (*weak) {
(*weak)->closeBox();
const auto done = [=](auto&&...) {
if (const auto strong = weak->data()) {
strong->uiShow()->showToast(
tr::lng_box_done(tr::now));
strong->closeBox();
}
};
const auto fail = [=] {
if (const auto strong = weak->data()) {
strong->closeBox();
}
};
const auto saveCallback = SaveAdminCallback(
show,
channel,
user,
close,
close);
done,
fail);
auto box = Box<EditAdminBox>(
channel,
user,
@@ -214,6 +221,7 @@ void ShowAddParticipantsError(
box->setSaveCallback(saveCallback);
*weak = box.data();
show->showBox(std::move(box));
close();
};
show->showBox(
Ui::MakeConfirmBox({

View File

@@ -237,7 +237,7 @@ shareColumnSkip: 6px;
shareActivateDuration: 150;
shareScrollDuration: 300;
shareComment: InputField(defaultInputField) {
font: normalFont;
style: defaultTextStyle;
textMargins: margins(8px, 8px, 8px, 6px);
heightMin: 36px;
heightMax: 72px;
@@ -290,6 +290,26 @@ passcodeTextLine: 28px;
passcodeLittleSkip: 5px;
passcodeAboutSkip: 7px;
passcodeSkip: 23px;
passcodeSystemUnlock: IconButton(defaultIconButton) {
width: 32px;
height: 36px;
icon: icon{{ "menu/passcode_winhello", lightButtonFg }};
iconOver: icon{{ "menu/passcode_winhello", lightButtonFg }};
iconPosition: point(4px, 4px);
rippleAreaSize: 32px;
rippleAreaPosition: point(0px, 0px);
ripple: RippleAnimation(defaultRippleAnimation) {
color: lightButtonBgOver;
}
}
passcodeSystemTouchID: icon{{ "menu/passcode_finger", lightButtonFg }};
passcodeSystemAppleWatch: icon{{ "menu/passcode_watch", lightButtonFg }};
passcodeSystemSystemPwd: icon{{ "menu/permissions", lightButtonFg }};
passcodeSystemUnlockLater: FlatLabel(defaultFlatLabel) {
align: align(top);
textFg: windowSubTextFg;
}
passcodeSystemUnlockSkip: 12px;
newGroupAboutFg: windowSubTextFg;
newGroupPadding: margins(4px, 6px, 4px, 3px);
@@ -585,7 +605,7 @@ groupStickersRemovePosition: point(6px, 6px);
groupStickersFieldPadding: margins(8px, 6px, 8px, 6px);
groupStickersField: InputField(defaultMultiSelectSearchField) {
placeholderFont: boxTextFont;
font: boxTextFont;
style: boxTextStyle;
placeholderMargins: margins(0px, 0px, 0px, 0px);
textMargins: margins(0px, 7px, 0px, 0px);
textBg: boxBg;
@@ -642,6 +662,10 @@ proxyDropdownUpPosition: point(-2px, 20px);
proxyAboutPadding: margins(22px, 7px, 22px, 14px);
proxyAboutSponsorPadding: margins(22px, 7px, 22px, 0px);
proxyApplyBoxLabel : FlatLabel(defaultFlatLabel) {
maxHeight: 30px;
}
markdownLinkFieldPadding: margins(22px, 0px, 22px, 10px);
termsContent: FlatLabel(defaultFlatLabel) {
@@ -668,7 +692,6 @@ themesMenuToggle: IconButton(defaultIconButton) {
themesMenuPosition: point(-2px, 25px);
createPollField: InputField(defaultInputField) {
font: boxTextFont;
textMargins: margins(0px, 4px, 0px, 4px);
textAlign: align(left);
heightMin: 36px;
@@ -873,7 +896,6 @@ scheduleDateField: InputField(defaultInputField) {
placeholderScale: 0.;
heightMin: 30px;
textAlign: align(top);
font: font(14px);
}
scheduleTimeField: InputField(scheduleDateField) {
border: 0px;
@@ -901,7 +923,6 @@ muteBoxTimeField: InputField(scheduleDateField) {
placeholderScale: 0.;
heightMin: 30px;
textAlign: align(left);
font: font(14px);
}
muteBoxTimeFieldPadding: margins(5px, 0px, 5px, 0px);

View File

@@ -39,6 +39,7 @@ Data::ChatFilter ChangedFilter(
filter.id(),
filter.title(),
filter.iconEmoji(),
filter.colorIndex(),
filter.flags(),
std::move(always),
filter.pinned(),
@@ -58,6 +59,7 @@ Data::ChatFilter ChangedFilter(
filter.id(),
filter.title(),
filter.iconEmoji(),
filter.colorIndex(),
filter.flags(),
std::move(always),
filter.pinned(),
@@ -81,7 +83,7 @@ void ChangeFilterById(
MTP_int(filter.id()),
filter.tl()
)).done([=, chat = history->peer->name(), name = filter.title()] {
const auto account = &history->session().account();
const auto account = not_null(&history->session().account());
if (const auto controller = Core::App().windowFor(account)) {
controller->showToast((add
? tr::lng_filters_toast_add

View File

@@ -7,32 +7,37 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/connection_box.h"
#include "ui/boxes/confirm_box.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "base/qthelp_url.h"
#include "base/call_delayed.h"
#include "base/qthelp_regex.h"
#include "base/qthelp_url.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "core/local_url_handlers.h"
#include "lang/lang_keys.h"
#include "main/main_account.h"
#include "mtproto/facade.h"
#include "ui/widgets/checkbox.h"
#include "storage/localstorage.h"
#include "ui/basic_click_handlers.h"
#include "ui/boxes/confirm_box.h"
#include "ui/effects/animations.h"
#include "ui/effects/radial_animation.h"
#include "ui/painter.h"
#include "ui/text/text_options.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/dropdown_menu.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/fields/number_input.h"
#include "ui/widgets/fields/password_input.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/dropdown_menu.h"
#include "ui/widgets/popup_menu.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/toast/toast.h"
#include "ui/effects/animations.h"
#include "ui/effects/radial_animation.h"
#include "ui/text/text_options.h"
#include "ui/text/text_utilities.h"
#include "ui/basic_click_handlers.h"
#include "ui/painter.h"
#include "ui/vertical_list.h"
#include "boxes/abstract_box.h" // Ui::show().
#include "window/window_session_controller.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h"
@@ -48,6 +53,22 @@ constexpr auto kSaveSettingsDelayedTimeout = crl::time(1000);
using ProxyData = MTP::ProxyData;
[[nodiscard]] ProxyData ProxyDataFromFields(
ProxyData::Type type,
const QMap<QString, QString> &fields) {
auto proxy = ProxyData();
proxy.type = type;
proxy.host = fields.value(u"server"_q);
proxy.port = fields.value(u"port"_q).toUInt();
if (type == ProxyData::Type::Socks5) {
proxy.user = fields.value(u"user"_q);
proxy.password = fields.value(u"pass"_q);
} else if (type == ProxyData::Type::Mtproto) {
proxy.password = fields.value(u"secret"_q);
}
return proxy;
};
class HostInput : public Ui::MaskedInputField {
public:
HostInput(
@@ -203,6 +224,7 @@ protected:
private:
void setupContent();
void setupTopButton();
void createNoRowsLabel();
void addNewProxy();
void applyView(View &&view);
@@ -600,9 +622,80 @@ void ProxiesBox::prepare() {
addButton(tr::lng_proxy_add(), [=] { addNewProxy(); });
addButton(tr::lng_close(), [=] { closeBox(); });
setupTopButton();
setupContent();
}
void ProxiesBox::setupTopButton() {
const auto top = addTopButton(st::infoTopBarMenu);
const auto menu
= top->lifetime().make_state<base::unique_qptr<Ui::PopupMenu>>();
const auto callback = [=] {
const auto maybeUrl = QGuiApplication::clipboard()->text();
const auto local = Core::TryConvertUrlToLocal(maybeUrl);
const auto proxyString = u"proxy"_q;
const auto socksString = u"socks"_q;
const auto protocol = u"tg://"_q;
const auto command = base::StringViewMid(
local,
protocol.size(),
8192);
if (local.startsWith(protocol + proxyString)
|| local.startsWith(protocol + socksString)) {
using namespace qthelp;
const auto options = RegExOption::CaseInsensitive;
for (const auto &[expression, _] : Core::LocalUrlHandlers()) {
const auto midExpression = base::StringViewMid(
expression,
1);
const auto isSocks = midExpression.startsWith(
socksString);
if (!midExpression.startsWith(proxyString)
&& !isSocks) {
continue;
}
const auto match = regex_match(
expression,
command,
options);
if (!match) {
continue;
}
const auto type = isSocks
? ProxyData::Type::Socks5
: ProxyData::Type::Mtproto;
const auto fields = url_parse_params(
match->captured(1),
qthelp::UrlParamNameTransform::ToLower);
const auto proxy = ProxyDataFromFields(type, fields);
const auto contains = _controller->contains(proxy);
const auto toast = (contains
? tr::lng_proxy_add_from_clipboard_existing_toast
: tr::lng_proxy_add_from_clipboard_good_toast)(tr::now);
uiShow()->showToast(toast);
if (!contains) {
_controller->addNewItem(proxy);
}
break;
}
} else {
uiShow()->showToast(
tr::lng_proxy_add_from_clipboard_failed_toast(tr::now));
}
};
top->setClickedCallback([=] {
*menu = base::make_unique_q<Ui::PopupMenu>(top, st::defaultPopupMenu);
(*menu)->addAction(
tr::lng_proxy_add_from_clipboard(tr::now),
callback);
(*menu)->popup(QCursor::pos());
return true;
});
}
void ProxiesBox::setupContent() {
const auto inner = setInnerWidget(object_ptr<Ui::VerticalLayout>(this));
@@ -1094,70 +1187,84 @@ ProxiesBoxController::ProxiesBoxController(not_null<Main::Account*> account)
}
void ProxiesBoxController::ShowApplyConfirmation(
Window::SessionController *controller,
Type type,
const QMap<QString, QString> &fields) {
const auto server = fields.value(u"server"_q);
const auto port = fields.value(u"port"_q).toUInt();
auto proxy = ProxyData();
proxy.type = type;
proxy.host = server;
proxy.port = port;
if (type == Type::Socks5) {
proxy.user = fields.value(u"user"_q);
proxy.password = fields.value(u"pass"_q);
} else if (type == Type::Mtproto) {
proxy.password = fields.value(u"secret"_q);
const auto proxy = ProxyDataFromFields(type, fields);
if (!proxy) {
auto box = Ui::MakeInformBox(
(proxy.status() == ProxyData::Status::Unsupported
? tr::lng_proxy_unsupported(tr::now)
: tr::lng_proxy_invalid(tr::now)));
if (controller) {
controller->uiShow()->showBox(std::move(box));
} else {
Ui::show(std::move(box));
}
return;
}
if (proxy) {
static const auto UrlStartRegExp = QRegularExpression(
"^https://",
QRegularExpression::CaseInsensitiveOption);
static const auto UrlEndRegExp = QRegularExpression("/$");
const auto displayed = "https://" + server + "/";
const auto parsed = QUrl::fromUserInput(displayed);
const auto displayUrl = !UrlClickHandler::IsSuspicious(displayed)
? displayed
: parsed.isValid()
? QString::fromUtf8(parsed.toEncoded())
: UrlClickHandler::ShowEncoded(displayed);
const auto displayServer = QString(
displayUrl
).replace(
UrlStartRegExp,
QString()
).replace(UrlEndRegExp, QString());
const auto text = tr::lng_sure_enable_socks(
tr::now,
lt_server,
displayServer,
lt_port,
QString::number(port))
+ (proxy.type == Type::Mtproto
? "\n\n" + tr::lng_proxy_sponsor_warning(tr::now)
: QString());
auto callback = [=](Fn<void()> &&close) {
static const auto UrlStartRegExp = QRegularExpression(
"^https://",
QRegularExpression::CaseInsensitiveOption);
static const auto UrlEndRegExp = QRegularExpression("/$");
const auto displayed = "https://" + proxy.host + "/";
const auto parsed = QUrl::fromUserInput(displayed);
const auto displayUrl = !UrlClickHandler::IsSuspicious(displayed)
? displayed
: parsed.isValid()
? QString::fromUtf8(parsed.toEncoded())
: UrlClickHandler::ShowEncoded(displayed);
const auto displayServer = QString(
displayUrl
).replace(
UrlStartRegExp,
QString()
).replace(UrlEndRegExp, QString());
const auto box = [=](not_null<Ui::GenericBox*> box) {
box->setTitle(tr::lng_proxy_box_title());
if (type == Type::Mtproto) {
box->addRow(object_ptr<Ui::FlatLabel>(
box,
tr::lng_proxy_sponsor_warning(),
st::boxDividerLabel));
Ui::AddSkip(box->verticalLayout());
Ui::AddSkip(box->verticalLayout());
}
const auto &stL = st::proxyApplyBoxLabel;
const auto &stSubL = st::boxDividerLabel;
const auto add = [&](const QString &s, tr::phrase<> phrase) {
if (!s.isEmpty()) {
box->addRow(object_ptr<Ui::FlatLabel>(box, s, stL));
box->addRow(object_ptr<Ui::FlatLabel>(box, phrase(), stSubL));
Ui::AddSkip(box->verticalLayout());
Ui::AddSkip(box->verticalLayout());
}
};
if (!displayServer.isEmpty()) {
add(displayServer, tr::lng_proxy_box_server);
}
add(QString::number(proxy.port), tr::lng_proxy_box_port);
if (type == Type::Socks5) {
add(proxy.user, tr::lng_proxy_box_username);
add(proxy.password, tr::lng_proxy_box_password);
} else if (type == Type::Mtproto) {
add(proxy.password, tr::lng_proxy_box_secret);
}
box->addButton(tr::lng_sure_enable(), [=] {
auto &proxies = Core::App().settings().proxy().list();
if (!ranges::contains(proxies, proxy)) {
proxies.push_back(proxy);
}
Core::App().setCurrentProxy(
proxy,
ProxyData::Settings::Enabled);
Core::App().setCurrentProxy(proxy, ProxyData::Settings::Enabled);
Local::writeSettings();
close();
};
Ui::show(
Ui::MakeConfirmBox({
.text = text,
.confirmed = std::move(callback),
.confirmText = tr::lng_sure_enable(),
}),
Ui::LayerOption::KeepOther);
box->closeBox();
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
};
if (controller) {
controller->uiShow()->showBox(Box(box));
} else {
Ui::show(Ui::MakeInformBox(
(proxy.status() == ProxyData::Status::Unsupported
? tr::lng_proxy_unsupported(tr::now)
: tr::lng_proxy_invalid(tr::now))));
Ui::show(Box(box));
}
}
@@ -1448,6 +1555,14 @@ object_ptr<Ui::BoxContent> ProxiesBoxController::addNewItemBox() {
});
}
bool ProxiesBoxController::contains(const ProxyData &proxy) const {
const auto j = ranges::find(
_list,
proxy,
[](const Item &item) { return item.data; });
return (j != end(_list));
}
void ProxiesBoxController::addNewItem(const ProxyData &proxy) {
auto &proxies = _settings.list();
proxies.push_back(proxy);

View File

@@ -30,6 +30,10 @@ namespace Main {
class Account;
} // namespace Main
namespace Window {
class SessionController;
} // namespace Window
class ProxiesBoxController {
public:
using ProxyData = MTP::ProxyData;
@@ -38,6 +42,7 @@ public:
explicit ProxiesBoxController(not_null<Main::Account*> account);
static void ShowApplyConfirmation(
Window::SessionController *controller,
Type type,
const QMap<QString, QString> &fields);
@@ -77,6 +82,9 @@ public:
void setTryIPv6(bool enabled);
rpl::producer<ProxyData::Settings> proxySettingsValue() const;
[[nodiscard]] bool contains(const ProxyData &proxy) const;
void addNewItem(const ProxyData &proxy);
rpl::producer<ItemView> views() const;
~ProxiesBoxController();
@@ -109,7 +117,6 @@ private:
void replaceItemValue(
std::vector<Item>::iterator which,
const ProxyData &proxy);
void addNewItem(const ProxyData &proxy);
const not_null<Main::Account*> _account;
Core::SettingsProxy &_settings;

View File

@@ -910,12 +910,12 @@ CreatePollBox::CreatePollBox(
PollData::Flags chosen,
PollData::Flags disabled,
Api::SendType sendType,
SendMenu::Type sendMenuType)
SendMenu::Details sendMenuDetails)
: _controller(controller)
, _chosen(chosen)
, _disabled(disabled)
, _sendType(sendType)
, _sendMenuType(sendMenuType) {
, _sendMenuDetails([result = sendMenuDetails] { return result; }) {
}
rpl::producer<CreatePollBox::Result> CreatePollBox::submitRequests() const {
@@ -1044,7 +1044,16 @@ not_null<Ui::InputField*> CreatePollBox::setupSolution(
solution->setInstantReplaces(Ui::InstantReplaces::Default());
solution->setInstantReplacesEnabled(
Core::App().settings().replaceEmojiValue());
solution->setMarkdownReplacesEnabled(rpl::single(true));
solution->setMarkdownReplacesEnabled(rpl::single(
Ui::MarkdownEnabledState{ Ui::MarkdownEnabled{ {
Ui::InputField::kTagBold,
Ui::InputField::kTagItalic,
Ui::InputField::kTagUnderline,
Ui::InputField::kTagStrikeOut,
Ui::InputField::kTagCode,
Ui::InputField::kTagSpoiler,
} } }
));
solution->setEditLinkCallback(
DefaultEditLinkCallback(_controller->uiShow(), solution));
solution->customTab(true);
@@ -1288,19 +1297,9 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
_submitRequests.fire({ collectResult(), sendOptions });
}
};
const auto sendSilent = [=] {
send({ .silent = true });
};
const auto sendScheduled = [=] {
_controller->show(
HistoryView::PrepareScheduleBox(
this,
SendMenu::Type::Scheduled,
send));
};
const auto sendWhenOnline = [=] {
send(Api::DefaultSendWhenOnlineOptions());
};
const auto sendAction = SendMenu::DefaultCallback(
_controller->uiShow(),
crl::guard(this, send));
options->scrollToWidget(
) | rpl::start_with_next([=](not_null<QWidget*> widget) {
@@ -1313,24 +1312,25 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
}, lifetime());
const auto isNormal = (_sendType == Api::SendType::Normal);
const auto schedule = [=] {
sendAction(
{ .type = SendMenu::ActionType::Schedule },
_sendMenuDetails());
};
const auto submit = addButton(
isNormal
(isNormal
? tr::lng_polls_create_button()
: tr::lng_schedule_button(),
[=] { isNormal ? send({}) : sendScheduled(); });
const auto sendMenuType = [=] {
: tr::lng_schedule_button()),
[=] { isNormal ? send({}) : schedule(); });
const auto sendMenuDetails = [=] {
collectError();
return (*error)
? SendMenu::Type::Disabled
: _sendMenuType;
return (*error) ? SendMenu::Details() : _sendMenuDetails();
};
SendMenu::SetupMenuAndShortcuts(
submit.data(),
sendMenuType,
sendSilent,
sendScheduled,
sendWhenOnline);
_controller->uiShow(),
sendMenuDetails,
sendAction);
addButton(tr::lng_cancel(), [=] { closeBox(); });
return result;

View File

@@ -27,7 +27,7 @@ class SessionController;
} // namespace Window
namespace SendMenu {
enum class Type;
struct Details;
} // namespace SendMenu
class CreatePollBox : public Ui::BoxContent {
@@ -43,7 +43,7 @@ public:
PollData::Flags chosen,
PollData::Flags disabled,
Api::SendType sendType,
SendMenu::Type sendMenuType);
SendMenu::Details sendMenuDetails);
[[nodiscard]] rpl::producer<Result> submitRequests() const;
void submitFailed(const QString &error);
@@ -75,7 +75,7 @@ private:
const PollData::Flags _chosen = PollData::Flags();
const PollData::Flags _disabled = PollData::Flags();
const Api::SendType _sendType = Api::SendType();
const SendMenu::Type _sendMenuType;
const Fn<SendMenu::Details()> _sendMenuDetails;
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;
Fn<void()> _setInnerFocus;
Fn<rpl::producer<bool>()> _dataIsValidValue;

View File

@@ -38,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "mainwidget.h" // controller->content() -> QWidget*
#include "menu/menu_send.h"
#include "mtproto/mtproto_config.h"
#include "platform/platform_specific.h"
#include "storage/localimageloader.h" // SendMediaType
@@ -175,7 +176,7 @@ void ChooseReplacement(
void EditPhotoImage(
not_null<Window::SessionController*> controller,
std::shared_ptr<Data::PhotoMedia> media,
bool wasSpoiler,
bool spoilered,
Fn<void(Ui::PreparedList)> done) {
const auto large = media
? media->image(Data::PhotoSize::Large)
@@ -198,7 +199,7 @@ void EditPhotoImage(
using ImageInfo = Ui::PreparedFileInformation::Image;
auto &file = list.files.front();
file.spoiler = wasSpoiler;
file.spoiler = spoilered;
const auto image = std::get_if<ImageInfo>(&file.information->media);
image->modifications = mods;
@@ -225,25 +226,18 @@ void EditPhotoImage(
} // namespace
EditCaptionBox::EditCaptionBox(
QWidget*,
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item)
: EditCaptionBox({}, controller, item, PrepareEditText(item), {}, {}) {
}
EditCaptionBox::EditCaptionBox(
QWidget*,
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item,
TextWithTags &&text,
bool spoilered,
bool invertCaption,
Ui::PreparedList &&list,
Fn<void()> saved)
: _controller(controller)
, _historyItem(item)
, _isAllowedEditMedia(item->media()
? item->media()->allowsEditMedia()
: false)
, _isAllowedEditMedia(item->media() && item->media()->allowsEditMedia())
, _albumType(ComputeAlbumType(item))
, _controls(base::make_unique_q<Ui::VerticalLayout>(this))
, _scroll(base::make_unique_q<Ui::ScrollArea>(this, st::boxScroll))
@@ -261,6 +255,8 @@ EditCaptionBox::EditCaptionBox(
Expects(item->media() != nullptr);
Expects(item->media()->allowsEditCaption());
_mediaEditManager.start(item, spoilered, invertCaption);
_controller->session().data().itemRemoved(
_historyItem->fullId()
) | rpl::start_with_next([=] {
@@ -274,6 +270,8 @@ void EditCaptionBox::StartMediaReplace(
not_null<Window::SessionController*> controller,
FullMsgId itemId,
TextWithTags text,
bool spoilered,
bool invertCaption,
Fn<void()> saved) {
const auto session = &controller->session();
const auto item = session->data().message(itemId);
@@ -285,6 +283,8 @@ void EditCaptionBox::StartMediaReplace(
controller,
item,
std::move(text),
spoilered,
invertCaption,
std::move(list),
std::move(saved)));
};
@@ -299,6 +299,8 @@ void EditCaptionBox::StartMediaReplace(
FullMsgId itemId,
Ui::PreparedList &&list,
TextWithTags text,
bool spoilered,
bool invertCaption,
Fn<void()> saved) {
const auto session = &controller->session();
const auto item = session->data().message(itemId);
@@ -332,6 +334,8 @@ void EditCaptionBox::StartMediaReplace(
controller,
item,
std::move(text),
spoilered,
invertCaption,
std::move(list),
std::move(saved)));
}
@@ -342,14 +346,15 @@ void EditCaptionBox::StartPhotoEdit(
std::shared_ptr<Data::PhotoMedia> media,
FullMsgId itemId,
TextWithTags text,
bool spoilered,
bool invertCaption,
Fn<void()> saved) {
const auto session = &controller->session();
const auto item = session->data().message(itemId);
if (!item) {
return;
}
const auto hasSpoiler = item->media() && item->media()->hasSpoiler();
EditPhotoImage(controller, media, hasSpoiler, [=](
EditPhotoImage(controller, media, spoilered, [=](
Ui::PreparedList &&list) mutable {
const auto item = session->data().message(itemId);
if (!item) {
@@ -359,15 +364,48 @@ void EditCaptionBox::StartPhotoEdit(
controller,
item,
std::move(text),
spoilered,
invertCaption,
std::move(list),
std::move(saved)));
});
}
void EditCaptionBox::prepare() {
addButton(tr::lng_settings_save(), [=] { save(); });
const auto button = addButton(tr::lng_settings_save(), [=] { save(); });
addButton(tr::lng_cancel(), [=] { closeBox(); });
const auto details = crl::guard(this, [=] {
auto result = SendMenu::Details();
const auto allWithSpoilers = ranges::all_of(
_preparedList.files,
&Ui::PreparedFile::spoiler);
result.spoiler = !_preparedList.hasSpoilerMenu(!_asFile)
? SendMenu::SpoilerState::None
: allWithSpoilers
? SendMenu::SpoilerState::Enabled
: SendMenu::SpoilerState::Possible;
const auto canMoveCaption = _preparedList.canMoveCaption(
false,
!_asFile
) && _field && HasSendText(_field);
result.caption = !canMoveCaption
? SendMenu::CaptionState::None
: _mediaEditManager.invertCaption()
? SendMenu::CaptionState::Above
: SendMenu::CaptionState::Below;
return result;
});
const auto callback = [=](SendMenu::Action action, const auto &) {
_mediaEditManager.apply(action);
rebuildPreview();
};
SendMenu::SetupMenuAndShortcuts(
button,
nullptr,
details,
crl::guard(this, callback));
updateBoxSize();
setupField();
@@ -396,7 +434,6 @@ void EditCaptionBox::rebuildPreview() {
applyChanges();
_previewHasSpoiler = nullptr;
if (_preparedList.files.empty()) {
const auto media = _historyItem->media();
const auto photo = media->photo();
@@ -426,11 +463,18 @@ void EditCaptionBox::rebuildPreview() {
st::defaultComposeControls,
gifPaused,
file,
[] { return true; },
Ui::AttachControls::Type::EditOnly);
_isPhoto = (media && media->isPhoto());
const auto withCheckbox = _isPhoto && CanBeCompressed(_albumType);
if (media && (!withCheckbox || !_asFile)) {
_previewHasSpoiler = [media] { return media->hasSpoiler(); };
media->spoileredChanges(
) | rpl::start_with_next([=](bool spoilered) {
_mediaEditManager.apply({ .type = spoilered
? SendMenu::ActionType::SpoilerOn
: SendMenu::ActionType::SpoilerOff
});
}, media->lifetime());
_content.reset(media);
} else {
_content.reset(Ui::CreateChild<Ui::SingleFilePreview>(
@@ -757,10 +801,7 @@ bool EditCaptionBox::setPreparedList(Ui::PreparedList &&list) {
}
bool EditCaptionBox::hasSpoiler() const {
return _preparedList.files.empty()
? (_historyItem->media()
&& _historyItem->media()->hasSpoiler())
: _preparedList.files.front().spoiler;
return _mediaEditManager.spoilered();
}
void EditCaptionBox::captionResized() {
@@ -869,8 +910,8 @@ bool EditCaptionBox::validateLength(const QString &text) const {
}
void EditCaptionBox::applyChanges() {
if (!_preparedList.files.empty() && _previewHasSpoiler) {
_preparedList.files.front().spoiler = _previewHasSpoiler();
if (!_preparedList.files.empty()) {
_preparedList.files.front().spoiler = _mediaEditManager.spoilered();
}
}
@@ -899,6 +940,7 @@ void EditCaptionBox::save() {
auto options = Api::SendOptions();
options.scheduled = item->isScheduled() ? item->date() : 0;
options.shortcutId = item->shortcutId();
options.invertCaption = _mediaEditManager.invertCaption();
if (!_preparedList.files.empty()) {
if ((_albumType != Ui::AlbumType::None)

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "history/view/controls/history_view_compose_media_edit_manager.h"
#include "ui/layers/box_content.h"
#include "ui/chat/attach/attach_prepare.h"
@@ -32,15 +33,13 @@ enum class AlbumType;
class EditCaptionBox final : public Ui::BoxContent {
public:
EditCaptionBox(
QWidget*,
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item);
EditCaptionBox(
QWidget*,
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item,
TextWithTags &&text,
bool spoilered,
bool invertCaption,
Ui::PreparedList &&list,
Fn<void()> saved);
~EditCaptionBox();
@@ -49,18 +48,24 @@ public:
not_null<Window::SessionController*> controller,
FullMsgId itemId,
TextWithTags text,
bool spoilered,
bool invertCaption,
Fn<void()> saved);
static void StartMediaReplace(
not_null<Window::SessionController*> controller,
FullMsgId itemId,
Ui::PreparedList &&list,
TextWithTags text,
bool spoilered,
bool invertCaption,
Fn<void()> saved);
static void StartPhotoEdit(
not_null<Window::SessionController*> controller,
std::shared_ptr<Data::PhotoMedia> media,
FullMsgId itemId,
TextWithTags text,
bool spoilered,
bool invertCaption,
Fn<void()> saved);
protected:
@@ -111,7 +116,6 @@ private:
const base::unique_qptr<Ui::EmojiButton> _emojiToggle;
base::unique_qptr<Ui::AbstractSinglePreview> _content;
Fn<bool()> _previewHasSpoiler;
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;
base::unique_qptr<QObject> _emojiFilter;
@@ -122,6 +126,7 @@ private:
std::shared_ptr<Data::PhotoMedia> _photoMedia;
Ui::PreparedList _preparedList;
HistoryView::MediaEditManager _mediaEditManager;
mtpRequestId _saveRequestId = 0;

View File

@@ -83,6 +83,7 @@ not_null<FilterChatsPreview*> SetupChatsPreview(
rules.id(),
rules.title(),
rules.iconEmoji(),
rules.colorIndex(),
(rules.flags() & ~flag),
rules.always(),
rules.pinned(),
@@ -104,6 +105,7 @@ not_null<FilterChatsPreview*> SetupChatsPreview(
rules.id(),
rules.title(),
rules.iconEmoji(),
rules.colorIndex(),
rules.flags(),
std::move(always),
std::move(pinned),
@@ -170,6 +172,7 @@ void EditExceptions(
rules.id(),
rules.title(),
rules.iconEmoji(),
rules.colorIndex(),
((rules.flags() & ~options)
| rawController->chosenOptions()),
include ? std::move(changed) : std::move(removeFrom),
@@ -240,6 +243,7 @@ void CreateIconSelector(
rules.id(),
rules.title(),
Ui::LookupFilterIcon(icon).emoji,
rules.colorIndex(),
rules.flags(),
rules.always(),
rules.pinned(),

View File

@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_boosts.h"
#include "data/data_changes.h"
#include "data/data_channel.h"
#include "data/data_credits.h"
#include "data/data_media_types.h" // Data::GiveawayStart.
#include "data/data_peer_values.h" // Data::PeerPremiumValue.
#include "data/data_session.h"
@@ -1010,14 +1011,16 @@ void GiftPremiumValidator::showChoosePeerBox(const QString &ref) {
}) | ranges::views::filter([](UserData *u) -> bool {
return u;
}) | ranges::to<std::vector<not_null<UserData*>>>();
if (!users.empty()) {
const auto giftBox = show->show(
Box(GiftsBox, _controller, users, api, ref));
giftBox->boxClosing(
) | rpl::start_with_next([=] {
_manyGiftsLifetime.destroy();
}, giftBox->lifetime());
if (users.empty()) {
show->showToast(
tr::lng_settings_gift_premium_choose(tr::now));
}
const auto giftBox = show->show(
Box(GiftsBox, _controller, users, api, ref));
giftBox->boxClosing(
) | rpl::start_with_next([=] {
_manyGiftsLifetime.destroy();
}, giftBox->lifetime());
(*ignoreClose) = true;
peersBox->closeBox();
};
@@ -1147,16 +1150,18 @@ void GiftCodeBox(
object_ptr<Ui::Premium::TopBar>(
box,
st::giveawayGiftCodeCover,
nullptr,
rpl::conditional(
state->used.value(),
tr::lng_gift_link_used_title(),
tr::lng_gift_link_title()),
rpl::conditional(
state->used.value(),
tr::lng_gift_link_used_about(Ui::Text::RichLangValue),
tr::lng_gift_link_about(Ui::Text::RichLangValue)),
true));
Ui::Premium::TopBarDescriptor{
.clickContextOther = nullptr,
.title = rpl::conditional(
state->used.value(),
tr::lng_gift_link_used_title(),
tr::lng_gift_link_title()),
.about = rpl::conditional(
state->used.value(),
tr::lng_gift_link_used_about(Ui::Text::RichLangValue),
tr::lng_gift_link_about(Ui::Text::RichLangValue)),
.light = true,
}));
const auto max = st::giveawayGiftCodeTopHeight;
bar->setMaximumHeight(max);
@@ -1283,13 +1288,15 @@ void GiftCodePendingBox(
object_ptr<Ui::Premium::TopBar>(
box,
st,
clickContext,
tr::lng_gift_link_title(),
tr::lng_gift_link_pending_about(
lt_user,
rpl::single(Ui::Text::Link(resultToName)),
Ui::Text::RichLangValue),
true));
Ui::Premium::TopBarDescriptor{
.clickContextOther = clickContext,
.title = tr::lng_gift_link_title(),
.about = tr::lng_gift_link_pending_about(
lt_user,
rpl::single(Ui::Text::Link(resultToName)),
Ui::Text::RichLangValue),
.light = true,
}));
const auto max = st::giveawayGiftCodeTopHeight;
bar->setMaximumHeight(max);
@@ -1629,3 +1636,119 @@ void ResolveGiveawayInfo(
messageId,
crl::guard(controller, show));
}
void AddCreditsHistoryEntryTable(
not_null<Window::SessionNavigation*> controller,
not_null<Ui::VerticalLayout*> container,
const Data::CreditsHistoryEntry &entry) {
auto table = container->add(
object_ptr<Ui::TableLayout>(
container,
st::giveawayGiftCodeTable),
st::giveawayGiftCodeTableMargin);
const auto peerId = PeerId(entry.barePeerId);
if (peerId) {
auto text = entry.in
? tr::lng_credits_box_history_entry_peer_in()
: tr::lng_credits_box_history_entry_peer();
AddTableRow(table, std::move(text), controller, peerId);
}
if (const auto msgId = MsgId(peerId ? entry.bareMsgId : 0)) {
const auto session = &controller->session();
const auto peer = session->data().peer(peerId);
if (const auto channel = peer->asBroadcast()) {
const auto username = channel->username();
const auto base = username.isEmpty()
? u"c/%1"_q.arg(peerToChannel(channel->id).bare)
: username;
const auto query = base + '/' + QString::number(msgId.bare);
const auto link = session->createInternalLink(query);
auto label = object_ptr<Ui::FlatLabel>(
table,
rpl::single(Ui::Text::Link(link)),
st::giveawayGiftCodeValue);
label->setClickHandlerFilter([=](const auto &...) {
controller->showPeerHistory(channel, {}, msgId);
return false;
});
AddTableRow(
table,
tr::lng_credits_box_history_entry_media(),
std::move(label),
st::giveawayGiftCodeValueMargin);
}
}
using Type = Data::CreditsHistoryEntry::PeerType;
if (entry.peerType == Type::AppStore) {
AddTableRow(
table,
tr::lng_credits_box_history_entry_via(),
tr::lng_credits_box_history_entry_app_store(
Ui::Text::RichLangValue));
} else if (entry.peerType == Type::PlayMarket) {
AddTableRow(
table,
tr::lng_credits_box_history_entry_via(),
tr::lng_credits_box_history_entry_play_market(
Ui::Text::RichLangValue));
} else if (entry.peerType == Type::Fragment) {
AddTableRow(
table,
tr::lng_credits_box_history_entry_via(),
tr::lng_credits_box_history_entry_fragment(
Ui::Text::RichLangValue));
} else if (entry.peerType == Type::Ads) {
AddTableRow(
table,
tr::lng_credits_box_history_entry_via(),
tr::lng_credits_box_history_entry_ads(Ui::Text::RichLangValue));
} else if (entry.peerType == Type::PremiumBot) {
AddTableRow(
table,
tr::lng_credits_box_history_entry_via(),
tr::lng_credits_box_history_entry_via_premium_bot(
Ui::Text::RichLangValue));
}
if (!entry.id.isEmpty()) {
constexpr auto kOneLineCount = 18;
const auto oneLine = entry.id.length() <= kOneLineCount;
auto label = object_ptr<Ui::FlatLabel>(
table,
rpl::single(
Ui::Text::Wrapped({ entry.id }, EntityType::Code, {})),
oneLine
? st::giveawayGiftCodeValue
: st::giveawayGiftCodeValueMultiline);
label->setClickHandlerFilter([=](const auto &...) {
TextUtilities::SetClipboardText(
TextForMimeData::Simple(entry.id));
controller->showToast(
tr::lng_credits_box_history_entry_id_copied(tr::now));
return false;
});
AddTableRow(
table,
tr::lng_credits_box_history_entry_id(),
std::move(label),
st::giveawayGiftCodeValueMargin);
}
if (!entry.date.isNull()) {
AddTableRow(
table,
tr::lng_gift_link_label_date(),
rpl::single(Ui::Text::WithEntities(langDateTime(entry.date))));
}
if (!entry.successDate.isNull()) {
AddTableRow(
table,
tr::lng_credits_box_history_entry_success_date(),
rpl::single(Ui::Text::WithEntities(langDateTime(entry.date))));
}
if (!entry.successLink.isEmpty()) {
AddTableRow(
table,
tr::lng_credits_box_history_entry_success_url(),
rpl::single(
Ui::Text::Link(entry.successLink, entry.successLink)));
}
}

View File

@@ -16,12 +16,14 @@ struct GiftCode;
} // namespace Api
namespace Data {
struct CreditsHistoryEntry;
struct GiveawayStart;
struct GiveawayResults;
} // namespace Data
namespace Ui {
class GenericBox;
class VerticalLayout;
} // namespace Ui
namespace Window {
@@ -71,3 +73,8 @@ void ResolveGiveawayInfo(
MsgId messageId,
std::optional<Data::GiveawayStart> start,
std::optional<Data::GiveawayResults> results);
void AddCreditsHistoryEntryTable(
not_null<Window::SessionNavigation*> controller,
not_null<Ui::VerticalLayout*> container,
const Data::CreditsHistoryEntry &entry);

View File

@@ -133,8 +133,8 @@ void MaxInviteBox::paintEvent(QPaintEvent *e) {
auto option = QTextOption(style::al_left);
option.setWrapMode(QTextOption::WrapAnywhere);
p.setFont(_linkOver
? st::defaultInputField.font->underline()
: st::defaultInputField.font);
? st::defaultInputField.style.font->underline()
: st::defaultInputField.style.font);
p.setPen(st::defaultLinkButton.color);
const auto inviteLinkText = _channel->inviteLink().isEmpty()
? tr::lng_group_invite_create(tr::now)

View File

@@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_widgets.h"
#include <xxhash.h> // XXH64.
#include <QtWidgets/QApplication>
[[nodiscard]] PeerListRowId UniqueRowIdFromString(const QString &d) {
return XXH64(d.data(), d.size() * sizeof(ushort), 0);
@@ -1552,15 +1553,44 @@ void PeerListContent::handleMouseMove(QPoint globalPosition) {
&& *_lastMousePosition == globalPosition) {
return;
}
if (_trackPressStart
&& ((*_trackPressStart - globalPosition).manhattanLength()
> QApplication::startDragDistance())) {
_trackPressStart = {};
_controller->rowTrackPressCancel();
}
if (!_controller->rowTrackPressSkipMouseSelection()) {
selectByMouse(globalPosition);
}
}
void PeerListContent::pressLeftToContextMenu(bool shown) {
if (shown) {
setContexted(_pressed);
setPressed(Selected());
} else {
setContexted(Selected());
}
}
bool PeerListContent::trackRowPressFromGlobal(QPoint globalPosition) {
selectByMouse(globalPosition);
if (const auto row = getRow(_selected.index)) {
if (_controller->rowTrackPress(row)) {
_trackPressStart = globalPosition;
return true;
}
}
return false;
}
void PeerListContent::mousePressEvent(QMouseEvent *e) {
_pressButton = e->button();
selectByMouse(e->globalPos());
setPressed(_selected);
if (auto row = getRow(_selected.index)) {
auto updateCallback = [this, row, hint = _selected.index] {
_trackPressStart = {};
if (const auto row = getRow(_selected.index)) {
const auto updateCallback = [this, row, hint = _selected.index] {
updateRow(row, hint);
};
if (_selected.element) {
@@ -1586,8 +1616,11 @@ void PeerListContent::mousePressEvent(QMouseEvent *e) {
row->addRipple(_st.item, maskGenerator, point, std::move(updateCallback));
}
}
if (_pressButton == Qt::LeftButton && _controller->rowTrackPress(row)) {
_trackPressStart = e->globalPos();
}
}
if (anim::Disabled() && !_selected.element) {
if (anim::Disabled() && !_trackPressStart && !_selected.element) {
mousePressReleased(e->button());
}
}
@@ -1597,6 +1630,9 @@ void PeerListContent::mouseReleaseEvent(QMouseEvent *e) {
}
void PeerListContent::mousePressReleased(Qt::MouseButton button) {
_trackPressStart = {};
_controller->rowTrackPressCancel();
updateRow(_pressed.index);
updateRow(_selected.index);

View File

@@ -348,6 +348,9 @@ public:
virtual int peerListPartitionRows(Fn<bool(const PeerListRow &a)> border) = 0;
virtual std::shared_ptr<Main::SessionShow> peerListUiShow() = 0;
virtual void peerListPressLeftToContextMenu(bool shown) = 0;
virtual bool peerListTrackRowPressFromGlobal(QPoint globalPosition) = 0;
template <typename PeerDataRange>
void peerListAddSelectedPeers(PeerDataRange &&range) {
for (const auto peer : range) {
@@ -478,6 +481,15 @@ public:
}
}
virtual bool rowTrackPress(not_null<PeerListRow*> row) {
return false;
}
virtual void rowTrackPressCancel() {
}
virtual bool rowTrackPressSkipMouseSelection() {
return false;
}
virtual void loadMoreRows() {
}
virtual void itemDeselectedHook(not_null<PeerData*> peer) {
@@ -655,6 +667,8 @@ public:
void refreshRows();
void mouseLeftGeometry();
void pressLeftToContextMenu(bool shown);
bool trackRowPressFromGlobal(QPoint globalPosition);
void setSearchMode(PeerListSearchMode mode);
void changeCheckState(
@@ -829,6 +843,7 @@ private:
bool _mouseSelection = false;
std::optional<QPoint> _lastMousePosition;
Qt::MouseButton _pressButton = Qt::LeftButton;
std::optional<QPoint> _trackPressStart;
rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
@@ -992,6 +1007,13 @@ public:
bool highlightRow,
Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr) override;
void peerListPressLeftToContextMenu(bool shown) override {
_content->pressLeftToContextMenu(shown);
}
bool peerListTrackRowPressFromGlobal(QPoint globalPosition) override {
return _content->trackRowPressFromGlobal(globalPosition);
}
protected:
not_null<PeerListContent*> content() const {
return _content;

View File

@@ -42,16 +42,14 @@ namespace {
constexpr auto kDefaultIconId = DocumentId(0x7FFF'FFFF'FFFF'FFFFULL);
struct DefaultIcon {
QString title;
int32 colorId = 0;
};
using DefaultIcon = Data::TopicIconDescriptor;
class DefaultIconEmoji final : public Ui::Text::CustomEmoji {
public:
DefaultIconEmoji(
rpl::producer<DefaultIcon> value,
Fn<void()> repaint);
Fn<void()> repaint,
Data::CustomEmojiSizeTag tag);
int width() override;
QString entityData() override;
@@ -64,14 +62,17 @@ public:
private:
DefaultIcon _icon = {};
QImage _image;
Data::CustomEmojiSizeTag _tag = {};
rpl::lifetime _lifetime;
};
DefaultIconEmoji::DefaultIconEmoji(
rpl::producer<DefaultIcon> value,
Fn<void()> repaint) {
rpl::producer<DefaultIcon> value,
Fn<void()> repaint,
Data::CustomEmojiSizeTag tag)
: _tag(tag) {
std::move(value) | rpl::start_with_next([=](DefaultIcon value) {
_icon = value;
_image = QImage();
@@ -88,15 +89,22 @@ QString DefaultIconEmoji::entityData() {
}
void DefaultIconEmoji::paint(QPainter &p, const Context &context) {
const auto &st = (_tag == Data::CustomEmojiSizeTag::Normal)
? st::normalForumTopicIcon
: st::defaultForumTopicIcon;
if (_image.isNull()) {
_image = Data::ForumTopicIconFrame(
_icon.colorId,
_icon.title,
st::defaultForumTopicIcon);
_image = Data::IsForumGeneralIconTitle(_icon.title)
? Data::ForumTopicGeneralIconFrame(
st.size,
Data::ParseForumGeneralIconColor(_icon.colorId))
: Data::ForumTopicIconFrame(_icon.colorId, _icon.title, st);
}
const auto esize = Ui::Emoji::GetSizeLarge() / style::DevicePixelRatio();
const auto full = (_tag == Data::CustomEmojiSizeTag::Normal)
? Ui::Emoji::GetSizeNormal()
: Ui::Emoji::GetSizeLarge();
const auto esize = full / style::DevicePixelRatio();
const auto customSize = Ui::Text::AdjustCustomEmojiSize(esize);
const auto skip = (customSize - st::defaultForumTopicIcon.size) / 2;
const auto skip = (customSize - st.size) / 2;
p.drawImage(context.position + QPoint(skip, skip), _image);
}
@@ -212,7 +220,7 @@ bool DefaultIconEmoji::readyInDefaultState() {
) | rpl::start_with_next([=] {
state->frame = Data::ForumTopicGeneralIconFrame(
st::largeForumTopicIcon.size,
st::windowSubTextFg);
st::windowSubTextFg->c);
result->update();
}, result->lifetime());
@@ -261,7 +269,8 @@ struct IconSelector {
if (id == kDefaultIconId) {
return std::make_unique<DefaultIconEmoji>(
rpl::duplicate(defaultIcon),
repaint);
std::move(repaint),
tag);
}
return manager->create(id, std::move(repaint), tag);
};
@@ -572,3 +581,13 @@ void EditForumTopicBox(
box->closeBox();
});
}
std::unique_ptr<Ui::Text::CustomEmoji> MakeTopicIconEmoji(
Data::TopicIconDescriptor descriptor,
Fn<void()> repaint,
Data::CustomEmojiSizeTag tag) {
return std::make_unique<DefaultIconEmoji>(
rpl::single(descriptor),
std::move(repaint),
tag);
}

View File

@@ -11,6 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class History;
namespace Data {
struct TopicIconDescriptor;
enum class CustomEmojiSizeTag : uchar;
} // namespace Data
namespace Window {
class SessionController;
} // namespace Window
@@ -25,3 +30,8 @@ void EditForumTopicBox(
not_null<Window::SessionController*> controller,
not_null<History*> forum,
MsgId rootId);
[[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> MakeTopicIconEmoji(
Data::TopicIconDescriptor descriptor,
Fn<void()> repaint,
Data::CustomEmojiSizeTag tag);

View File

@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peers/edit_peer_info_box.h"
#include "apiwrap.h"
#include "api/api_credits.h"
#include "api/api_peer_photo.h"
#include "api/api_user_names.h"
#include "main/main_session.h"
@@ -42,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_premium_limits.h"
#include "data/data_user.h"
#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/profile/info_profile_values.h"
#include "info/info_memento.h"
@@ -52,6 +54,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/boost_box.h"
#include "ui/controls/emoji_button.h"
#include "ui/controls/userpic_button.h"
#include "ui/effects/premium_graphics.h"
#include "ui/rect.h"
#include "ui/rp_widget.h"
#include "ui/vertical_list.h"
#include "ui/toast/toast.h"
@@ -71,6 +75,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_boxes.h"
#include "styles/style_info.h"
#include <QtSvg/QSvgRenderer>
namespace {
constexpr auto kBotManagerUsername = "BotFather"_cs;
@@ -343,6 +349,7 @@ private:
void fillPendingRequestsButton();
void fillBotUsernamesButton();
void fillBotBalanceButton();
void fillBotEditIntroButton();
void fillBotEditCommandsButton();
void fillBotEditSettingsButton();
@@ -415,7 +422,12 @@ private:
std::deque<FnMut<void()>> _saveStagesQueue;
Saving _savingData;
const rpl::event_stream<Privacy> _privacyTypeUpdates;
struct PrivacyAndForwards {
Privacy privacy;
bool noForwards = false;
};
const rpl::event_stream<PrivacyAndForwards> _privacyTypeUpdates;
const rpl::event_stream<ChannelData*> _linkedChatUpdates;
mtpRequestId _linkedChatsRequestId = 0;
@@ -761,7 +773,7 @@ void Controller::refreshHistoryVisibility() {
void Controller::showEditPeerTypeBox(
std::optional<rpl::producer<QString>> error) {
const auto boxCallback = crl::guard(this, [=](EditPeerTypeData data) {
_privacyTypeUpdates.fire_copy(data.privacy);
_privacyTypeUpdates.fire({ data.privacy, data.noForwards });
_typeDataSavedValue = data;
refreshHistoryVisibility();
});
@@ -882,7 +894,8 @@ void Controller::fillPrivacyTypeButton() {
? tr::lng_manage_peer_group_type
: tr::lng_manage_peer_channel_type)(),
_privacyTypeUpdates.events(
) | rpl::map([=](Privacy flag) {
) | rpl::map([=](PrivacyAndForwards data) {
const auto flag = data.privacy;
if (flag == Privacy::HasUsername) {
_peer->session().api().usernames().requestToCache(_peer);
}
@@ -894,14 +907,21 @@ void Controller::fillPrivacyTypeButton() {
: tr::lng_manage_public_peer_title)()
: (hasLocation
? tr::lng_manage_peer_link_invite
: isGroup
: ((!data.noForwards) && isGroup)
? tr::lng_manage_private_group_title
: tr::lng_manage_private_peer_title)();
: ((!data.noForwards) && !isGroup)
? tr::lng_manage_private_peer_title
: isGroup
? tr::lng_manage_private_group_noforwards_title
: tr::lng_manage_private_peer_noforwards_title)();
}) | rpl::flatten_latest(),
[=] { showEditPeerTypeBox(); },
{ &st::menuIconCustomize });
_privacyTypeUpdates.fire_copy(_typeDataSavedValue->privacy);
_privacyTypeUpdates.fire_copy({
_typeDataSavedValue->privacy,
_typeDataSavedValue->noForwards,
});
}
void Controller::fillLinkedChatButton() {
@@ -1113,6 +1133,7 @@ void Controller::fillManageSection() {
::AddSkip(container, 0);
fillBotUsernamesButton();
fillBotBalanceButton();
fillBotEditIntroButton();
fillBotEditCommandsButton();
fillBotEditSettingsButton();
@@ -1523,6 +1544,84 @@ void Controller::fillBotUsernamesButton() {
{ &st::menuIconLinks });
}
void Controller::fillBotBalanceButton() {
Expects(_isBot);
struct State final {
rpl::variable<QString> balance;
};
auto &lifetime = _controls.buttonsLayout->lifetime();
const auto state = lifetime.make_state<State>();
const auto wrap = _controls.buttonsLayout->add(
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
_controls.buttonsLayout,
EditPeerInfoBox::CreateButton(
_controls.buttonsLayout,
tr::lng_manage_peer_bot_balance(),
state->balance.value(),
[controller = _navigation->parentController(), peer = _peer] {
controller->showSection(Info::BotEarn::Make(peer));
},
st::manageGroupButton,
{})));
wrap->toggle(false, anim::type::instant);
const auto button = wrap->entity();
{
const auto api = button->lifetime().make_state<Api::CreditsStatus>(
_peer);
api->request({}, [=](Data::CreditsStatusSlice data) {
if (data.balance) {
wrap->toggle(true, anim::type::normal);
}
state->balance = QString::number(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([=] {
auto p = QPainter(icon);
svg->render(&p, Rect(starSize));
}, icon->lifetime());
button->sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
icon->moveToLeft(
button->st().iconLeft + kSizeShift / 2.,
(size.height() - icon->height()) / 2);
}, icon->lifetime());
}
}
void Controller::fillBotEditIntroButton() {
Expects(_isBot);
@@ -2101,8 +2200,11 @@ void Controller::saveForum() {
channel->inputChannel,
MTP_bool(*_savingData.forum)
)).done([=](const MTPUpdates &result) {
const auto weak = base::make_weak(this);
channel->session().api().applyUpdates(result);
continueSave();
if (weak) { // todo better to be able to save in closed already box.
continueSave();
}
}).fail([=](const MTP::Error &error) {
if (error.type() == u"CHAT_NOT_MODIFIED"_q) {
continueSave();

View File

@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/event_filter.h"
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "core/ui_integration.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_document.h"
@@ -351,8 +352,8 @@ object_ptr<Ui::RpWidget> AddReactionsSelector(
const auto customEmojiPaused = [controller = args.controller] {
return controller->isGifPausedAtLeastFor(PauseReason::Layer);
};
raw->setCustomEmojiFactory([=](QStringView data, Fn<void()> update)
-> std::unique_ptr<Ui::Text::CustomEmoji> {
auto factory = [=](QStringView data, Fn<void()> update)
-> std::unique_ptr<Ui::Text::CustomEmoji> {
const auto id = Data::ParseCustomEmojiData(data);
auto result = owner->customEmojiManager().create(
data,
@@ -364,7 +365,13 @@ object_ptr<Ui::RpWidget> AddReactionsSelector(
}
using namespace Ui::Text;
return std::make_unique<FirstFrameEmoji>(std::move(result));
}, std::move(customEmojiPaused));
};
raw->setCustomTextContext([=](Fn<void()> repaint) {
return std::any(Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = std::move(repaint),
});
}, customEmojiPaused, customEmojiPaused, std::move(factory));
const auto callback = args.callback;
const auto isCustom = [=](DocumentId id) {

View File

@@ -398,7 +398,7 @@ void PeerShortInfoCover::paintRadial(QPainter &p) {
QImage PeerShortInfoCover::currentVideoFrame() const {
const auto size = QSize(_st.size, _st.size);
const auto request = Media::Streaming::FrameRequest{
.resize = size * style::DevicePixelRatio(),
.resize = size,
.outer = size,
};
return (_videoInstance

View File

@@ -459,6 +459,7 @@ Ui::BoostFeatures LookupBoostFeatures(not_null<ChannelData*> channel) {
.customWallpaperLevel = group
? levelLimits.groupCustomWallpaperLevelMin()
: levelLimits.channelCustomWallpaperLevelMin(),
.sponsoredLevel = levelLimits.channelRestrictSponsoredLevelMin(),
};
}

View File

@@ -418,7 +418,9 @@ void SimpleLimitBox(
BoxShowFinishes(box),
0,
descriptor.current,
descriptor.premiumLimit,
(descriptor.complexRatio
? descriptor.premiumLimit
: 2 * descriptor.current),
premiumPossible,
descriptor.phrase,
descriptor.icon);
@@ -769,7 +771,7 @@ void FilterLinksLimitBox(
premiumLimit,
&st::premiumIconChats,
std::nullopt,
true });
/*true */}); // Don't use real ratio, "Free" doesn't fit.
}
@@ -856,7 +858,7 @@ void ShareableFiltersLimitBox(
premiumLimit,
&st::premiumIconFolders,
std::nullopt,
true });
/*true*/ }); // Don't use real ratio, "Free" doesn't fit.
}
void FilterPinsLimitBox(

View File

@@ -131,6 +131,8 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
return tr::lng_premium_summary_subtitle_translation();
case PremiumFeature::Business:
return tr::lng_premium_summary_subtitle_business();
case PremiumFeature::Effects:
return tr::lng_premium_summary_subtitle_effects();
case PremiumFeature::BusinessLocation:
return tr::lng_business_subtitle_location();
@@ -192,6 +194,8 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
return tr::lng_premium_summary_about_translation();
case PremiumFeature::Business:
return tr::lng_premium_summary_about_business();
case PremiumFeature::Effects:
return tr::lng_premium_summary_about_effects();
case PremiumFeature::BusinessLocation:
return tr::lng_business_about_location();
@@ -286,7 +290,7 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
document,
media->videoThumbnailContent(),
QString(),
true);
Stickers::EffectType::PremiumSticker);
const auto update = [=] {
if (!state->readyInvoked
@@ -529,6 +533,7 @@ struct VideoPreviewDocument {
case PremiumFeature::Wallpapers: return "wallpapers";
case PremiumFeature::LastSeen: return "last_seen";
case PremiumFeature::MessagePrivacy: return "message_privacy";
case PremiumFeature::Effects: return "effects";
case PremiumFeature::BusinessLocation: return "business_location";
case PremiumFeature::BusinessHours: return "business_hours";

View File

@@ -70,6 +70,7 @@ enum class PremiumFeature {
LastSeen,
MessagePrivacy,
Business,
Effects,
// Business features.
BusinessLocation,

View File

@@ -16,8 +16,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/admin_log/history_admin_log_item.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/view/history_view_element.h"
#include "history/view/reactions/history_view_reactions_strip.h"
#include "history/view/history_view_element.h"
#include "history/view/history_view_fake_items.h"
#include "lang/lang_keys.h"
#include "boxes/premium_preview_box.h"
#include "main/main_session.h"
@@ -43,53 +44,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
PeerId GenerateUser(not_null<History*> history, const QString &name) {
Expects(history->peer->isUser());
const auto peerId = Data::FakePeerIdForJustName(name);
history->owner().processUser(MTP_user(
MTP_flags(MTPDuser::Flag::f_first_name | MTPDuser::Flag::f_min),
peerToBareMTPInt(peerId),
MTP_long(0),
MTP_string(tr::lng_settings_chat_message_reply_from(tr::now)),
MTPstring(), // last name
MTPstring(), // username
MTPstring(), // phone
MTPUserProfilePhoto(), // profile photo
MTPUserStatus(), // status
MTP_int(0), // bot info version
MTPVector<MTPRestrictionReason>(), // restrictions
MTPstring(), // bot placeholder
MTPstring(), // lang code
MTPEmojiStatus(),
MTPVector<MTPUsername>(),
MTPint(), // stories_max_id
MTPPeerColor(), // color
MTPPeerColor())); // profile_color
return peerId;
}
AdminLog::OwnedItem GenerateItem(
not_null<HistoryView::ElementDelegate*> delegate,
not_null<History*> history,
PeerId from,
FullMsgId replyTo,
const QString &text) {
Expects(history->peer->isUser());
const auto item = history->addNewLocalMessage({
.id = history->nextNonHistoryEntryId(),
.flags = (MessageFlag::FakeHistoryItem
| MessageFlag::HasFromId
| MessageFlag::HasReplyInfo),
.from = from,
.replyTo = FullReplyTo{ .messageId = replyTo },
.date = base::unixtime::now(),
}, TextWithEntities{ .text = text }, MTP_messageMediaEmpty());
return AdminLog::OwnedItem(delegate, item);
}
void AddMessage(
not_null<Ui::VerticalLayout*> container,
not_null<Window::SessionController*> controller,
@@ -135,15 +89,15 @@ void AddMessage(
const auto history = controller->session().data().history(
PeerData::kServiceNotificationsId);
state->reply = GenerateItem(
state->reply = HistoryView::GenerateItem(
state->delegate.get(),
history,
GenerateUser(
HistoryView::GenerateUser(
history,
tr::lng_settings_chat_message_reply_from(tr::now)),
FullMsgId(),
tr::lng_settings_chat_message_reply(tr::now));
auto message = GenerateItem(
auto message = HistoryView::GenerateItem(
state->delegate.get(),
history,
history->peer->id,

View File

@@ -0,0 +1,403 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/send_credits_box.h"
#include "api/api_credits.h"
#include "apiwrap.h"
#include "core/ui_integration.h" // Core::MarkedTextContext.
#include "data/data_credits.h"
#include "data/data_photo.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "data/stickers/data_custom_emoji.h"
#include "history/history.h"
#include "history/history_item.h"
#include "info/channel_statistics/boosts/giveaway/boost_badge.h" // InfiniteRadialAnimationWidget.
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "payments/payments_checkout_process.h"
#include "payments/payments_form.h"
#include "settings/settings_credits_graphics.h"
#include "ui/boxes/confirm_box.h"
#include "ui/controls/userpic_button.h"
#include "ui/effects/premium_graphics.h"
#include "ui/effects/premium_top_bar.h" // Ui::Premium::ColorizedSvg.
#include "ui/image/image_prepare.h"
#include "ui/layers/generic_box.h"
#include "ui/rect.h"
#include "ui/text/text_utilities.h"
#include "ui/vertical_list.h"
#include "ui/widgets/buttons.h"
#include "styles/style_boxes.h"
#include "styles/style_credits.h"
#include "styles/style_giveaway.h"
#include "styles/style_layers.h"
#include "styles/style_premium.h"
#include "styles/style_settings.h"
namespace Ui {
namespace {
struct PaidMediaData {
const Data::Invoice *invoice = nullptr;
HistoryItem *item = nullptr;
PeerData *peer = nullptr;
int photos = 0;
int videos = 0;
explicit operator bool() const {
return invoice && item && peer && (photos || videos);
}
};
[[nodiscard]] PaidMediaData LookupPaidMediaData(
not_null<Main::Session*> session,
not_null<Payments::CreditsFormData*> form) {
using namespace Payments;
const auto message = std::get_if<InvoiceMessage>(&form->id.value);
const auto item = message
? session->data().message(message->peer, message->itemId)
: nullptr;
const auto media = item ? item->media() : nullptr;
const auto invoice = media ? media->invoice() : nullptr;
if (!invoice || !invoice->isPaidMedia) {
return {};
}
auto photos = 0;
auto videos = 0;
for (const auto &media : invoice->extendedMedia) {
const auto photo = media->photo();
if (photo && !photo->extendedMediaVideoDuration().has_value()) {
++photos;
} else {
++videos;
}
}
const auto sender = item->originalSender();
const auto broadcast = (sender && sender->isBroadcast())
? sender
: message->peer.get();
return {
.invoice = invoice,
.item = item,
.peer = broadcast,
.photos = photos,
.videos = videos,
};
}
[[nodiscard]] rpl::producer<TextWithEntities> SendCreditsConfirmText(
not_null<Main::Session*> session,
not_null<Payments::CreditsFormData*> form) {
if (const auto data = LookupPaidMediaData(session, form)) {
auto photos = 0;
auto videos = 0;
for (const auto &media : data.invoice->extendedMedia) {
const auto photo = media->photo();
if (photo && !photo->extendedMediaVideoDuration().has_value()) {
++photos;
} else {
++videos;
}
}
auto photosBold = tr::lng_credits_box_out_photos(
lt_count,
rpl::single(photos) | tr::to_count(),
Ui::Text::Bold);
auto videosBold = tr::lng_credits_box_out_videos(
lt_count,
rpl::single(videos) | tr::to_count(),
Ui::Text::Bold);
auto media = (!videos)
? ((photos > 1)
? std::move(photosBold)
: tr::lng_credits_box_out_photo(Ui::Text::WithEntities))
: (!photos)
? ((videos > 1)
? std::move(videosBold)
: tr::lng_credits_box_out_video(Ui::Text::WithEntities))
: tr::lng_credits_box_out_both(
lt_photo,
std::move(photosBold),
lt_video,
std::move(videosBold),
Ui::Text::WithEntities);
return tr::lng_credits_box_out_media(
lt_count,
rpl::single(form->invoice.amount) | tr::to_count(),
lt_media,
std::move(media),
lt_chat,
rpl::single(Ui::Text::Bold(data.peer->name())),
Ui::Text::RichLangValue);
}
const auto bot = session->data().user(form->botId);
return tr::lng_credits_box_out_sure(
lt_count,
rpl::single(form->invoice.amount) | tr::to_count(),
lt_text,
rpl::single(TextWithEntities{ form->title }),
lt_bot,
rpl::single(TextWithEntities{ bot->name() }),
Ui::Text::RichLangValue);
}
[[nodiscard]] object_ptr<Ui::RpWidget> SendCreditsThumbnail(
not_null<Ui::RpWidget*> parent,
not_null<Main::Session*> session,
not_null<Payments::CreditsFormData*> form,
int photoSize) {
if (const auto data = LookupPaidMediaData(session, form)) {
const auto first = data.invoice->extendedMedia[0]->photo();
const auto second = (data.photos > 1)
? data.invoice->extendedMedia[1]->photo()
: nullptr;
const auto totalCount = int(data.invoice->extendedMedia.size());
if (first && first->extendedMediaPreview()) {
return Settings::PaidMediaThumbnail(
parent,
first,
second,
totalCount,
photoSize);
}
}
if (form->photo) {
return Settings::HistoryEntryPhoto(parent, form->photo, photoSize);
}
const auto bot = session->data().user(form->botId);
return object_ptr<Ui::UserpicButton>(
parent,
bot,
st::defaultUserpicButton);
}
} // namespace
void SendCreditsBox(
not_null<Ui::GenericBox*> box,
std::shared_ptr<Payments::CreditsFormData> form,
Fn<void()> sent) {
if (!form) {
return;
}
struct State {
rpl::variable<bool> confirmButtonBusy = false;
};
const auto state = box->lifetime().make_state<State>();
box->setStyle(st::giveawayGiftCodeBox);
box->setNoContentMargin(true);
const auto session = form->invoice.session;
const auto photoSize = st::defaultUserpicButton.photoSize;
const auto content = box->verticalLayout();
Ui::AddSkip(content, photoSize / 2);
{
const auto ministarsContainer = Ui::CreateChild<Ui::RpWidget>(box);
const auto fullHeight = photoSize * 2;
using MiniStars = Ui::Premium::ColoredMiniStars;
const auto ministars = box->lifetime().make_state<MiniStars>(
ministarsContainer,
false,
Ui::Premium::MiniStars::Type::BiStars);
ministars->setColorOverride(Ui::Premium::CreditsIconGradientStops());
ministarsContainer->paintRequest(
) | rpl::start_with_next([=] {
auto p = QPainter(ministarsContainer);
ministars->paint(p);
}, ministarsContainer->lifetime());
box->widthValue(
) | rpl::start_with_next([=](int width) {
ministarsContainer->resize(width, fullHeight);
const auto w = fullHeight / 3 * 2;
ministars->setCenter(QRect(
(width - w) / 2,
(fullHeight - w) / 2,
w,
w));
}, ministarsContainer->lifetime());
}
const auto thumb = box->addRow(object_ptr<Ui::CenterWrap<>>(
content,
SendCreditsThumbnail(content, session, form.get(), photoSize)));
thumb->setAttribute(Qt::WA_TransparentForMouseEvents);
Ui::AddSkip(content);
box->addRow(object_ptr<Ui::CenterWrap<>>(
box,
object_ptr<Ui::FlatLabel>(
box,
tr::lng_credits_box_out_title(),
st::settingsPremiumUserTitle)));
Ui::AddSkip(content);
box->addRow(object_ptr<Ui::CenterWrap<>>(
box,
object_ptr<Ui::FlatLabel>(
box,
SendCreditsConfirmText(session, form.get()),
st::creditsBoxAbout)));
Ui::AddSkip(content);
Ui::AddSkip(content);
const auto button = box->addButton(rpl::single(QString()), [=] {
if (state->confirmButtonBusy.current()) {
return;
}
const auto show = box->uiShow();
const auto weak = MakeWeak(box.get());
state->confirmButtonBusy = true;
session->api().request(
MTPpayments_SendStarsForm(
MTP_flags(0),
MTP_long(form->formId),
form->inputInvoice)
).done([=](auto result) {
if (weak) {
state->confirmButtonBusy = false;
box->closeBox();
}
sent();
}).fail([=](const MTP::Error &error) {
if (weak) {
state->confirmButtonBusy = false;
}
const auto id = error.type();
if (id == u"BOT_PRECHECKOUT_FAILED"_q) {
auto error = ::Ui::MakeInformBox(
tr::lng_payments_precheckout_stars_failed(tr::now));
error->boxClosing() | rpl::start_with_next([=] {
if (const auto paybox = weak.data()) {
paybox->closeBox();
}
}, error->lifetime());
show->showBox(std::move(error));
} else if (id == u"BOT_PRECHECKOUT_TIMEOUT"_q) {
show->showToast(
tr::lng_payments_precheckout_stars_timeout(tr::now));
} else {
show->showToast(id);
}
}).send();
});
{
using namespace Info::Statistics;
const auto loadingAnimation = InfiniteRadialAnimationWidget(
button,
st::giveawayGiftCodeStartButton.height / 2);
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());
}
const auto buttonWidth = st::boxWidth
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
button->widthValue() | rpl::filter([=] {
return (button->widthNoMargins() != buttonWidth);
}) | rpl::start_with_next([=] {
button->resizeToWidth(buttonWidth);
}, button->lifetime());
{
const auto close = Ui::CreateChild<Ui::IconButton>(
box.get(),
st::boxTitleClose);
close->setClickedCallback([=] {
box->closeBox();
});
box->widthValue(
) | rpl::start_with_next([=](int width) {
close->moveToRight(0, 0);
close->raise();
}, close->lifetime());
}
{
const auto balance = Settings::AddBalanceWidget(
content,
session->creditsValue(),
false);
const auto api = balance->lifetime().make_state<Api::CreditsStatus>(
session->user());
api->request({}, [=](Data::CreditsStatusSlice slice) {
session->setCredits(slice.balance);
});
rpl::combine(
balance->sizeValue(),
content->sizeValue()
) | rpl::start_with_next([=](const QSize &, const QSize &) {
balance->moveToLeft(
st::creditsHistoryRightSkip * 2,
st::creditsHistoryRightSkip);
balance->update();
}, balance->lifetime());
}
}
TextWithEntities CreditsEmoji(not_null<Main::Session*> session) {
return Ui::Text::SingleCustomEmoji(
session->data().customEmojiManager().registerInternalEmoji(
st::settingsPremiumIconStar,
QMargins{ 0, -st::moderateBoxExpandInnerSkip, 0, 0 },
true),
QString(QChar(0x2B50)));
}
TextWithEntities CreditsEmojiSmall(not_null<Main::Session*> session) {
return Ui::Text::SingleCustomEmoji(
session->data().customEmojiManager().registerInternalEmoji(
st::starIconSmall,
st::starIconSmallPadding,
true),
QString(QChar(0x2B50)));
}
} // namespace Ui

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
class HistoryItem;
namespace Main {
class Session;
} // namespace Main
namespace Payments {
struct CreditsFormData;
} // namespace Payments
namespace Ui {
class GenericBox;
void SendCreditsBox(
not_null<Ui::GenericBox*> box,
std::shared_ptr<Payments::CreditsFormData> data,
Fn<void()> sent);
[[nodiscard]] TextWithEntities CreditsEmoji(
not_null<Main::Session*> session);
[[nodiscard]] TextWithEntities CreditsEmojiSmall(
not_null<Main::Session*> session);
} // namespace Ui

View File

@@ -10,7 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "storage/storage_media_prepare.h"
#include "iv/iv_instance.h"
#include "mainwidget.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "mtproto/mtproto_config.h"
@@ -24,11 +26,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/controls/history_view_characters_limit.h"
#include "history/view/history_view_schedule_box.h"
#include "core/mime_type.h"
#include "core/ui_integration.h"
#include "base/event_filter.h"
#include "base/call_delayed.h"
#include "boxes/premium_limits_box.h"
#include "boxes/premium_preview_box.h"
#include "boxes/send_credits_box.h"
#include "ui/effects/scroll_content_shadow.h"
#include "ui/widgets/fields/number_input.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/popup_menu.h"
@@ -36,10 +41,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/chat/attach/attach_single_file_preview.h"
#include "ui/chat/attach/attach_single_media_preview.h"
#include "ui/grouped_layout.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/controls/emoji_button.h"
#include "ui/painter.h"
#include "ui/vertical_list.h"
#include "lottie/lottie_single_player.h"
#include "data/data_channel.h"
#include "data/data_document.h"
#include "data/data_user.h"
#include "data/data_peer_values.h" // Data::AmPremiumValue.
@@ -103,6 +111,84 @@ rpl::producer<QString> FieldPlaceholder(
: tr::lng_photos_comment();
}
void EditPriceBox(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session,
uint64 price,
Fn<void(uint64)> apply) {
box->setTitle(tr::lng_paid_title());
AddSubsectionTitle(
box->verticalLayout(),
tr::lng_paid_enter_cost(),
(st::boxRowPadding - QMargins(
st::defaultSubsectionTitlePadding.left(),
0,
st::defaultSubsectionTitlePadding.right(),
0)));
const auto limit = session->appConfig().get<int>(
u"stars_paid_post_amount_max"_q,
10'000);
const auto wrap = box->addRow(object_ptr<Ui::FixedHeightWidget>(
box,
st::editTagField.heightMin));
auto owned = object_ptr<Ui::NumberInput>(
wrap,
st::editTagField,
tr::lng_paid_cost_placeholder(),
price ? QString::number(price) : QString(),
limit);
const auto field = owned.data();
wrap->widthValue() | rpl::start_with_next([=](int width) {
field->move(0, 0);
field->resize(width, field->height());
wrap->resize(width, field->height());
}, wrap->lifetime());
field->selectAll();
box->setFocusCallback([=] {
field->setFocusFast();
});
const auto about = box->addRow(
object_ptr<Ui::FlatLabel>(
box,
tr::lng_paid_about(
lt_link,
tr::lng_paid_about_link() | Ui::Text::ToLink(),
Ui::Text::WithEntities),
st::paidAmountAbout),
st::boxRowPadding + QMargins(0, st::sendMediaRowSkip, 0, 0));
about->setClickHandlerFilter([=](const auto &...) {
Core::App().iv().openWithIvPreferred(
session,
tr::lng_paid_about_link_url(tr::now));
return false;
});
field->paintRequest() | rpl::start_with_next([=](QRect clip) {
auto p = QPainter(field);
st::paidStarIcon.paint(p, 0, st::paidStarIconTop, field->width());
}, field->lifetime());
const auto save = [=] {
const auto now = field->getLastText().toULongLong();
if (now > limit) {
field->showError();
return;
}
const auto weak = Ui::MakeWeak(box);
apply(now);
if (const auto strong = weak.data()) {
strong->closeBox();
}
};
QObject::connect(field, &Ui::NumberInput::submitted, box, save);
box->addButton(tr::lng_settings_save(), save);
box->addButton(tr::lng_cancel(), [=] {
box->closeBox();
});
}
} // namespace
SendFilesLimits DefaultLimitsForPeer(not_null<PeerData*> peer) {
@@ -153,7 +239,8 @@ SendFilesBox::Block::Block(
int from,
int till,
Fn<bool()> gifPaused,
SendFilesWay way)
SendFilesWay way,
Fn<bool()> canToggleSpoiler)
: _items(items)
, _from(from)
, _till(till) {
@@ -170,14 +257,16 @@ SendFilesBox::Block::Block(
parent.get(),
st,
my,
way);
way,
std::move(canToggleSpoiler));
_preview.reset(preview);
} else {
const auto media = Ui::SingleMediaPreview::Create(
parent,
st,
gifPaused,
first);
first,
std::move(canToggleSpoiler));
if (media) {
_isSingleMedia = true;
_preview.reset(media);
@@ -253,6 +342,14 @@ rpl::producer<int> SendFilesBox::Block::itemModifyRequest() const {
}
}
rpl::producer<> SendFilesBox::Block::orderUpdated() const {
if (_isAlbum) {
const auto album = static_cast<Ui::AlbumPreview*>(_preview.get());
return album->orderUpdated();
}
return rpl::never<>();
}
void SendFilesBox::Block::setSendWay(Ui::SendFilesWay way) {
if (!_isAlbum) {
if (_isSingleMedia) {
@@ -321,6 +418,18 @@ void SendFilesBox::Block::applyChanges() {
}
}
QImage SendFilesBox::Block::generatePriceTagBackground() const {
const auto preview = _preview.get();
if (_isAlbum) {
const auto album = static_cast<Ui::AlbumPreview*>(preview);
return album->generatePriceTagBackground();
} else if (_isSingleMedia) {
const auto media = static_cast<Ui::SingleMediaPreview*>(preview);
return media->generatePriceTagBackground();
}
return QImage();
}
SendFilesBox::SendFilesBox(
QWidget*,
not_null<Window::SessionController*> controller,
@@ -328,7 +437,7 @@ SendFilesBox::SendFilesBox(
const TextWithTags &caption,
not_null<PeerData*> toPeer,
Api::SendType sendType,
SendMenu::Type sendMenuType)
SendMenu::Details sendMenuDetails)
: SendFilesBox(nullptr, {
.show = controller->uiShow(),
.list = std::move(list),
@@ -337,7 +446,7 @@ SendFilesBox::SendFilesBox(
.limits = DefaultLimitsForPeer(toPeer),
.check = DefaultCheckForPeer(controller, toPeer),
.sendType = sendType,
.sendMenuType = sendMenuType,
.sendMenuDetails = [=] { return sendMenuDetails; },
}) {
}
@@ -350,7 +459,8 @@ SendFilesBox::SendFilesBox(QWidget*, SendFilesBoxDescriptor &&descriptor)
, _titleHeight(st::boxTitleHeight)
, _list(std::move(descriptor.list))
, _limits(descriptor.limits)
, _sendMenuType(descriptor.sendMenuType)
, _sendMenuDetails(prepareSendMenuDetails(descriptor))
, _sendMenuCallback(prepareSendMenuCallback())
, _captionToPeer(descriptor.captionToPeer)
, _check(std::move(descriptor.check))
, _confirmedCallback(std::move(descriptor.confirmed))
@@ -364,6 +474,54 @@ SendFilesBox::SendFilesBox(QWidget*, SendFilesBoxDescriptor &&descriptor)
enqueueNextPrepare();
}
Fn<SendMenu::Details()> SendFilesBox::prepareSendMenuDetails(
const SendFilesBoxDescriptor &descriptor) {
auto initial = descriptor.sendMenuDetails;
return crl::guard(this, [=] {
auto result = initial ? initial() : SendMenu::Details();
result.spoiler = !hasSpoilerMenu()
? SendMenu::SpoilerState::None
: allWithSpoilers()
? SendMenu::SpoilerState::Enabled
: SendMenu::SpoilerState::Possible;
const auto way = _sendWay.current();
const auto canMoveCaption = _list.canMoveCaption(
way.groupFiles() && way.sendImagesAsPhotos(),
way.sendImagesAsPhotos()
) && _caption && HasSendText(_caption);
result.caption = !canMoveCaption
? SendMenu::CaptionState::None
: _invertCaption
? SendMenu::CaptionState::Above
: SendMenu::CaptionState::Below;
result.price = canChangePrice()
? _price.current()
: std::optional<uint64>();
return result;
});
}
auto SendFilesBox::prepareSendMenuCallback()
-> Fn<void(MenuAction, MenuDetails)> {
return crl::guard(this, [=](MenuAction action, MenuDetails details) {
using Type = SendMenu::ActionType;
switch (action.type) {
case Type::CaptionDown: _invertCaption = false; break;
case Type::CaptionUp: _invertCaption = true; break;
case Type::SpoilerOn: toggleSpoilers(true); break;
case Type::SpoilerOff: toggleSpoilers(false); break;
case Type::ChangePrice: changePrice(); break;
default:
SendMenu::DefaultCallback(
_show,
sendCallback())(
action,
details);
break;
}
});
}
void SendFilesBox::initPreview() {
using namespace rpl::mappers;
@@ -529,10 +687,9 @@ void SendFilesBox::refreshButtons() {
if (_sendType == Api::SendType::Normal) {
SendMenu::SetupMenuAndShortcuts(
_send,
[=] { return _sendMenuType; },
[=] { sendSilent(); },
[=] { sendScheduled(); },
[=] { sendWhenOnline(); });
_show,
_sendMenuDetails,
_sendMenuCallback);
}
addButton(tr::lng_cancel(), [=] { closeBox(); });
_addFile = addLeftButton(
@@ -544,21 +701,27 @@ void SendFilesBox::refreshButtons() {
addMenuButton();
}
bool SendFilesBox::hasSendMenu() const {
return (_sendMenuType != SendMenu::Type::Disabled);
bool SendFilesBox::hasSendMenu(const MenuDetails &details) const {
return (details.type != SendMenu::Type::Disabled)
|| (details.spoiler != SendMenu::SpoilerState::None)
|| (details.caption != SendMenu::CaptionState::None);
}
bool SendFilesBox::hasSpoilerMenu() const {
const auto allAreVideo = !ranges::any_of(_list.files, [](const auto &f) {
using Type = Ui::PreparedFile::Type;
return (f.type != Type::Video);
});
const auto allAreMedia = !ranges::any_of(_list.files, [](const auto &f) {
using Type = Ui::PreparedFile::Type;
return (f.type != Type::Photo) && (f.type != Type::Video);
});
return allAreVideo
|| (allAreMedia && _sendWay.current().sendImagesAsPhotos());
return !hasPrice()
&& _list.hasSpoilerMenu(_sendWay.current().sendImagesAsPhotos());
}
bool SendFilesBox::canChangePrice() const {
const auto way = _sendWay.current();
const auto broadcast = _captionToPeer
? _captionToPeer->asBroadcast()
: nullptr;
return broadcast
&& broadcast->canPostPaidMedia()
&& _list.canChangePrice(
way.groupFiles() && way.sendImagesAsPhotos(),
way.sendImagesAsPhotos());
}
void SendFilesBox::applyBlockChanges() {
@@ -581,41 +744,139 @@ void SendFilesBox::toggleSpoilers(bool enabled) {
}
}
void SendFilesBox::changePrice() {
const auto weak = Ui::MakeWeak(this);
const auto session = &_show->session();
const auto now = _price.current();
_show->show(Box(EditPriceBox, session, now, [=](uint64 price) {
if (weak && price != now) {
_price = price;
refreshPriceTag();
}
}));
}
bool SendFilesBox::hasPrice() const {
return canChangePrice() && _price.current() > 0;
}
void SendFilesBox::refreshPriceTag() {
const auto resetSpoilers = hasPrice() || _priceTag;
if (resetSpoilers) {
for (auto &file : _list.files) {
file.spoiler = false;
}
for (auto &block : _blocks) {
block.toggleSpoilers(hasPrice());
}
}
if (!hasPrice()) {
_priceTag = nullptr;
_priceTagBg = QImage();
} else if (!_priceTag) {
_priceTag = std::make_unique<Ui::RpWidget>(_inner.data());
const auto raw = _priceTag.get();
raw->show();
raw->paintRequest() | rpl::start_with_next([=] {
if (_priceTagBg.isNull()) {
_priceTagBg = preparePriceTagBg(raw->size());
}
QPainter(raw).drawImage(0, 0, _priceTagBg);
}, raw->lifetime());
const auto session = &_show->session();
auto price = _price.value() | rpl::map([=](uint64 amount) {
auto result = Ui::Text::Colorized(Ui::CreditsEmoji(session));
result.append(Lang::FormatCountDecimal(amount));
return result;
});
auto text = tr::lng_paid_price(
lt_price,
std::move(price),
Ui::Text::WithEntities);
const auto label = Ui::CreateChild<Ui::FlatLabel>(
raw,
QString(),
st::paidTagLabel);
std::move(text) | rpl::start_with_next([=](TextWithEntities &&text) {
label->setMarkedText(text, Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = [=] { label->update(); },
});
}, label->lifetime());
label->show();
label->sizeValue() | rpl::start_with_next([=](QSize size) {
const auto inner = QRect(QPoint(), size);
const auto rect = inner.marginsAdded(st::paidTagPadding);
raw->resize(rect.size());
label->move(-rect.topLeft());
}, label->lifetime());
_inner->sizeValue() | rpl::start_with_next([=](QSize size) {
raw->move(
(size.width() - raw->width()) / 2,
(size.height() - raw->height()) / 2);
}, raw->lifetime());
} else {
_priceTag->raise();
_priceTag->update();
_priceTagBg = QImage();
}
}
QImage SendFilesBox::preparePriceTagBg(QSize size) const {
const auto ratio = style::DevicePixelRatio();
const auto outer = _blocks.empty()
? size
: _inner->widgetAt(0)->geometry().size();
auto bg = _blocks.empty()
? QImage()
: _blocks.front().generatePriceTagBackground();
if (bg.isNull()) {
bg = QImage(ratio, ratio, QImage::Format_ARGB32_Premultiplied);
bg.fill(Qt::black);
}
auto result = QImage(size * ratio, QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(ratio);
result.fill(Qt::black);
auto p = QPainter(&result);
auto hq = PainterHighQualityEnabler(p);
p.drawImage(
QRect(
(size.width() - outer.width()) / 2,
(size.height() - outer.height()) / 2,
outer.width(),
outer.height()),
bg);
p.fillRect(QRect(QPoint(), size), st::msgDateImgBg);
p.end();
const auto radius = std::min(size.width(), size.height()) / 2;
return Images::Round(std::move(result), Images::CornersMask(radius));
}
void SendFilesBox::addMenuButton() {
if (!hasSendMenu() && !hasSpoilerMenu()) {
const auto details = _sendMenuDetails();
if (!hasSendMenu(details)) {
return;
}
const auto top = addTopButton(_st.files.menu);
top->setClickedCallback([=] {
const auto &tabbed = _st.tabbed;
const auto &icons = tabbed.icons;
_menu = base::make_unique_q<Ui::PopupMenu>(top, tabbed.menu);
if (hasSpoilerMenu()) {
const auto spoilered = allWithSpoilers();
_menu->addAction(
(spoilered
? tr::lng_context_disable_spoiler(tr::now)
: tr::lng_context_spoiler_effect(tr::now)),
[=] { toggleSpoilers(!spoilered); },
spoilered ? &icons.menuSpoilerOff : &icons.menuSpoiler);
if (hasSendMenu()) {
_menu->addSeparator(&tabbed.expandedSeparator);
}
}
if (hasSendMenu()) {
SendMenu::FillSendMenu(
_menu.get(),
_sendMenuType,
[=] { sendSilent(); },
[=] { sendScheduled(); },
[=] { sendWhenOnline(); },
&_st.tabbed.icons);
}
_menu->popup(QCursor::pos());
const auto position = QCursor::pos();
SendMenu::FillSendMenu(
_menu.get(),
_show,
_sendMenuDetails(),
_sendMenuCallback,
&_st.tabbed.icons,
position);
_menu->popup(position);
return true;
});
}
void SendFilesBox::initSendWay() {
@@ -657,9 +918,8 @@ void SendFilesBox::initSendWay() {
for (auto &block : _blocks) {
block.setSendWay(value);
}
if (!hasSendMenu()) {
refreshButtons();
}
refreshButtons();
refreshPriceTag();
if (was != hidden()) {
updateBoxSize();
updateControlsGeometry();
@@ -745,7 +1005,8 @@ void SendFilesBox::pushBlock(int from, int till) {
from,
till,
gifPaused,
_sendWay.current());
_sendWay.current(),
[=] { return !hasPrice(); });
auto &block = _blocks.back();
const auto widget = _inner->add(
block.takeWidget(),
@@ -868,12 +1129,18 @@ void SendFilesBox::pushBlock(int from, int till) {
st::sendMediaPreviewSize,
[=] { refreshAllAfterChanges(from); });
}, widget->lifetime());
block.orderUpdated() | rpl::start_with_next([=]{
if (_priceTag) {
_priceTagBg = QImage();
_priceTag->update();
}
}, widget->lifetime());
}
void SendFilesBox::refreshControls(bool initial) {
if (initial || !hasSendMenu()) {
refreshButtons();
}
refreshButtons();
refreshPriceTag();
refreshTitleText();
updateSendWayControls();
updateCaptionPlaceholder();
@@ -1425,7 +1692,13 @@ void SendFilesBox::send(
if ((_sendType == Api::SendType::Scheduled
|| _sendType == Api::SendType::ScheduledToUser)
&& !options.scheduled) {
return sendScheduled();
auto child = _sendMenuDetails();
child.spoiler = SendMenu::SpoilerState::None;
child.caption = SendMenu::CaptionState::None;
child.price = std::nullopt;
return SendMenu::DefaultCallback(_show, sendCallback())(
{ .type = SendMenu::ActionType::Schedule },
child);
}
if (_preparing) {
_whenReadySend = [=] {
@@ -1453,6 +1726,13 @@ void SendFilesBox::send(
if (!validateLength(caption.text)) {
return;
}
options.invertCaption = _invertCaption;
options.price = hasPrice() ? _price.current() : 0;
if (options.price > 0) {
for (auto &file : _list.files) {
file.spoiler = false;
}
}
_confirmedCallback(
std::move(_list),
_sendWay.current(),
@@ -1463,25 +1743,10 @@ void SendFilesBox::send(
closeBox();
}
void SendFilesBox::sendSilent() {
send({ .silent = true });
}
void SendFilesBox::sendScheduled() {
const auto type = (_sendType == Api::SendType::ScheduledToUser)
? SendMenu::Type::ScheduledToUser
: _sendMenuType;
const auto callback = [=](Api::SendOptions options) { send(options); };
auto box = HistoryView::PrepareScheduleBox(this, type, callback);
const auto weak = Ui::MakeWeak(box.data());
_show->showBox(std::move(box));
if (const auto strong = weak.data()) {
strong->setCloseByOutsideClick(false);
}
}
void SendFilesBox::sendWhenOnline() {
send(Api::DefaultSendWhenOnlineOptions());
Fn<void(Api::SendOptions)> SendFilesBox::sendCallback() {
return crl::guard(this, [=](Api::SendOptions options) {
send(options, false);
});
}
SendFilesBox::~SendFilesBox() = default;

View File

@@ -47,7 +47,8 @@ class SessionController;
} // namespace Window
namespace SendMenu {
enum class Type;
struct Details;
struct Action;
} // namespace SendMenu
namespace HistoryView::Controls {
@@ -96,7 +97,7 @@ struct SendFilesBoxDescriptor {
SendFilesLimits limits = {};
SendFilesCheck check;
Api::SendType sendType = {};
SendMenu::Type sendMenuType = {};
Fn<SendMenu::Details()> sendMenuDetails = nullptr;
const style::ComposeControls *stOverride = nullptr;
SendFilesConfirmed confirmed;
Fn<void()> cancelled;
@@ -115,7 +116,7 @@ public:
const TextWithTags &caption,
not_null<PeerData*> toPeer,
Api::SendType sendType,
SendMenu::Type sendMenuType);
SendMenu::Details sendMenuDetails);
SendFilesBox(QWidget*, SendFilesBoxDescriptor &&descriptor);
void setConfirmedCallback(SendFilesConfirmed callback) {
@@ -136,6 +137,9 @@ protected:
void resizeEvent(QResizeEvent *e) override;
private:
using MenuAction = SendMenu::Action;
using MenuDetails = SendMenu::Details;
class Block final {
public:
Block(
@@ -145,7 +149,8 @@ private:
int from,
int till,
Fn<bool()> gifPaused,
Ui::SendFilesWay way);
Ui::SendFilesWay way,
Fn<bool()> canToggleSpoiler);
Block(Block &&other) = default;
Block &operator=(Block &&other) = default;
@@ -156,11 +161,14 @@ private:
[[nodiscard]] rpl::producer<int> itemDeleteRequest() const;
[[nodiscard]] rpl::producer<int> itemReplaceRequest() const;
[[nodiscard]] rpl::producer<int> itemModifyRequest() const;
[[nodiscard]] rpl::producer<> orderUpdated() const;
void setSendWay(Ui::SendFilesWay way);
void toggleSpoilers(bool enabled);
void applyChanges();
[[nodiscard]] QImage generatePriceTagBackground() const;
private:
base::unique_qptr<Ui::RpWidget> _preview;
not_null<std::vector<Ui::PreparedFile>*> _items;
@@ -173,7 +181,7 @@ private:
void initSendWay();
void initPreview();
[[nodiscard]] bool hasSendMenu() const;
[[nodiscard]] bool hasSendMenu(const MenuDetails &details) const;
[[nodiscard]] bool hasSpoilerMenu() const;
[[nodiscard]] bool allWithSpoilers();
[[nodiscard]] bool checkWithWay(
@@ -186,6 +194,12 @@ private:
void addMenuButton();
void applyBlockChanges();
void toggleSpoilers(bool enabled);
void changePrice();
[[nodiscard]] bool canChangePrice() const;
[[nodiscard]] bool hasPrice() const;
void refreshPriceTag();
[[nodiscard]] QImage preparePriceTagBg(QSize size) const;
bool validateLength(const QString &text) const;
void refreshButtons();
@@ -202,9 +216,7 @@ private:
void generatePreviewFrom(int fromBlock);
void send(Api::SendOptions options, bool ctrlShiftEnter = false);
void sendSilent();
void sendScheduled();
void sendWhenOnline();
[[nodiscard]] Fn<void(Api::SendOptions)> sendCallback();
void captionResized();
void saveSendWaySettings();
@@ -227,6 +239,11 @@ private:
void checkCharsLimitation();
[[nodiscard]] Fn<MenuDetails()> prepareSendMenuDetails(
const SendFilesBoxDescriptor &descriptor);
[[nodiscard]] auto prepareSendMenuCallback()
-> Fn<void(MenuAction, MenuDetails)>;
const std::shared_ptr<ChatHelpers::Show> _show;
const style::ComposeControls &_st;
const Api::SendType _sendType = Api::SendType();
@@ -238,12 +255,17 @@ private:
std::optional<int> _removingIndex;
SendFilesLimits _limits = {};
SendMenu::Type _sendMenuType = {};
Fn<MenuDetails()> _sendMenuDetails;
Fn<void(MenuAction, MenuDetails)> _sendMenuCallback;
PeerData *_captionToPeer = nullptr;
SendFilesCheck _check;
SendFilesConfirmed _confirmedCallback;
Fn<void()> _cancelledCallback;
rpl::variable<uint64> _price = 0;
std::unique_ptr<Ui::RpWidget> _priceTag;
QImage _priceTagBg;
bool _confirmed = false;
bool _invertCaption = false;
object_ptr<Ui::InputField> _caption = { nullptr };
TextWithTags _prefilledCaptionText;

View File

@@ -473,15 +473,18 @@ void ShareBox::keyPressEvent(QKeyEvent *e) {
}
}
SendMenu::Type ShareBox::sendMenuType() const {
SendMenu::Details ShareBox::sendMenuDetails() const {
const auto selected = _inner->selected();
return ranges::all_of(
const auto type = ranges::all_of(
selected | ranges::views::transform(&Data::Thread::peer),
HistoryView::CanScheduleUntilOnline)
? SendMenu::Type::ScheduledToUser
: (selected.size() == 1 && selected.front()->peer()->isSelf())
? SendMenu::Type::Reminder
: SendMenu::Type::Scheduled;
// We can't support effect here because we don't have ChatHelpers::Show.
return { .type = type, .effectAllowed = false };
}
void ShareBox::showMenu(not_null<Ui::RpWidget*> parent) {
@@ -518,15 +521,32 @@ void ShareBox::showMenu(not_null<Ui::RpWidget*> parent) {
_menu->addSeparator();
}
const auto result = SendMenu::FillSendMenu(
using namespace SendMenu;
const auto sendAction = crl::guard(this, [=](Action action, Details) {
if (action.type == ActionType::Send) {
submit(action.options);
return;
}
uiShow()->showBox(
HistoryView::PrepareScheduleBox(
this,
nullptr, // ChatHelpers::Show for effect attachment.
sendMenuDetails(),
[=](Api::SendOptions options) { submit(options); },
action.options,
HistoryView::DefaultScheduleTime(),
_descriptor.scheduleBoxStyle));
});
_menu->setForcedVerticalOrigin(Ui::PopupMenu::VerticalOrigin::Bottom);
const auto result = FillSendMenu(
_menu.get(),
sendMenuType(),
[=] { submitSilent(); },
[=] { submitScheduled(); },
[=] { submitWhenOnline(); });
const auto success = (result == SendMenu::FillMenuResult::Success);
if (_descriptor.forwardOptions.show || success) {
_menu->setForcedVerticalOrigin(Ui::PopupMenu::VerticalOrigin::Bottom);
nullptr, // showForEffect.
sendMenuDetails(),
sendAction);
if (result == SendMenu::FillMenuResult::Prepared) {
_menu->popupPrepared();
} else if (_descriptor.forwardOptions.show
&& result != SendMenu::FillMenuResult::Failed) {
_menu->popup(QCursor::pos());
}
}
@@ -607,25 +627,6 @@ void ShareBox::submit(Api::SendOptions options) {
}
}
void ShareBox::submitSilent() {
submit({ .silent = true });
}
void ShareBox::submitScheduled() {
const auto callback = [=](Api::SendOptions options) { submit(options); };
uiShow()->showBox(
HistoryView::PrepareScheduleBox(
this,
sendMenuType(),
callback,
HistoryView::DefaultScheduleTime(),
_descriptor.scheduleBoxStyle));
}
void ShareBox::submitWhenOnline() {
submit(Api::DefaultSendWhenOnlineOptions());
}
void ShareBox::copyLink() const {
if (const auto onstack = _descriptor.copyCallback) {
onstack();
@@ -1408,55 +1409,6 @@ std::vector<not_null<Data::Thread*>> ShareBox::Inner::selected() const {
return result;
}
QString AppendShareGameScoreUrl(
not_null<Main::Session*> session,
const QString &url,
const FullMsgId &fullId) {
auto shareHashData = QByteArray(0x20, Qt::Uninitialized);
auto shareHashDataInts = reinterpret_cast<uint64*>(shareHashData.data());
const auto peer = fullId.peer
? session->data().peerLoaded(fullId.peer)
: static_cast<PeerData*>(nullptr);
const auto channelAccessHash = uint64((peer && peer->isChannel())
? peer->asChannel()->access
: 0);
shareHashDataInts[0] = session->userId().bare;
shareHashDataInts[1] = fullId.peer.value;
shareHashDataInts[2] = uint64(fullId.msg.bare);
shareHashDataInts[3] = channelAccessHash;
// Count SHA1() of data.
auto key128Size = 0x10;
auto shareHashEncrypted = QByteArray(key128Size + shareHashData.size(), Qt::Uninitialized);
hashSha1(shareHashData.constData(), shareHashData.size(), shareHashEncrypted.data());
//// Mix in channel access hash to the first 64 bits of SHA1 of data.
//*reinterpret_cast<uint64*>(shareHashEncrypted.data()) ^= channelAccessHash;
// Encrypt data.
if (!session->local().encrypt(shareHashData.constData(), shareHashEncrypted.data() + key128Size, shareHashData.size(), shareHashEncrypted.constData())) {
return url;
}
auto shareHash = shareHashEncrypted.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
auto shareUrl = u"tg://share_game_score?hash="_q + QString::fromLatin1(shareHash);
auto shareComponent = u"tgShareScoreUrl="_q + qthelp::url_encode(shareUrl);
auto hashPosition = url.indexOf('#');
if (hashPosition < 0) {
return url + '#' + shareComponent;
}
auto hash = url.mid(hashPosition + 1);
if (hash.indexOf('=') >= 0 || hash.indexOf('?') >= 0) {
return url + '&' + shareComponent;
}
if (!hash.isEmpty()) {
return url + '?' + shareComponent;
}
return url + shareComponent;
}
ChatHelpers::ForwardedMessagePhraseArgs CreateForwardedMessagePhraseArgs(
const std::vector<not_null<Data::Thread*>> &result,
const MessageIdsList &msgIds) {
@@ -1611,9 +1563,8 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
}
void FastShareMessage(
not_null<Window::SessionController*> controller,
std::shared_ptr<Main::SessionShow> show,
not_null<HistoryItem*> item) {
const auto show = controller->uiShow();
const auto history = item->history();
const auto owner = &history->owner();
const auto session = &history->session();
@@ -1642,7 +1593,7 @@ void FastShareMessage(
}
if (item->hasDirectLink()) {
using namespace HistoryView;
CopyPostLink(controller, item->fullId(), Context::History);
CopyPostLink(show, item->fullId(), Context::History);
} else if (const auto bot = item->getMessageBot()) {
if (const auto media = item->media()) {
if (const auto game = media->game()) {
@@ -1674,23 +1625,27 @@ void FastShareMessage(
auto copyLinkCallback = canCopyLink
? Fn<void()>(std::move(copyCallback))
: Fn<void()>();
controller->show(
Box<ShareBox>(ShareBox::Descriptor{
.session = session,
.copyCallback = std::move(copyLinkCallback),
.submitCallback = ShareBox::DefaultForwardCallback(
show,
history,
msgIds),
.filterCallback = std::move(filterCallback),
.forwardOptions = {
.sendersCount = ItemsForwardSendersCount(items),
.captionsCount = ItemsForwardCaptionsCount(items),
.show = !hasOnlyForcedForwardedInfo,
},
.premiumRequiredError = SharePremiumRequiredError(),
}),
Ui::LayerOption::CloseOther);
show->show(Box<ShareBox>(ShareBox::Descriptor{
.session = session,
.copyCallback = std::move(copyLinkCallback),
.submitCallback = ShareBox::DefaultForwardCallback(
show,
history,
msgIds),
.filterCallback = std::move(filterCallback),
.forwardOptions = {
.sendersCount = ItemsForwardSendersCount(items),
.captionsCount = ItemsForwardCaptionsCount(items),
.show = !hasOnlyForcedForwardedInfo,
},
.premiumRequiredError = SharePremiumRequiredError(),
}), Ui::LayerOption::CloseOther);
}
void FastShareMessage(
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item) {
FastShareMessage(controller->uiShow(), item);
}
void FastShareLink(
@@ -1792,111 +1747,3 @@ auto SharePremiumRequiredError()
-> Fn<RecipientPremiumRequiredError(not_null<UserData*>)> {
return WritePremiumRequiredError;
}
void ShareGameScoreByHash(
not_null<Window::SessionController*> controller,
const QString &hash) {
auto &session = controller->session();
auto key128Size = 0x10;
auto hashEncrypted = QByteArray::fromBase64(hash.toLatin1(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
if (hashEncrypted.size() <= key128Size || (hashEncrypted.size() != key128Size + 0x20)) {
controller->show(
Ui::MakeInformBox(tr::lng_confirm_phone_link_invalid()),
Ui::LayerOption::CloseOther);
return;
}
// Decrypt data.
auto hashData = QByteArray(hashEncrypted.size() - key128Size, Qt::Uninitialized);
if (!session.local().decrypt(hashEncrypted.constData() + key128Size, hashData.data(), hashEncrypted.size() - key128Size, hashEncrypted.constData())) {
return;
}
// Count SHA1() of data.
char dataSha1[20] = { 0 };
hashSha1(hashData.constData(), hashData.size(), dataSha1);
//// Mix out channel access hash from the first 64 bits of SHA1 of data.
//auto channelAccessHash = *reinterpret_cast<uint64*>(hashEncrypted.data()) ^ *reinterpret_cast<uint64*>(dataSha1);
//// Check next 64 bits of SHA1() of data.
//auto skipSha1Part = sizeof(channelAccessHash);
//if (memcmp(dataSha1 + skipSha1Part, hashEncrypted.constData() + skipSha1Part, key128Size - skipSha1Part) != 0) {
// Ui::show(Box<Ui::InformBox>(tr::lng_share_wrong_user(tr::now)));
// return;
//}
// Check 128 bits of SHA1() of data.
if (memcmp(dataSha1, hashEncrypted.constData(), key128Size) != 0) {
controller->show(
Ui::MakeInformBox(tr::lng_share_wrong_user()),
Ui::LayerOption::CloseOther);
return;
}
auto hashDataInts = reinterpret_cast<uint64*>(hashData.data());
if (hashDataInts[0] != session.userId().bare) {
controller->show(
Ui::MakeInformBox(tr::lng_share_wrong_user()),
Ui::LayerOption::CloseOther);
return;
}
const auto peerId = PeerId(hashDataInts[1]);
const auto channelAccessHash = hashDataInts[3];
if (!peerIsChannel(peerId) && channelAccessHash) {
// If there is no channel id, there should be no channel access_hash.
controller->show(
Ui::MakeInformBox(tr::lng_share_wrong_user()),
Ui::LayerOption::CloseOther);
return;
}
const auto msgId = MsgId(int64(hashDataInts[2]));
if (const auto item = session.data().message(peerId, msgId)) {
FastShareMessage(controller, item);
} else {
const auto weak = base::make_weak(controller);
const auto resolveMessageAndShareScore = crl::guard(weak, [=](
PeerData *peer) {
auto done = crl::guard(weak, [=] {
const auto item = weak->session().data().message(
peerId,
msgId);
if (item) {
FastShareMessage(weak.get(), item);
} else {
weak->show(
Ui::MakeInformBox(tr::lng_edit_deleted()),
Ui::LayerOption::CloseOther);
}
});
auto &api = weak->session().api();
api.requestMessageData(peer, msgId, std::move(done));
});
const auto peer = peerIsChannel(peerId)
? controller->session().data().peerLoaded(peerId)
: nullptr;
if (peer || !peerIsChannel(peerId)) {
resolveMessageAndShareScore(peer);
} else {
const auto owner = &controller->session().data();
controller->session().api().request(MTPchannels_GetChannels(
MTP_vector<MTPInputChannel>(
1,
MTP_inputChannel(
MTP_long(peerToChannel(peerId).bare),
MTP_long(channelAccessHash)))
)).done([=](const MTPmessages_Chats &result) {
result.match([&](const auto &data) {
owner->processChats(data.vchats());
});
if (const auto peer = owner->peerLoaded(peerId)) {
resolveMessageAndShareScore(peer);
}
}).send();
}
}
}

View File

@@ -24,7 +24,7 @@ struct PeerList;
} // namespace style
namespace SendMenu {
enum class Type;
struct Details;
} // namespace SendMenu
namespace Window {
@@ -59,13 +59,11 @@ class SlideWrap;
class PopupMenu;
} // namespace Ui
QString AppendShareGameScoreUrl(
not_null<Main::Session*> session,
const QString &url,
const FullMsgId &fullId);
void ShareGameScoreByHash(
not_null<Window::SessionController*> controller,
const QString &hash);
class ShareBox;
void FastShareMessage(
std::shared_ptr<Main::SessionShow> show,
not_null<HistoryItem*> item);
void FastShareMessage(
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item);
@@ -130,13 +128,10 @@ private:
void scrollAnimationCallback();
void submit(Api::SendOptions options);
void submitSilent();
void submitScheduled();
void submitWhenOnline();
void copyLink() const;
bool searchByUsername(bool useCache = false);
SendMenu::Type sendMenuType() const;
[[nodiscard]] SendMenu::Details sendMenuDetails() const;
void scrollTo(Ui::ScrollToRequest request);
void needSearchByUsername();

View File

@@ -73,7 +73,9 @@ using Data::StickersSet;
using Data::StickersPack;
using SetFlag = Data::StickersSetFlag;
[[nodiscard]] std::optional<QColor> ComputeImageColor(const QImage &frame) {
[[nodiscard]] std::optional<QColor> ComputeImageColor(
const style::icon &lockIcon,
const QImage &frame) {
if (frame.isNull()
|| frame.format() != QImage::Format_ARGB32_Premultiplied) {
return {};
@@ -83,7 +85,7 @@ using SetFlag = Data::StickersSetFlag;
auto sb = int64();
auto sa = int64();
const auto factor = frame.devicePixelRatio();
const auto size = st::stickersPremiumLock.size() * factor;
const auto size = lockIcon.size() * factor;
const auto width = std::min(frame.width(), size.width());
const auto height = std::min(frame.height(), size.height());
const auto skipx = (frame.width() - width) / 2;
@@ -110,22 +112,30 @@ using SetFlag = Data::StickersSetFlag;
}
[[nodiscard]] QColor ComputeLockColor(const QImage &frame) {
return ComputeImageColor(frame).value_or(st::windowSubTextFg->c);
[[nodiscard]] QColor ComputeLockColor(
const style::icon &lockIcon,
const QImage &frame) {
return ComputeImageColor(
lockIcon,
frame
).value_or(st::windowSubTextFg->c);
}
void ValidatePremiumLockBg(QImage &image, const QImage &frame) {
void ValidatePremiumLockBg(
const style::icon &lockIcon,
QImage &image,
const QImage &frame) {
if (!image.isNull()) {
return;
}
const auto factor = style::DevicePixelRatio();
const auto size = st::stickersPremiumLock.size();
const auto size = lockIcon.size();
image = QImage(
size * factor,
QImage::Format_ARGB32_Premultiplied);
image.setDevicePixelRatio(factor);
auto p = QPainter(&image);
const auto color = ComputeLockColor(frame);
const auto color = ComputeLockColor(lockIcon, frame);
p.fillRect(
QRect(QPoint(), size),
anim::color(color, st::windowSubTextFg, kGrayLockOpacity));
@@ -134,12 +144,12 @@ void ValidatePremiumLockBg(QImage &image, const QImage &frame) {
image = Images::Circle(std::move(image));
}
void ValidatePremiumStarFg(QImage &image) {
void ValidatePremiumStarFg(const style::icon &lockIcon, QImage &image) {
if (!image.isNull()) {
return;
}
const auto factor = style::DevicePixelRatio();
const auto size = st::stickersPremiumLock.size();
const auto size = lockIcon.size();
image = QImage(
size * factor,
QImage::Format_ARGB32_Premultiplied);
@@ -176,7 +186,10 @@ void ValidatePremiumStarFg(QImage &image) {
} // namespace
StickerPremiumMark::StickerPremiumMark(not_null<Main::Session*> session) {
StickerPremiumMark::StickerPremiumMark(
not_null<Main::Session*> session,
const style::icon &lockIcon)
: _lockIcon(lockIcon) {
style::PaletteChanged(
) | rpl::start_with_next([=] {
_lockGray = QImage();
@@ -202,16 +215,14 @@ void StickerPremiumMark::paint(
const auto factor = style::DevicePixelRatio();
const auto radius = st::roundRadiusSmall;
const auto point = position + QPoint(
(_premium
? (singleSize.width() - (bg.width() / factor) - radius)
: (singleSize.width() - (bg.width() / factor)) / 2),
(singleSize.width() - (bg.width() / factor) - radius),
singleSize.height() - (bg.height() / factor) - radius);
p.drawImage(point, bg);
if (_premium) {
validateStar();
p.drawImage(point, _star);
} else {
st::stickersPremiumLock.paint(p, point, outerWidth);
_lockIcon.paint(p, point, outerWidth);
}
}
@@ -219,11 +230,11 @@ void StickerPremiumMark::validateLock(
const QImage &frame,
QImage &backCache) {
auto &image = frame.isNull() ? _lockGray : backCache;
ValidatePremiumLockBg(image, frame);
ValidatePremiumLockBg(_lockIcon, image, frame);
}
void StickerPremiumMark::validateStar() {
ValidatePremiumStarFg(_star);
ValidatePremiumStarFg(_lockIcon, _star);
}
class StickerSetBox::Inner final : public Ui::RpWidget {
@@ -664,7 +675,7 @@ StickerSetBox::Inner::Inner(
st::windowBgRipple,
st::windowBgOver,
[=] { repaintItems(); }))
, _premiumMark(_session)
, _premiumMark(_session, st::stickersPremiumLock)
, _updateItemsTimer([=] { updateItems(); })
, _input(set)
, _padding((type == Data::StickersType::Emoji)
@@ -1014,7 +1025,7 @@ void StickerSetBox::Inner::contextMenuEvent(QContextMenuEvent *e) {
_menu = base::make_unique_q<Ui::PopupMenu>(
this,
st::popupMenuWithIcons);
const auto type = _show->sendMenuType();
const auto details = _show->sendMenuDetails();
if (setType() == Data::StickersType::Emoji) {
if (const auto t = PrepareTextFromEmoji(_pack[index]); !t.empty()) {
_menu->addAction(tr::lng_mediaview_copy(tr::now), [=] {
@@ -1023,17 +1034,16 @@ void StickerSetBox::Inner::contextMenuEvent(QContextMenuEvent *e) {
}
}, &st::menuIconCopy);
}
} else if (type != SendMenu::Type::Disabled) {
} else if (details.type != SendMenu::Type::Disabled) {
const auto document = _pack[index];
const auto sendSelected = [=](Api::SendOptions options) {
const auto send = crl::guard(this, [=](Api::SendOptions options) {
chosen(index, document, options);
};
});
SendMenu::FillSendMenu(
_menu.get(),
type,
SendMenu::DefaultSilentCallback(sendSelected),
SendMenu::DefaultScheduleCallback(_show, type, sendSelected),
SendMenu::DefaultWhenOnlineCallback(sendSelected));
_show,
details,
SendMenu::DefaultCallback(_show, send));
const auto show = _show;
const auto toggleFavedSticker = [=] {

View File

@@ -23,10 +23,6 @@ namespace Data {
class StickersSet;
} // namespace Data
namespace SendMenu {
enum class Type;
} // namespace SendMenu
namespace ChatHelpers {
struct FileChosen;
class Show;
@@ -34,7 +30,9 @@ class Show;
class StickerPremiumMark final {
public:
explicit StickerPremiumMark(not_null<Main::Session*> session);
StickerPremiumMark(
not_null<Main::Session*> session,
const style::icon &lockIcon);
void paint(
QPainter &p,
@@ -48,6 +46,7 @@ private:
void validateLock(const QImage &frame, QImage &backCache);
void validateStar();
const style::icon &_lockIcon;
QImage _lockGray;
QImage _star;
bool _premium = false;

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