Compare commits

...

230 Commits

Author SHA1 Message Date
AlexeyZavar 57aebdd300 fix: forum link 2025-02-14 04:45:09 +03:00
AlexeyZavar 25821ba338 fix: build on Linux/macOS 2025-02-14 03:58:37 +03:00
AlexeyZavar 7dda5461ad chore: enable replaceBottomInfoWithIcons by default 2025-02-14 03:14:17 +03:00
AlexeyZavar ee0238bad8 chore: hide annoying gift button 2025-02-14 02:07:32 +03:00
AlexeyZavar 451940e6a7 fix: make it build 2025-02-14 02:07:01 +03:00
AlexeyZavar 6163cc8a5a Merge tag 'v5.11.1' into dev 2025-02-14 01:51:34 +03:00
John Preston ef859d77e9 Version 5.11.1.
- Fix arbitrary cropping support in the image editor.
- Fix crash on round video message playback.
- Fix complex custom keyboard shortcuts.
2025-02-13 21:24:40 +04:00
John Preston 0fd752657a Fix build with latest Visual Studio. 2025-02-13 21:24:40 +04:00
23rd f1451a1de3 Added ripple animation to button for gift to channel in history widget. 2025-02-13 21:02:44 +04:00
John Preston fa96f25683 Fix webview with old webkitgtk. 2025-02-13 21:01:28 +04:00
John Preston 9e447383df Support Shift+.. shortcuts on Linux. 2025-02-13 21:01:16 +04:00
John Preston 5e762be32b Remove redundant new progress in round videos. 2025-02-13 20:20:47 +04:00
John Preston 75de81a3ab Update lib_webview. 2025-02-13 20:20:43 +04:00
John Preston d26b64a5bb Fix crash in video messages. 2025-02-13 20:20:40 +04:00
John Preston cb8d40eaf2 Fix recording shortcuts on Windows. 2025-02-13 20:20:34 +04:00
John Preston ded0936bc4 Fix arbitrary aspect in photo editor crop. 2025-02-13 20:20:14 +04:00
John Preston 16830a410c Improve gift-to-channel button conditions. 2025-02-13 20:20:07 +04:00
John Preston 9f79dda463 Version 5.11.
- Copy video links at current time.
- Set custom video covers when posting to channels.
- Send paid reactions from the name of your channels.
2025-02-13 10:37:16 +04:00
John Preston 15dc7c74d7 Make emoji status in chat bubbles clickable. 2025-02-12 18:27:21 +04:00
John Preston f9abef9e05 Update API scheme on layer 199. 2025-02-12 17:24:24 +04:00
John Preston ba84499f00 Send paid reactions from channels. 2025-02-12 17:24:24 +04:00
John Preston 79ce24222a Prepare code for paid reactions from channels. 2025-02-12 17:24:24 +04:00
John Preston 6cb9264864 Update API scheme to layer 199. 2025-02-12 17:24:24 +04:00
John Preston 247a070405 Prioritize last used playback timestamp. 2025-02-12 17:24:24 +04:00
John Preston e05bb75b8a Allow sending videos with covers. 2025-02-12 17:24:24 +04:00
John Preston 6a415cf232 Support on-hover autoplay and position save. 2025-02-12 17:24:24 +04:00
John Preston e8034189df Use video covers in messages. 2025-02-12 17:24:24 +04:00
John Preston 107f329b4f Update API scheme on layer 198. 2025-02-12 17:24:24 +04:00
John Preston 999a13358e Allow sharing video at a timestamp. 2025-02-12 17:24:24 +04:00
John Preston 2077f51084 Open video from ?t= links. 2025-02-12 17:24:24 +04:00
John Preston 1e77a3df20 Fix file reference video cover. 2025-02-12 17:24:24 +04:00
John Preston 141a291523 Start video from required timestamp. 2025-02-12 17:24:24 +04:00
John Preston cb03d5a9d3 Show video_timestamp progress line. 2025-02-12 17:24:24 +04:00
John Preston b618d1e56a Parse and store video_timestamp. 2025-02-12 17:24:24 +04:00
John Preston 23ae638512 Parse video cover photos. 2025-02-12 17:24:23 +04:00
John Preston eda749d7cb Show correct shortcut text on macOS. 2025-02-12 17:00:39 +04:00
John Preston 08d0186e53 Beta version 5.10.8.
- Edit keyboard shortcuts in Settings > Chat Settings.
- Hide controls in calls with incoming video.
- Fix several crashes.
2025-02-12 11:45:58 +04:00
John Preston 443981ba31 Fix reordering of various-height pinned chats.
Fixes #28948.
2025-02-12 11:39:26 +04:00
John Preston f3ed7c5e19 Fix crash in Instant View in some themes.
Fixes #28604.
2025-02-12 11:39:26 +04:00
John Preston dd2378b591 Hide call controls in one-on-one video. 2025-02-12 11:39:26 +04:00
John Preston b885779365 Support location picker on Linux. 2025-02-12 11:39:26 +04:00
John Preston 32b95f0d9a Improve forum outlines in stories/folders. 2025-02-12 11:39:26 +04:00
John Preston 2b8eec8666 Allow fast bot mode without support mode. 2025-02-12 11:39:26 +04:00
23rd 3f24627f54 Removed reactions list for anonymous non-owner admins. 2025-02-10 23:48:09 +03:00
23rd 0c4bca312e Removed ability to set wallpaper to chat with require-premium user. 2025-02-10 22:52:25 +03:00
23rd e36afc675e Removed ability to set TTL to chat with require-premium user. 2025-02-10 22:52:00 +03:00
23rd d754014321 Added missed label about similar channels to box for doubled limits. 2025-02-10 22:38:42 +03:00
23rd 9f73242cc5 Improved width of voice waveform. 2025-02-10 21:22:57 +03:00
23rd fae9649773 Added log to catch possible api errors when disable calls for account. 2025-02-10 19:42:26 +03:00
23rd 4b43f4cbec Fixed color update of right buttons in chats list on palette change. 2025-02-10 10:49:53 +04:00
23rd b61befa210 Fixed color update of badges in chat filters strip on palette change. 2025-02-10 10:49:53 +04:00
23rd 5e2bc337bc Fixed preview display of personal channel on change of last message. 2025-02-10 10:49:53 +04:00
23rd af1608cbfa Added rpl filter to exclude incomplete sizes. 2025-02-10 10:49:53 +04:00
John Preston 9aa80976ff Fix viewing gifts from the list. 2025-02-10 10:46:49 +04:00
John Preston c9cfe9e90f Allow filtering users gifts. 2025-02-10 10:46:49 +04:00
John Preston 482e337762 Move shortcuts settings lower. 2025-02-10 10:46:49 +04:00
John Preston a0821f5a01 Handle additional phone call discard reason. 2025-02-10 10:46:49 +04:00
John Preston 5ebdf3ed39 Save custom shortcuts to disk. 2025-02-10 10:46:49 +04:00
John Preston 86096db02d Apply shortcuts changes to actions. 2025-02-10 10:46:49 +04:00
John Preston a93e01b896 Don't show Non-Unique status without Upgrade. 2025-02-10 10:46:49 +04:00
John Preston 0585e72c35 Section for shortcuts editing. 2025-02-10 10:46:48 +04:00
John Preston c82fbefcfc Don't show Send a Gift to service users. 2025-02-10 10:46:48 +04:00
John Preston 23542a1db1 Fix boost group phrases. 2025-02-10 10:46:48 +04:00
John Preston 1bf50d60d8 Fix possible crash in stories. 2025-02-10 10:46:48 +04:00
John Preston 8596b0309e Fix bot chat scroll shift on state restore.
Fixes #28085.
2025-02-10 10:46:48 +04:00
John Preston 7f6e871b26 Fix crash in emoji color picker. 2025-02-10 10:46:48 +04:00
John Preston 8912d4d55a Fix weather widget in stories. 2025-02-10 10:46:48 +04:00
John Preston 7ac849ab12 Don't update mouse in non-active window. 2025-02-10 10:46:48 +04:00
John Preston 4cab699b04 Use larger unique gift preview. 2025-02-10 10:46:48 +04:00
John Preston 03aa05e4d2 Remove creation of telesco.pe links. 2025-02-10 10:46:48 +04:00
John Preston 428a3cf0ce Improve phrases for forward-from-saved. 2025-02-10 10:46:48 +04:00
John Preston 1c8b165a64 Show quantity of issued unique gifts. 2025-02-10 10:46:48 +04:00
John Preston 1869071ef7 Sort/Filter channel gifts by non-admins. 2025-02-10 10:46:48 +04:00
John Preston 33ca5ee39f Limited before In Stock. 2025-02-10 10:46:48 +04:00
John Preston e46d5a86d3 Don't auto-submit /start on non-loaded history. 2025-02-10 10:46:48 +04:00
John Preston 2729bcac3b Fix multi-line description in gift box. 2025-02-10 10:46:48 +04:00
John Preston d60ce41fa9 Add a gift button to channel bottom. 2025-02-10 10:46:48 +04:00
John Preston 958db945f3 Fix bot verify badge position. 2025-02-10 10:46:48 +04:00
John Preston 057f906ca4 Remove bot verification from saved messages. 2025-02-10 10:46:48 +04:00
23rd 8df7a45e29 Added animated arrow to toggle of business hours. 2025-02-10 10:46:47 +04:00
23rd e4af1570cb Replaced link button for switch of business hours with small round one. 2025-02-10 10:46:47 +04:00
23rd 049ebf9027 Fixed display of credits icon in table row from gift box. 2025-02-10 10:46:47 +04:00
23rd f2f0c7df92 Extended context menu for outgoing gifts. 2025-02-10 10:46:47 +04:00
Anton Ryzhov bee4118513 Export media sizes 2025-02-09 12:22:37 +04:00
AlexeyZavar a1096f7ecb fix: update .ico file 2025-02-07 23:23:05 +03:00
AlexeyZavar dac0a6ec20 fix: allow go back if there's authed account 2025-02-05 23:03:00 +03:00
AlexeyZavar 4327fd4c58 fix: reduce CPU usage for TTL messages & refactor 2025-02-05 22:49:33 +03:00
AlexeyZavar 3243f57fd5 chore: remove annoying stories tooltip 2025-02-04 19:32:48 +03:00
AlexeyZavar 383c488268 feat: simplify intro 2025-02-04 19:28:46 +03:00
AlexeyZavar 53392d6e5d feat: bypass client-side restrictions & minor adjustments 2025-02-04 16:20:37 +03:00
Ilya Fedin bf48025d12 Update Qt 6.8.1 -> 6.8.2 2025-02-02 15:37:34 +04:00
Ilya Fedin 5e624605cf Don't print "Choosing Qt $version" multiple times 2025-02-02 15:37:34 +04:00
Ilya Fedin 026490acc6 Add geoclue and geocode-glib to snap 2025-02-02 15:37:34 +04:00
Ilya Fedin 709ce3adb8 Set UID in docker run 2025-02-01 12:54:55 +04:00
Ilya Fedin a8d23489c4 Set up gcc toolset environment manually 2025-02-01 12:52:22 +04:00
Ilya Fedin 296df113e3 Implement getting geolocation via geoclue/geocode-glib on Linux 2025-02-01 12:51:02 +04:00
AlexeyZavar 292de83c35 fix: messageshot on macOS
Co-authored-by: Vladislav Gorodkov <fuccsoc@gmail.com>
2025-01-31 03:07:17 +03:00
AlexeyZavar 7fe66c07e4 fix: use default icon for service notifications 2025-01-31 01:34:56 +03:00
AlexeyZavar 9aa8f30e2b chore: modify about box 2025-01-31 01:17:55 +03:00
AlexeyZavar e3ebb30518 chore: remove β from option 2025-01-31 00:27:19 +03:00
AlexeyZavar 51122ab3ba feat: show badges in dialogs list 2025-01-30 23:04:03 +03:00
AlexeyZavar 5d96eab7b4 fix: badges 2025-01-30 20:29:29 +03:00
23rd 97c4e79e96 Improved title for separated windows with shared media. 2025-01-30 13:59:53 +03:00
23rd f89bac7781 Improved handle of existing separated windows for shared media. 2025-01-30 13:59:53 +03:00
23rd 1d8a7f8fd3 Removed redundant template from Info::Media::AddCountedButton. 2025-01-30 13:59:35 +03:00
23rd ef9f7ab27a Moved process of separated window for shared media to correspond file. 2025-01-30 13:59:00 +03:00
23rd a676138745 Added ability to open shared media in window with Ctrl+Click. 2025-01-30 12:16:31 +03:00
23rd 7442ea7a16 Added ability to open single typed shared media in window for topics. 2025-01-30 12:04:44 +03:00
23rd 0089cad740 Replaced default shortcut to open chat menu with Ctrl+\. 2025-01-30 11:42:12 +03:00
23rd cf1fa718a8 Improved conflict handle between IV shortcuts and Shortcuts. 2025-01-30 11:39:56 +03:00
23rd d6ba6ac41e Added initial ability to open single typed shared media in window. 2025-01-30 11:01:04 +03:00
23rd bbdd5feaa4 Fixed display of video userpic in short box on Retina. 2025-01-30 11:01:04 +03:00
Ilya Fedin 678527254b Add development facilities to the CentOS Dockerfile 2025-01-29 15:27:25 +01:00
AlexeyZavar a43943ec3d fix: make it build 2025-01-29 02:49:15 +03:00
AlexeyZavar ab3a61794b Merge tag 'v5.10.7' into dev 2025-01-29 01:49:51 +03:00
John Preston 46bf7781aa Version 5.10.7: Haptic on Linux. 2025-01-27 23:43:57 +04:00
John Preston 99c547b625 Version 5.10.7: Fix gifts scrolling. 2025-01-27 23:41:07 +04:00
John Preston 6976e97de3 Fix possible crash in font selection box. 2025-01-27 23:41:07 +04:00
23rd f8e3e70273 Added peer userpic to gift service messages. 2025-01-27 21:10:44 +03:00
23rd 23c9f7a957 Added entities support to title from ServiceBoxContent. 2025-01-27 21:10:44 +03:00
23rd 2ca763cc77 Made less strict condition to show unique status of gift in gift box. 2025-01-27 21:10:44 +03:00
John Preston 2adc811351 Version 5.10.7: Fix build. 2025-01-27 20:31:01 +04:00
23rd be18be4a86 Added volume slider to settings box for rtmp live stream. 2025-01-27 18:35:58 +03:00
John Preston 8d7abb1b8a Version 5.10.7.
- Fix some phrases for collectible gifts.
- Fix freeze in sound reencoding on macOS and Linux.
2025-01-27 19:27:11 +04:00
John Preston 1cc5988c40 Improve gifts phrases. 2025-01-27 19:25:17 +04:00
John Preston da426ae03b Introduce fast-buttons-bots for support mode. 2025-01-27 19:25:17 +04:00
John Preston 3cebd6d923 Fix deadlock in audio for notification repacking. 2025-01-27 18:10:38 +04:00
John Preston f1019c8ca4 Update Qt patches in Linux build. 2025-01-27 18:07:42 +04:00
John Preston edfb7b6b24 Add delay for anim::Disabled tabbed panel hide. 2025-01-27 18:07:42 +04:00
23rd 157a928f5a Added shortcut to open chat menu from dialogs, chat info and history. 2025-01-27 18:07:42 +04:00
Ilya Fedin 5f5a2a3ef2 Check high contrast theme for auto dark mode on Windows 2025-01-27 15:01:11 +01:00
Ilya Fedin 8534cf3756 Extract system dark mode from QPalette 2025-01-27 15:01:11 +01:00
Ilya Fedin 39b90092ff Revert "Fallback to portal on Linux if QStyleHints::colorScheme is unknown"
This reverts commit a88f48cd93.
2025-01-27 15:01:11 +01:00
John Preston d3142ebe6d Version 5.10.6: Fix build. 2025-01-26 14:00:45 +04:00
John Preston d914c6be2e Version 5.10.6.
- Fix crash in opening users gifts section.
- Fix unintended sound in reaction notifications.
- Fix disabling notifications sound on macOS and Linux.
2025-01-26 12:48:57 +04:00
John Preston 05c3e968df Remove 'SOON' badge for tradable feature. 2025-01-26 11:34:45 +04:00
John Preston 7756cce123 Fix viewing gifts from list of saved. 2025-01-26 11:34:44 +04:00
John Preston 8287d717f8 Fix notifications without sounds on macOS. 2025-01-26 11:34:32 +04:00
23rd db9e60b4b5 Added amount of total voters to poll results with multiple answers.
Fixed #28879.
2025-01-26 08:49:21 +03:00
23rd 28a79bfccb Improved sort of received star gifts. 2025-01-25 20:18:42 +03:00
23rd 321490e528 Fixed ability to moderate messages from channel authors. 2025-01-25 19:32:49 +03:00
23rd 6f752357d7 Fixed ability to moderate messages from topbar in sections. 2025-01-25 19:32:49 +03:00
Ilya Fedin e1f71baed6 Move native notifications option check logic to cross platform code 2025-01-25 18:33:13 +04:00
John Preston df377cd5bb Version 5.10.5.
- Count all unread topics as a single chat in archive unread counter.
- Fix common groups section in bots profiles.
- Fix crash on scheduling messages from Share box.
- Fix silent notifications on macOS.
- Add support for 'sound-file' notification hint on Linux.
2025-01-24 22:23:27 +04:00
John Preston 691a0acdab Allow sound-without-toast again on macOS/Linux. 2025-01-24 22:23:27 +04:00
John Preston 5e2eda6af3 Fix webpage preview glitch. 2025-01-24 22:12:21 +04:00
23rd 17cdc2b585 Fixed search shortcut in section for comments. 2025-01-24 21:09:30 +04:00
23rd c08266f81b Fixed reward label for point details widget in credits statistics. 2025-01-24 21:09:28 +04:00
23rd f90a4db569 Respected pause reason in mini stars from section for main settings. 2025-01-24 21:09:27 +04:00
John Preston 22ec7a6d75 Fix notification sounds on macOS. 2025-01-24 21:08:54 +04:00
John Preston 1b16a84810 Return common groups section to bots.
Fixes #28886.
2025-01-24 20:22:16 +04:00
John Preston d778276f5d Reuse views in gifts lists. 2025-01-24 13:40:57 +04:00
John Preston e3030a168f Fix crash in share box scheduling. 2025-01-24 12:13:35 +04:00
John Preston dfd07a4f4f Count all topics as one chat for outer badges. 2025-01-24 12:02:28 +04:00
John Preston f9fc65d7de Fix webapp window top in some cases. 2025-01-24 11:14:06 +04:00
Ilya Fedin 46cf7db242 Add support for sound-file notification hint on Linux 2025-01-24 11:02:38 +04:00
John Preston 2b94cffe7e Allow getting name or path for sound. 2025-01-23 12:13:01 +04:00
John Preston 2e74ad6fbe Use common code for local sound disk cache. 2025-01-23 11:58:20 +04:00
John Preston df7dc1583d Try settings window title for miniapps. 2025-01-23 10:55:59 +04:00
John Preston 88e80b4fae Version 5.10.4: Fix build with GCC. 2025-01-22 23:56:52 +04:00
John Preston aea90f4b65 Version 5.10.4.
- Add messages search in channel comments.
- Allow sending your unique gifts to blockchain.
- Add option for running in backround on Linux.
- Fix possible freeze on quit on macOS.
2025-01-22 22:22:20 +04:00
John Preston 80db076f38 Improve channel gifts viewing. 2025-01-22 22:13:29 +04:00
John Preston 27bba8250a Add special phrase for unique hidden gift. 2025-01-22 22:13:29 +04:00
John Preston 3fb0fa6892 Swap confirm and password boxes on export. 2025-01-22 22:13:28 +04:00
John Preston c3195cfcbe Improve convert-to-stars message in channel gift. 2025-01-22 22:13:28 +04:00
John Preston 51661a872c Fix channel unique gift transfer. 2025-01-22 22:13:28 +04:00
John Preston 64706ea103 Add confirmation for go-to-Fragment. 2025-01-22 22:13:28 +04:00
John Preston ec69d557dc Add channel gifts filter. 2025-01-22 22:13:28 +04:00
John Preston addd37fb1f Make channel gifts wearing / transferring. 2025-01-22 22:13:28 +04:00
John Preston 9dc947ecb6 Simplify run-in-background option selection. 2025-01-22 22:13:28 +04:00
John Preston 7d74d3da3a Remove dnd/focus query code on macOS. 2025-01-22 22:13:28 +04:00
John Preston aa0c56876c Use sound in native notifications on macOS. 2025-01-22 22:13:28 +04:00
John Preston 37c7b0c6d1 Allow exporting unique gifts to blockchain. 2025-01-22 22:13:28 +04:00
John Preston 0d07d238bc Allow converting channel gifts to stars. 2025-01-22 22:13:28 +04:00
John Preston fa4e74ffef Show and edit channel gifts notify settings. 2025-01-22 22:13:28 +04:00
John Preston c22d76e5be Show only channel userpic in RTMP livestreams. 2025-01-22 22:13:28 +04:00
John Preston 18850ebd83 Gradient on premium gift discount badge. 2025-01-22 22:13:28 +04:00
John Preston 17abef95eb Build FFmpeg with WAV encoder/muxer. 2025-01-22 22:13:28 +04:00
John Preston d135151477 Use native sound support in macOS notifications. 2025-01-22 22:13:28 +04:00
John Preston 07fd9b3074 Show nice collectible tooltip on wearing. 2025-01-22 22:13:27 +04:00
John Preston 0523ae705a Fix invisible chat type filter. 2025-01-22 22:13:27 +04:00
John Preston 9db2502cd0 Preserve formatting when editing links.
Fixes #28170.
2025-01-22 22:13:27 +04:00
John Preston a174119877 Update API scheme on layer 198. 2025-01-22 22:13:27 +04:00
John Preston 569dd19932 Fix build on Xcode. 2025-01-22 22:13:27 +04:00
John Preston 530e2a1feb Support background-run options on Linux. 2025-01-22 22:13:27 +04:00
John Preston de732ba692 Update icons for collectible wearing. 2025-01-22 22:13:27 +04:00
John Preston c6649e84a6 Update patches for Qt 5.15. 2025-01-22 22:13:27 +04:00
John Preston e3517aceab Improve stars in collectible emoji status. 2025-01-22 22:13:27 +04:00
John Preston 6d7abd1718 Don't use double-badge in profile, use two. 2025-01-22 22:13:27 +04:00
John Preston e9e493707b Show left / sold gifts counts. 2025-01-22 22:13:27 +04:00
John Preston c25adf8b57 Use a custom animated emoji for collectible status. 2025-01-22 22:13:27 +04:00
John Preston d2be10cd4e Use "Display on my Page" big button. 2025-01-22 22:13:27 +04:00
John Preston 006ecf9a56 Fix giveaway results message sticker. 2025-01-22 22:13:27 +04:00
John Preston a53cc52241 Ask for premium to wear a collectible. 2025-01-22 22:13:26 +04:00
John Preston 961d283325 Put on / Take off collectible wearable. 2025-01-22 22:13:26 +04:00
John Preston 4e46529eb6 Show gift on collectible status click. 2025-01-22 22:13:26 +04:00
John Preston 81001e04e9 Partially implement viewing channel gifts. 2025-01-22 22:13:26 +04:00
John Preston 2fd174ab9c Partially implement sending gifts to channels. 2025-01-22 22:13:26 +04:00
John Preston 6ff5e221ea Support similar bots section. 2025-01-22 22:13:26 +04:00
John Preston 232077b919 Allow wearing unique gifts as status. 2025-01-22 22:13:26 +04:00
John Preston fecddb5203 Allow wearing collectibles from emoji status. 2025-01-22 22:13:26 +04:00
John Preston d0132c0f7b Update API scheme to layer 198. 2025-01-22 22:13:26 +04:00
John Preston 37d32b32f8 Show gifts inside media view in stories. 2025-01-22 22:13:26 +04:00
John Preston 51213b499f Start showing gifts from stories. 2025-01-22 22:13:25 +04:00
John Preston dcb98ce0fb WebPage open button doesn't inflate the preview. 2025-01-22 22:13:25 +04:00
John Preston 428e90a844 Add unique gifts link preview. 2025-01-22 22:13:25 +04:00
John Preston 017535cf7b Handle t.me/nft/slug links. 2025-01-22 22:13:25 +04:00
John Preston 409389a994 Add gift menu with Share and Transfer. 2025-01-22 22:13:25 +04:00
John Preston ba34d92cd3 Update API scheme to layer 197. 2025-01-22 22:13:25 +04:00
23rd b412ee258d Respected first day of week from system in calendar box. 2025-01-22 11:23:08 +03:00
23rd f1ffe2a641 Added icon to webview buttons from bot keyboards. 2025-01-22 10:21:22 +03:00
23rd a13ca95894 Added initial implementation of search in section for comments. 2025-01-18 01:10:28 +03:00
23rd 0989a80a57 Added ability to provide top msg id to api message search class. 2025-01-18 01:10:28 +03:00
23rd d72c15e9d3 Added ability to set wallpaper for bots. 2025-01-17 22:08:48 +03:00
23rd 7084cf9526 Removed ability to set wallpaper for deleted accounts. 2025-01-17 22:08:48 +03:00
23rd 67cc0ef75c Removed call button from history with deleted account. 2025-01-17 22:08:48 +03:00
23rd 75714cc358 Fixed caption for personal photo in media viewer when it's single photo. 2025-01-17 22:08:48 +03:00
23rd 75e454f3fd Fixed blinking of message preview from user's personal channel. 2025-01-17 20:17:22 +03:00
23rd 97afb4e01a Added shortcut to section of public photo settings to self userpic. 2025-01-17 17:55:22 +03:00
23rd 8fcbf43410 Fixed display of reloaded boost statistics after buying of giveaway. 2025-01-17 16:52:30 +03:00
23rd 15a834b883 Fixed text color in badges from giveaway message stickers. 2025-01-17 16:37:25 +03:00
23rd 3d8396e586 Made less strict condition to show button of removed users in chat edit. 2025-01-17 16:28:11 +03:00
23rd 3257fd364a Replaced hardcoded threshold of gigagroup conversion with server value. 2025-01-17 16:09:36 +03:00
23rd d690af99fc Fixed ripple color for top buttons in section of affiliate programs. 2025-01-17 15:45:59 +03:00
23rd 1c28495162 Fixed boost bubble position on resize container. 2025-01-17 12:46:21 +03:00
23rd ec82d5674f Slightly improved style of section for boost statistics. 2025-01-14 20:15:11 +03:00
23rd 4a22e76bdb Fixed opening of chat export progress from history peer menu. 2025-01-14 20:15:11 +03:00
23rd 527be95618 Improved style of labels in top bar for chat export. 2025-01-14 20:15:11 +03:00
23rd 41985c0a5f Fixed ability to cancel of loading video as part of grouped media. 2025-01-09 22:52:33 +03:00
23rd 522e45ce92 Removed trial transcribe button from long voice messages. 2025-01-09 22:52:33 +03:00
404 changed files with 10781 additions and 3556 deletions
+35
View File
@@ -0,0 +1,35 @@
{
"name": "CentOS",
"image": "tdesktop:centos_env",
"customizations": {
"vscode": {
"settings": {
"C_Cpp.intelliSenseEngine": "disabled",
"clangd.arguments": [
"--compile-commands-dir=${workspaceFolder}/out"
],
"cmake.generator": "Ninja Multi-Config",
"cmake.buildDirectory": "${workspaceFolder}/out",
"cmake.configureSettings": {
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
},
"extensions": [
"ms-vscode.cpptools-extension-pack",
"llvm-vs-code-extensions.vscode-clangd",
"TheQtCompany.qt",
"ms-python.python",
"ms-azuretools.vscode-docker",
"eamodio.gitlens"
]
}
},
"capAdd": [
"SYS_PTRACE"
],
"securityOpt": [
"seccomp=unconfined"
],
"workspaceMount": "source=${localWorkspaceFolder},target=/usr/src/tdesktop,type=bind,consistency=cached",
"workspaceFolder": "/usr/src/tdesktop"
}
+3 -2
View File
@@ -83,6 +83,7 @@ jobs:
fi
docker run --rm \
-u $(id -u) \
-v $PWD:/usr/src/tdesktop \
-e CONFIG=Debug \
tdesktop:centos_env \
@@ -114,8 +115,8 @@ jobs:
if: env.UPLOAD_ARTIFACT == 'true'
run: |
cd $REPO_NAME/out/Debug
sudo mkdir artifact
sudo mv {Telegram,Updater} artifact/
mkdir artifact
mv {Telegram,Updater} artifact/
- uses: actions/upload-artifact@v4
if: env.UPLOAD_ARTIFACT == 'true'
name: Upload artifact.
+1
View File
@@ -19,6 +19,7 @@ Release/
ipch/
.vs/
.vscode/
.cache/
/Telegram/log.txt
/Telegram/data
+9 -5
View File
@@ -1041,6 +1041,7 @@ PRIVATE
info/global_media/info_global_media_inner_widget.h
info/global_media/info_global_media_provider.cpp
info/global_media/info_global_media_provider.h
info/media/info_media_buttons.cpp
info/media/info_media_buttons.h
info/media/info_media_common.cpp
info/media/info_media_common.h
@@ -1096,8 +1097,8 @@ PRIVATE
info/saved/info_saved_sublists_widget.h
info/settings/info_settings_widget.cpp
info/settings/info_settings_widget.h
info/similar_channels/info_similar_channels_widget.cpp
info/similar_channels/info_similar_channels_widget.h
info/similar_peers/info_similar_peers_widget.cpp
info/similar_peers/info_similar_peers_widget.h
info/statistics/info_statistics_common.h
info/statistics/info_statistics_inner_widget.cpp
info/statistics/info_statistics_inner_widget.h
@@ -1168,8 +1169,6 @@ PRIVATE
intro/intro_qr.h
intro/intro_signup.cpp
intro/intro_signup.h
intro/intro_start.cpp
intro/intro_start.h
intro/intro_step.cpp
intro/intro_step.h
intro/intro_widget.cpp
@@ -1217,6 +1216,8 @@ PRIVATE
media/audio/media_audio_loader.h
media/audio/media_audio_loaders.cpp
media/audio/media_audio_loaders.h
media/audio/media_audio_local_cache.cpp
media/audio/media_audio_local_cache.h
media/audio/media_audio_track.cpp
media/audio/media_audio_track.h
media/audio/media_child_ffmpeg_loader.cpp
@@ -1285,6 +1286,8 @@ PRIVATE
media/streaming/media_streaming_video_track.h
media/view/media_view_group_thumbs.cpp
media/view/media_view_group_thumbs.h
media/view/media_view_open_common.cpp
media/view/media_view_open_common.h
media/view/media_view_overlay_opengl.cpp
media/view/media_view_overlay_opengl.h
media/view/media_view_overlay_raster.cpp
@@ -1303,7 +1306,6 @@ PRIVATE
media/view/media_view_playback_controls.h
media/view/media_view_playback_progress.cpp
media/view/media_view_playback_progress.h
media/view/media_view_open_common.h
media/system_media_controls_manager.h
media/system_media_controls_manager.cpp
menu/menu_antispam_validator.cpp
@@ -1553,6 +1555,8 @@ PRIVATE
settings/settings_privacy_security.h
settings/settings_scale_preview.cpp
settings/settings_scale_preview.h
settings/settings_shortcuts.cpp
settings/settings_shortcuts.h
settings/settings_type.h
settings/settings_websites.cpp
settings/settings_websites.h
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 21 KiB

@@ -1,6 +1,7 @@
// This is a list of your own shortcuts for Telegram Desktop
// You can see full list of commands in the 'shortcuts-default.json' file
// Place a null value instead of a command string to switch the shortcut off
// You can also edit them in Settings > Chat Settings > Keyboard Shortcuts.
[
// {
Binary file not shown.

After

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 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: 716 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

+145 -10
View File
@@ -604,6 +604,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_update_fail" = "Update check failed :(";
"lng_settings_workmode_tray" = "Show tray icon";
"lng_settings_workmode_window" = "Show taskbar icon";
"lng_settings_window_close" = "When window closed";
"lng_settings_run_in_background" = "Run in the background";
"lng_settings_quit_on_close" = "Quit the application";
"lng_settings_close_to_taskbar" = "Close to taskbar";
"lng_settings_monochrome_icon" = "Use monochrome icon";
"lng_settings_window_system" = "Window title bar";
@@ -639,6 +642,46 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_chat_quick_action_react" = "Send reaction with double click";
"lng_settings_chat_corner_reaction" = "Reaction button on messages";
"lng_settings_shortcuts" = "Keyboard shortcuts";
"lng_shortcuts_reset" = "Reset to default";
"lng_shortcuts_recording" = "Recording...";
"lng_shortcuts_add_another" = "Add another";
"lng_shortcuts_close" = "Close the window";
"lng_shortcuts_lock" = "Lock the application";
"lng_shortcuts_minimize" = "Minimize the window";
"lng_shortcuts_quit" = "Quit the application";
"lng_shortcuts_media_play" = "Play the media";
"lng_shortcuts_media_pause" = "Pause the media";
"lng_shortcuts_media_play_pause" = "Toggle media playback";
"lng_shortcuts_media_stop" = "Stop media playback";
"lng_shortcuts_media_previous" = "Previous track";
"lng_shortcuts_media_next" = "Next track";
"lng_shortcuts_search" = "Search messages";
"lng_shortcuts_chat_previous" = "Previous chat";
"lng_shortcuts_chat_next" = "Next chat";
"lng_shortcuts_chat_first" = "First chat";
"lng_shortcuts_chat_last" = "Last chat";
"lng_shortcuts_chat_self" = "Saved Messages";
"lng_shortcuts_chat_pinned_n" = "Pinned chat #{index}";
"lng_shortcuts_show_account_n" = "Account #{index}";
"lng_shortcuts_show_all_chats" = "All Chats folder";
"lng_shortcuts_show_folder_n" = "Folder #{index}";
"lng_shortcuts_show_folder_last" = "Last folder";
"lng_shortcuts_folder_next" = "Next folder";
"lng_shortcuts_folder_previous" = "Previous folder";
"lng_shortcuts_scheduled" = "Scheduled messages";
"lng_shortcuts_archive" = "Archived chats";
"lng_shortcuts_contacts" = "Contacts list";
"lng_shortcuts_just_send" = "Just send";
"lng_shortcuts_silent_send" = "Silent send";
"lng_shortcuts_schedule" = "Schedule";
"lng_shortcuts_read_chat" = "Mark chat as read";
"lng_shortcuts_archive_chat" = "Archive chat";
"lng_shortcuts_media_fullscreen" = "Toggle video fullscreen";
"lng_shortcuts_show_chat_menu" = "Show chat menu";
"lng_settings_chat_reactions_title" = "Quick Reaction";
"lng_settings_chat_reactions_subtitle" = "Choose your favorite reaction";
"lng_settings_chat_message_reply_from" = "Bob Harris";
@@ -1346,6 +1389,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_common_groups#other" = "{count} groups in common";
"lng_profile_similar_channels#one" = "{count} similar channel";
"lng_profile_similar_channels#other" = "{count} similar channels";
"lng_profile_similar_bots#one" = "{count} similar bot";
"lng_profile_similar_bots#other" = "{count} similar bots";
"lng_profile_saved_messages#one" = "{count} saved message";
"lng_profile_saved_messages#other" = "{count} saved messages";
"lng_profile_peer_gifts#one" = "{count} gift";
@@ -2020,16 +2065,28 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_gift_sent" = "You sent a gift for {cost}";
"lng_action_gift_unique_sent" = "You sent a unique collectible item";
"lng_action_gift_upgraded" = "{user} turned the gift from you into a unique collectible";
"lng_action_gift_upgraded_channel" = "{user} turned this gift to {channel} into a unique collectible";
"lng_action_gift_upgraded_self_channel" = "You turned this gift to {channel} into a unique collectible";
"lng_action_gift_upgraded_mine" = "You turned the gift from {user} into a unique collectible";
"lng_action_gift_upgraded_self" = "You turned this gift into a unique collectible";
"lng_action_gift_transferred" = "{user} transferred you a gift";
"lng_action_gift_transferred_channel" = "{user} transferred a gift to {channel}";
"lng_action_gift_transferred_unknown" = "Someone transferred you a gift";
"lng_action_gift_transferred_unknown_channel" = "Someone transferred a gift to {channel}";
"lng_action_gift_transferred_self" = "You transferred a unique collectible";
"lng_action_gift_transferred_self_channel" = "You transferred a gift to {channel}";
"lng_action_gift_transferred_mine" = "You transferred a gift to {user}";
"lng_action_gift_received_anonymous" = "Unknown user sent you a gift for {cost}";
"lng_action_gift_sent_channel" = "{user} sent a gift to {name} for {cost}";
"lng_action_gift_sent_self_channel" = "You sent a gift to {name} for {cost}";
"lng_action_gift_self_bought" = "You bought a gift for {cost}";
"lng_action_gift_self_subtitle" = "Saved Gift";
"lng_action_gift_self_about#one" = "Display this gift on your page or convert it to **{count}** Star.";
"lng_action_gift_self_about#other" = "Display this gift on your page or convert it to **{count}** Stars.";
"lng_action_gift_self_about_unique" = "You can display this gift on your page or turn it into unique collectible and send to others.";
"lng_action_gift_channel_about#one" = "Display this gift in channel's Gifts or convert it to **{count}** Star.";
"lng_action_gift_channel_about#other" = "Display this gift in channel's Gifts or convert it to **{count}** Stars.";
"lng_action_gift_channel_about_unique" = "You can display this gift in channel's Gifts or turn it into unique collectible.";
"lng_action_gift_for_stars#one" = "{count} Star";
"lng_action_gift_for_stars#other" = "{count} Stars";
"lng_action_gift_got_subtitle" = "Gift from {user}";
@@ -2038,6 +2095,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_gift_got_upgradable_text" = "Upgrade this gift to a unique collectible.";
"lng_action_gift_got_gift_text" = "You can keep this gift on your page.";
"lng_action_gift_can_remove_text" = "You can remove this gift from your page.";
"lng_action_gift_got_gift_channel" = "You can keep this gift in channel's Gifts.";
"lng_action_gift_can_remove_channel" = "You can remove this gift from channel's Gifts.";
"lng_action_gift_sent_subtitle" = "Gift for {user}";
"lng_action_gift_sent_text#one" = "{user} can display this gift on their page or convert it to {count} Star.";
"lng_action_gift_sent_text#other" = "{user} can display this gift on their page or convert it to {count} Stars.";
@@ -2108,9 +2167,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_similar_channels_premium_all_link" = "Telegram Premium";
"lng_similar_channels_show_more" = "Show more channels";
"lng_similar_bots_title" = "Similar bots";
"lng_similar_bots_premium_all#one" = "Subscribe to {link} to unlock up to **{count}** similar bot.";
"lng_similar_bots_premium_all#other" = "Subscribe to {link} to unlock up to **{count}** similar bots.";
"lng_similar_bots_show_more" = "Show more bots";
"lng_peer_gifts_title" = "Gifts";
"lng_peer_gifts_about" = "These gifts were sent to {user} by other users.";
"lng_peer_gifts_about_mine" = "These gifts were sent to you by other users. Click on a gift to convert it to Stars or change its privacy settings.";
"lng_peer_gifts_notify" = "Notify About New Gifts";
"lng_peer_gifts_notify_enabled" = "You will receive a message from Telegram when your channel receives a gift.";
"lng_peer_gifts_filter_by_value" = "Sort by Value";
"lng_peer_gifts_filter_by_date" = "Sort by Date";
"lng_peer_gifts_filter_unlimited" = "Unlimited";
"lng_peer_gifts_filter_limited" = "Limited";
"lng_peer_gifts_filter_unique" = "Unique";
"lng_peer_gifts_filter_saved" = "Displayed";
"lng_peer_gifts_filter_unsaved" = "Hidden";
"lng_premium_gift_duration_months#one" = "for {count} month";
"lng_premium_gift_duration_months#other" = "for {count} months";
@@ -2373,6 +2446,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_stickers_add" = "Choose sticker set";
"lng_group_emoji" = "Select Emoji Pack";
"lng_group_emoji_description" = "Choose an emoji pack that will be available to all members within the group.";
"lng_collectible_emoji" = "Collectibles";
"lng_premium" = "Premium";
"lng_premium_free" = "Free";
@@ -2517,6 +2591,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_premium_double_limits_subtitle_accounts" = "Connected Accounts";
"lng_premium_double_limits_about_accounts#one" = "Connect {count} account with different mobile numbers";
"lng_premium_double_limits_about_accounts#other" = "Connect {count} accounts with different mobile numbers";
"lng_premium_double_limits_subtitle_similar_channels" = "Similar Channel";
"lng_premium_double_limits_about_similar_channels#one" = "View up to {count} similar channel";
"lng_premium_double_limits_about_similar_channels#other" = "View up to {count} similar channels";
//
"lng_premium_gift_title" = "Gift Telegram Premium";
@@ -2960,11 +3038,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_boost_group_needs_level_emoji#one" = "Your group needs to reach **Level {count}** to set emoji pack.";
"lng_boost_group_needs_level_emoji#other" = "Your group needs to reach **Level {count}** to set emoji pack.";
"lng_boost_channel_title_wear" = "Wear Item";
"lng_boost_channel_needs_level_wear#one" = "Your channel needs **Level {count}** to wear collectibles.";
"lng_boost_channel_needs_level_wear#other" = "Your channel needs **Level {count}** to wear collectibles.";
"lng_boost_channel_ask" = "Ask your **Premium** subscribers to boost your channel with this link:";
"lng_boost_channel_ask_button" = "Copy Link";
"lng_boost_channel_or" = "or";
"lng_boost_channel_gifting" = "Boost your channel by gifting your subscribers Telegram Premium. {link}";
"lng_boost_channel_gifting_link" = "Get boosts >";
//"lng_boost_channel_or" = "or";
//"lng_boost_channel_gifting" = "Boost your channel by gifting your subscribers Telegram Premium. {link}";
//"lng_boost_channel_gifting_link" = "Get boosts >";
"lng_boost_group_ask" = "Ask your **Premium** members to boost your group with this link:";
//"lng_boost_group_gifting" = "Boost your group by gifting your members Telegram Premium. {link}";
"lng_feature_stories#one" = "**{count}** Story Per Day";
"lng_feature_stories#other" = "**{count}** Stories Per Day";
@@ -3239,10 +3323,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_send_anonymous" = "Hide My Name";
"lng_gift_send_anonymous_self" = "Hide my name and message from visitors to my profile.";
"lng_gift_send_anonymous_about" = "You can hide your name and message from visitors to {user}'s profile. {recipient} will still see your name and message.";
"lng_gift_send_anonymous_about_channel" = "You can hide your name and message from all visitors of this channel except its admins.";
"lng_gift_send_unique" = "Make Unique for {price}";
"lng_gift_send_unique_about" = "Enable this to let {user} turn your gift into a unique collectible. {link}";
"lng_gift_send_unique_about_channel" = "Enable this to let the admins of {name} turn your gift into a unique collectible. {link}";
"lng_gift_send_unique_link" = "Learn More >";
"lng_gift_send_premium_about" = "Only {user} will see your message.";
"lng_gift_send_limited_sold#one" = "{count} sold";
"lng_gift_send_limited_sold#other" = "{count} sold";
"lng_gift_send_limited_left#one" = "{count} left";
"lng_gift_send_limited_left#other" = "{count} left";
"lng_gift_send_button" = "Send a Gift for {cost}";
"lng_gift_send_button_self" = "Buy a Gift for {cost}";
"lng_gift_sent_title" = "Gift Sent!";
@@ -3254,20 +3344,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_price_unique" = "Unique";
"lng_gift_view_unpack" = "Unpack";
"lng_gift_anonymous_hint" = "Only you can see the sender's name.";
"lng_gift_anonymous_hint_channel" = "Only admins of this channel can see the sender's name.";
"lng_gift_hidden_hint" = "This gift is hidden. Only you can see it.";
"lng_gift_visible_hint" = "This gift is visible to visitors of your page.";
"lng_gift_hidden_unique" = "This gift is not displayed on your page.";
"lng_gift_visible_hint" = "This gift is visible on your page.";
"lng_gift_hidden_hint_channel" = "This gift is hidden from visitors of your channel.";
"lng_gift_visible_hint_channel" = "This gift is visible in your channel's Gifts.";
"lng_gift_in_blockchain" = "This gift is in TON blockchain. {link}";
"lng_gift_in_blockchain_link" = "View >";
"lng_gift_visible_hide" = "Hide >";
"lng_gift_show_on_page" = "Display on my Page";
"lng_gift_show_on_channel" = "Display in channel's Gifts";
"lng_gift_availability" = "Availability";
"lng_gift_from_hidden" = "Hidden User";
"lng_gift_visibility" = "Visibility";
"lng_gift_visibility_shown" = "Visible on your page";
"lng_gift_visibility_hidden" = "Not visible on your page";
"lng_gift_visibility_show" = "show";
"lng_gift_visibility_hide" = "hide";
"lng_gift_self_status" = "buy yourself a gift";
"lng_gift_self_title" = "Buy a Gift";
"lng_gift_self_about" = "Buy yourself a gift to display on your page or reserve for later.\n\nLimited-edition gifts upgraded to collectibles can be gifted to others later.";
"lng_gift_channel_title" = "Send a Gift";
"lng_gift_channel_about" = "Select a gift to show appreciation for {name}.";
"lng_gift_unique_owner" = "Owner";
"lng_gift_unique_owner_change" = "change";
"lng_gift_unique_address_copied" = "Address copied to clipboard.";
"lng_gift_unique_status" = "Status";
"lng_gift_unique_status_non" = "Non-Unique";
"lng_gift_unique_status_upgrade" = "upgrade";
@@ -3276,6 +3372,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_unique_backdrop" = "Backdrop";
"lng_gift_unique_symbol" = "Symbol";
"lng_gift_unique_rarity" = "Only {percent} of such collectibles have this attribute.";
"lng_gift_unique_availability_label" = "Quantity";
"lng_gift_unique_availability#one" = "{count} of {amount} issued";
"lng_gift_unique_availability#other" = "{count} of {amount} issued";
"lng_gift_unique_info" = "Gifted to {recipient} on {date}.";
@@ -3291,14 +3388,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_convert_sure_title" = "Convert Gift to Stars";
"lng_gift_convert_sure_confirm#one" = "Do you want to convert this gift from {user} to **{count} Star**?";
"lng_gift_convert_sure_confirm#other" = "Do you want to convert this gift from {user} to **{count} Stars**?";
"lng_gift_convert_sure_confirm_channel#one" = "Do you want to convert this gift to {channel} to **{count} Star**?";
"lng_gift_convert_sure_confirm_channel#other" = "Do you want to convert this gift to {channel} to **{count} Stars**?";
"lng_gift_convert_sure_limit#one" = "Conversion is available for the next **{count} day**.";
"lng_gift_convert_sure_limit#other" = "Conversion is available for the next **{count} days**.";
"lng_gift_convert_sure_caution" = "This action cannot be undone. This will permanently destroy the gift.";
"lng_gift_convert_sure" = "Convert";
"lng_gift_display_done" = "The gift is now shown on your profile page.";
"lng_gift_display_done_channel" = "The gift is now shown in channel's Gifts.";
"lng_gift_display_done_hide" = "The gift is now hidden from your profile page.";
"lng_gift_display_done_hide_channel" = "The gift is now hidden from channel's Gifts.";
"lng_gift_got_stars#one" = "You got **{count} Star** for this gift.";
"lng_gift_got_stars#other" = "You got **{count} Stars** for this gift.";
"lng_gift_channel_got#one" = "Channel got **{count} Star** for this gift.";
"lng_gift_channel_got#other" = "Channel got **{count} Stars** for this gift.";
"lng_gift_sold_out_title" = "Sold Out!";
"lng_gift_sold_out_text#one" = "All {count} gift was already sold.";
"lng_gift_sold_out_text#other" = "All {count} gifts were already sold.";
@@ -3309,6 +3412,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_upgrade_about" = "Turn your gift into a unique collectible\nthat you can transfer or auction.";
"lng_gift_upgrade_preview_title" = "Make Unique";
"lng_gift_upgrade_preview_about" = "Let {name} turn your gift into a unique collectible.";
"lng_gift_upgrade_preview_about_channel" = "Let the admins of {name} turn your gift into a unique collectible.";
"lng_gift_upgrade_unique_title" = "Unique";
"lng_gift_upgrade_unique_about" = "Get a unique number, model, backdrop and symbol for your gift.";
"lng_gift_upgrade_transferable_title" = "Transferable";
@@ -3328,6 +3432,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_transferred_about" = "{name} was successfully transferred to {recipient}.";
"lng_gift_transfer_title" = "Transfer {name}";
"lng_gift_transfer_via_blockchain" = "Send via Blockchain";
"lng_gift_transfer_password_title" = "Two-step verification";
"lng_gift_transfer_password_description" = "Please enter your password to transfer.";
"lng_gift_transfer_password_about" = "You can withdraw only if you have:";
"lng_gift_transfer_confirm_title" = "Manage with Fragment";
"lng_gift_transfer_confirm_text" = "You can use Fragment, a third-party service, to transfer {name} to your TON account. After that, you can manage it as an NFT with any TON wallet outside Telegram.\n\nYou can also move such NFTs back to your Telegram account via Fragment.";
"lng_gift_transfer_confirm_button" = "Open Fragment";
"lng_gift_transfer_unlocks_days#one" = "unlocks in {count} day";
"lng_gift_transfer_unlocks_days#other" = "unlocks in {count} days";
"lng_gift_transfer_unlocks_hours#one" = "unlocks in {count} hour";
@@ -3344,6 +3454,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_transfer_sure_for" = "Do you want to transfer ownership of {name} to {recipient} for {price}?";
"lng_gift_transfer_button" = "Transfer";
"lng_gift_transfer_button_for" = "Transfer for {price}";
"lng_gift_transfer_wear" = "Wear";
"lng_gift_transfer_take_off" = "Take Off";
"lng_gift_wear_title" = "Wear {name}";
"lng_gift_wear_about" = "and get these benefits:";
"lng_gift_wear_badge_title" = "Radiant Badge";
"lng_gift_wear_badge_about" = "The glittering icon of this item will be displayed next to your name.";
"lng_gift_wear_badge_about_channel" = "The glittering icon of this item will be displayed next to channel's name.";
"lng_gift_wear_proof_title" = "Proof of Ownership";
"lng_gift_wear_proof_about" = "Clicking the icon of this item next to your name will show its info and owner.";
"lng_gift_wear_proof_about_channel" = "Clicking the icon of this item next to channel's name will show its info and owner.";
"lng_gift_wear_start" = "Start Wearing";
"lng_gift_wear_subscribe" = "Subscribe to {link} to wear collectibles.";
"lng_gift_wear_start_toast" = "You put on {name}";
"lng_gift_wear_end_toast" = "You took off {name}";
"lng_accounts_limit_title" = "Limit Reached";
"lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected account.";
@@ -3759,6 +3883,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_choose_image" = "Choose an image";
"lng_choose_file" = "Choose a file";
"lng_choose_files" = "Choose Files";
"lng_choose_cover" = "Choose video cover";
"lng_choose_cover_bad" = "Can't use this file as a caption.";
"lng_game_tag" = "Game";
"lng_context_new_window" = "Open in new window";
@@ -3875,6 +4001,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_read_show" = "show when";
"lng_context_edit_shortcut" = "Edit Shortcut";
"lng_context_delete_shortcut" = "Delete Quick Reply";
"lng_context_gift_send" = "Send Another Gift";
"lng_add_tag_about" = "Tag this message with an emoji for quick search.";
"lng_subscribe_tag_about" = "Organize your Saved Messages with tags. {link}";
@@ -3903,6 +4030,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_disable_spoiler" = "Remove Spoiler";
"lng_context_make_paid" = "Make This Content Paid";
"lng_context_change_price" = "Change Price";
"lng_context_edit_cover" = "Edit Cover";
"lng_context_clear_cover" = "Clear Cover";
"lng_context_mention" = "Mention";
"lng_context_search_from" = "Search messages";
@@ -4032,6 +4161,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_reply_cant_forward" = "Sorry, you can't reply to a message that was sent before the group was upgraded to a supergroup. Do you wish to forward it and add your comment?";
"lng_share_title" = "Share to";
"lng_share_at_time_title" = "Share at {time} to";
"lng_share_copy_link" = "Copy share link";
"lng_share_confirm" = "Send";
"lng_share_wrong_user" = "This game was opened from a different user.";
@@ -4155,7 +4285,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_media_save_progress" = "{ready} of {total} {mb}";
"lng_mediaview_save_as" = "Save As...";
"lng_mediaview_copy" = "Copy";
"lng_mediaview_copy_frame" = "Copy Frame";
"lng_mediaview_forward" = "Forward";
"lng_mediaview_share_at_time" = "Share at {time}";
"lng_mediaview_delete" = "Delete";
"lng_mediaview_save_to_profile" = "Post to Profile";
"lng_mediaview_pin_story_done" = "Story pinned";
@@ -5520,6 +5652,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_view_button_iv" = "Instant View";
"lng_view_button_stickerset" = "View stickers";
"lng_view_button_emojipack" = "View emoji";
"lng_view_button_collectible" = "View collectible";
"lng_sponsored_hide_ads" = "Hide";
"lng_sponsored_title" = "What are sponsored messages?";
@@ -5836,6 +5969,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_boosts_list_tab_gifts#other" = "{count} Gifts";
"lng_boosts_prepaid_giveaway_title" = "Prepaid giveaways";
"lng_boosts_prepaid_giveaway_title_subtext" = "Select a giveaway you already paid for to set it up.";
"lng_boosts_prepaid_giveaway_single" = "Prepaid giveaway";
"lng_boosts_prepaid_giveaway_quantity#one" = "{count} Telegram Premium";
"lng_boosts_prepaid_giveaway_quantity#other" = "{count} Telegram Premium";
@@ -5895,6 +6029,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_channel_earn_learn_coin_link" = "https://telegram.org/blog/monetization-for-channels";
"lng_channel_earn_chart_top_hours" = "Ad impressions";
"lng_channel_earn_chart_revenue" = "Ad rewards";
"lng_channel_earn_chart_overriden_detail_credits" = "Rewards in Stars";
"lng_channel_earn_chart_overriden_detail_currency" = "Rewards in TON";
"lng_channel_earn_chart_overriden_detail_usd" = "Rewards in USD";
"lng_channel_earn_currency_history" = "TON Transactions";
+1 -1
View File
@@ -10,7 +10,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="5.10.3.0" />
Version="5.11.1.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
+4 -4
View File
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 5,10,3,0
PRODUCTVERSION 5,10,3,0
FILEVERSION 5,11,1,0
PRODUCTVERSION 5,11,1,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop"
VALUE "FileVersion", "5.10.3.0"
VALUE "FileVersion", "5.11.1.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "5.10.3.0"
VALUE "ProductVersion", "5.11.1.0"
END
END
BLOCK "VarFileInfo"
+4 -4
View File
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 5,10,3,0
PRODUCTVERSION 5,10,3,0
FILEVERSION 5,11,1,0
PRODUCTVERSION 5,11,1,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop Updater"
VALUE "FileVersion", "5.10.3.0"
VALUE "FileVersion", "5.11.1.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "5.10.3.0"
VALUE "ProductVersion", "5.11.1.0"
END
END
BLOCK "VarFileInfo"
@@ -217,7 +217,11 @@ void Authorizations::toggleCallsDisabled(uint64 hash, bool disabled) {
MTP_bool(disabled)
)).done([=] {
_toggleCallsDisabledRequests.remove(hash);
}).fail([=] {
}).fail([=](const MTP::Error &error) {
LOG(("API Error: toggle calls %1. Hash: %2. %3.")
.arg(disabled ? u"disabled"_q : u"enabled"_q)
.arg(hash)
.arg(error.type()));
_toggleCallsDisabledRequests.remove(hash);
}).send();
_toggleCallsDisabledRequests.emplace(hash, id);
@@ -211,11 +211,10 @@ void ApplyBotsList(
Data::PeerUpdate::Flag::FullInfo);
}
[[nodiscard]] ChatParticipants::Channels ParseSimilar(
[[nodiscard]] ChatParticipants::Peers ParseSimilarChannels(
not_null<Main::Session*> session,
const MTPmessages_Chats &chats) {
auto result = ChatParticipants::Channels();
std::vector<not_null<ChannelData*>>();
auto result = ChatParticipants::Peers();
chats.match([&](const auto &data) {
const auto &list = data.vchats().v;
result.list.reserve(list.size());
@@ -234,10 +233,29 @@ void ApplyBotsList(
return result;
}
[[nodiscard]] ChatParticipants::Channels ParseSimilar(
[[nodiscard]] ChatParticipants::Peers ParseSimilarChannels(
not_null<ChannelData*> channel,
const MTPmessages_Chats &chats) {
return ParseSimilar(&channel->session(), chats);
return ParseSimilarChannels(&channel->session(), chats);
}
[[nodiscard]] ChatParticipants::Peers ParseSimilarBots(
not_null<Main::Session*> session,
const MTPusers_Users &users) {
auto result = ChatParticipants::Peers();
users.match([&](const auto &data) {
const auto &list = data.vusers().v;
result.list.reserve(list.size());
for (const auto &user : list) {
result.list.push_back(session->data().processUser(user));
}
if constexpr (MTPDusers_usersSlice::Is<decltype(data)>()) {
if (session->premiumPossible()) {
result.more = data.vcount().v - data.vusers().v.size();
}
}
});
return result;
}
} // namespace
@@ -782,52 +800,65 @@ void ChatParticipants::unblock(
_kickRequests.emplace(kick, requestId);
}
void ChatParticipants::loadSimilarChannels(not_null<ChannelData*> channel) {
if (!channel->isBroadcast()) {
return;
} else if (const auto i = _similar.find(channel); i != end(_similar)) {
void ChatParticipants::loadSimilarPeers(not_null<PeerData*> peer) {
if (const auto i = _similar.find(peer); i != end(_similar)) {
if (i->second.requestId
|| !i->second.channels.more
|| !channel->session().premium()) {
|| !i->second.peers.more
|| !peer->session().premium()) {
return;
}
}
using Flag = MTPchannels_GetChannelRecommendations::Flag;
_similar[channel].requestId = _api.request(
MTPchannels_GetChannelRecommendations(
MTP_flags(Flag::f_channel),
channel->inputChannel)
).done([=](const MTPmessages_Chats &result) {
auto &similar = _similar[channel];
similar.requestId = 0;
auto parsed = ParseSimilar(channel, result);
if (similar.channels == parsed) {
return;
}
similar.channels = std::move(parsed);
if (const auto history = channel->owner().historyLoaded(channel)) {
if (const auto item = history->joinedMessageInstance()) {
history->owner().requestItemResize(item);
if (const auto channel = peer->asBroadcast()) {
using Flag = MTPchannels_GetChannelRecommendations::Flag;
_similar[peer].requestId = _api.request(
MTPchannels_GetChannelRecommendations(
MTP_flags(Flag::f_channel),
channel->inputChannel)
).done([=](const MTPmessages_Chats &result) {
auto &similar = _similar[channel];
similar.requestId = 0;
auto parsed = ParseSimilarChannels(channel, result);
if (similar.peers == parsed) {
return;
}
}
_similarLoaded.fire_copy(channel);
}).send();
similar.peers = std::move(parsed);
if (const auto history = channel->owner().historyLoaded(channel)) {
if (const auto item = history->joinedMessageInstance()) {
history->owner().requestItemResize(item);
}
}
_similarLoaded.fire_copy(channel);
}).send();
} else if (const auto bot = peer->asBot()) {
_similar[peer].requestId = _api.request(
MTPbots_GetBotRecommendations(bot->inputUser)
).done([=](const MTPusers_Users &result) {
auto &similar = _similar[peer];
similar.requestId = 0;
auto parsed = ParseSimilarBots(&peer->session(), result);
if (similar.peers == parsed) {
return;
}
similar.peers = std::move(parsed);
_similarLoaded.fire_copy(peer);
}).send();
}
}
auto ChatParticipants::similar(not_null<ChannelData*> channel)
-> const Channels & {
const auto i = channel->isBroadcast()
? _similar.find(channel)
auto ChatParticipants::similar(not_null<PeerData*> peer)
-> const Peers & {
const auto i = (peer->isBroadcast() || peer->isBot())
? _similar.find(peer)
: end(_similar);
if (i != end(_similar)) {
return i->second.channels;
return i->second.peers;
}
static const auto empty = Channels();
static const auto empty = Peers();
return empty;
}
auto ChatParticipants::similarLoaded() const
-> rpl::producer<not_null<ChannelData*>> {
-> rpl::producer<not_null<PeerData*>> {
return _similarLoaded.events();
}
@@ -841,15 +872,15 @@ void ChatParticipants::loadRecommendations() {
MTP_inputChannelEmpty())
).done([=](const MTPmessages_Chats &result) {
_recommendations.requestId = 0;
auto parsed = ParseSimilar(_session, result);
_recommendations.channels = std::move(parsed);
_recommendations.channels.more = 0;
auto parsed = ParseSimilarChannels(_session, result);
_recommendations.peers = std::move(parsed);
_recommendations.peers.more = 0;
_recommendationsLoaded = true;
}).send();
}
const ChatParticipants::Channels &ChatParticipants::recommendations() const {
return _recommendations.channels;
const ChatParticipants::Peers &ChatParticipants::recommendations() const {
return _recommendations.peers;
}
rpl::producer<> ChatParticipants::recommendationsLoaded() const {
@@ -138,27 +138,27 @@ public:
not_null<ChannelData*> channel,
not_null<PeerData*> participant);
void loadSimilarChannels(not_null<ChannelData*> channel);
void loadSimilarPeers(not_null<PeerData*> peer);
struct Channels {
std::vector<not_null<ChannelData*>> list;
struct Peers {
std::vector<not_null<PeerData*>> list;
int more = 0;
friend inline bool operator==(
const Channels &,
const Channels &) = default;
const Peers &,
const Peers &) = default;
};
[[nodiscard]] const Channels &similar(not_null<ChannelData*> channel);
[[nodiscard]] const Peers &similar(not_null<PeerData*> peer);
[[nodiscard]] auto similarLoaded() const
-> rpl::producer<not_null<ChannelData*>>;
-> rpl::producer<not_null<PeerData*>>;
void loadRecommendations();
[[nodiscard]] const Channels &recommendations() const;
[[nodiscard]] const Peers &recommendations() const;
[[nodiscard]] rpl::producer<> recommendationsLoaded() const;
private:
struct SimilarChannels {
Channels channels;
struct SimilarPeers {
Peers peers;
mtpRequestId requestId = 0;
};
@@ -186,10 +186,10 @@ private:
not_null<PeerData*>>;
base::flat_map<KickRequest, mtpRequestId> _kickRequests;
base::flat_map<not_null<ChannelData*>, SimilarChannels> _similar;
rpl::event_stream<not_null<ChannelData*>> _similarLoaded;
base::flat_map<not_null<PeerData*>, SimilarPeers> _similar;
rpl::event_stream<not_null<PeerData*>> _similarLoaded;
SimilarChannels _recommendations;
SimilarPeers _recommendations;
rpl::variable<bool> _recommendationsLoaded = false;
};
+1
View File
@@ -74,6 +74,7 @@ struct MessageToSend {
struct RemoteFileInfo {
MTPInputFile file;
std::optional<MTPInputFile> thumb;
std::optional<MTPInputPhoto> videoCover;
std::vector<MTPInputDocument> attachedStickers;
};
+8
View File
@@ -513,4 +513,12 @@ void EditCreditsSubscription(
)).done(done).fail([=](const MTP::Error &e) { fail(e.type()); }).send();
}
MTPInputSavedStarGift InputSavedStarGiftId(const Data::SavedStarGiftId &id) {
return id.isUser()
? MTP_inputSavedStarGiftUser(MTP_int(id.userMessageId().bare))
: MTP_inputSavedStarGiftChat(
id.chat()->input,
MTP_long(id.chatSavedId()));
}
} // namespace Api
+7
View File
@@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_credits_earn.h"
#include "mtproto/sender.h"
namespace Data {
class SavedStarGiftId;
} // namespace Data
namespace Main {
class Session;
} // namespace Main
@@ -116,4 +120,7 @@ void EditCreditsSubscription(
Fn<void()> done,
Fn<void(QString)> fail);
[[nodiscard]] MTPInputSavedStarGift InputSavedStarGiftId(
const Data::SavedStarGiftId &id);
} // namespace Api
+9 -1
View File
@@ -276,14 +276,22 @@ mtpRequestId EditTextMessage(
takeFileReference = [=] { return photo->fileReference(); };
} else if (const auto document = media->document()) {
using Flag = MTPDinputMediaDocument::Flag;
const auto videoCover = media->videoCover();
const auto videoTimestamp = media->videoTimestamp();
const auto flags = Flag()
| (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag())
| (spoilered ? Flag::f_spoiler : Flag());
| (spoilered ? Flag::f_spoiler : Flag())
| (videoTimestamp ? Flag::f_video_timestamp : Flag())
| (videoCover ? Flag::f_video_cover : Flag());
takeInputMedia = [=] {
return MTP_inputMediaDocument(
MTP_flags(flags),
document->mtpInput(),
(videoCover
? videoCover->mtpInput()
: MTPInputPhoto()),
MTP_int(media->ttlSeconds()),
MTP_int(videoTimestamp),
MTPstring()); // query
};
takeFileReference = [=] { return document->fileReference(); };
@@ -13,6 +13,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Api {
PeerId ParsePaidReactionShownPeer(
not_null<Main::Session*> session,
const MTPPaidReactionPrivacy &value) {
return value.match([&](const MTPDpaidReactionPrivacyDefault &) {
return session->userPeerId();
}, [](const MTPDpaidReactionPrivacyAnonymous &) {
return PeerId();
}, [&](const MTPDpaidReactionPrivacyPeer &data) {
return data.vpeer().match([&](const MTPDinputPeerSelf &) {
return session->userPeerId();
}, [](const MTPDinputPeerUser &data) {
return peerFromUser(data.vuser_id());
}, [](const MTPDinputPeerChat &data) {
return peerFromChat(data.vchat_id());
}, [](const MTPDinputPeerChannel &data) {
return peerFromChannel(data.vchannel_id());
}, [](const MTPDinputPeerUserFromMessage &data) -> PeerId {
Unexpected("From message peer in ParsePaidReactionShownPeer.");
}, [](const MTPDinputPeerChannelFromMessage &data) -> PeerId {
Unexpected("From message peer in ParsePaidReactionShownPeer.");
}, [](const MTPDinputPeerEmpty &) -> PeerId {
Unexpected("Empty peer in ParsePaidReactionShownPeer.");
});
});
}
GlobalPrivacy::GlobalPrivacy(not_null<ApiWrap*> api)
: _session(&api->session())
, _api(&api->instance()) {
@@ -115,27 +141,27 @@ rpl::producer<bool> GlobalPrivacy::newRequirePremium() const {
return _newRequirePremium.value();
}
void GlobalPrivacy::loadPaidReactionAnonymous() {
if (_paidReactionAnonymousLoaded) {
void GlobalPrivacy::loadPaidReactionShownPeer() {
if (_paidReactionShownPeerLoaded) {
return;
}
_paidReactionAnonymousLoaded = true;
_paidReactionShownPeerLoaded = true;
_api.request(MTPmessages_GetPaidReactionPrivacy(
)).done([=](const MTPUpdates &result) {
_session->api().applyUpdates(result);
}).send();
}
void GlobalPrivacy::updatePaidReactionAnonymous(bool value) {
_paidReactionAnonymous = value;
void GlobalPrivacy::updatePaidReactionShownPeer(PeerId shownPeer) {
_paidReactionShownPeer = shownPeer;
}
bool GlobalPrivacy::paidReactionAnonymousCurrent() const {
return _paidReactionAnonymous.current();
PeerId GlobalPrivacy::paidReactionShownPeerCurrent() const {
return _paidReactionShownPeer.current();
}
rpl::producer<bool> GlobalPrivacy::paidReactionAnonymous() const {
return _paidReactionAnonymous.value();
rpl::producer<PeerId> GlobalPrivacy::paidReactionShownPeer() const {
return _paidReactionShownPeer.value();
}
void GlobalPrivacy::updateArchiveAndMute(bool value) {
+10 -6
View File
@@ -23,6 +23,10 @@ enum class UnarchiveOnNewMessage {
AnyUnmuted,
};
[[nodiscard]] PeerId ParsePaidReactionShownPeer(
not_null<Main::Session*> session,
const MTPPaidReactionPrivacy &value);
class GlobalPrivacy final {
public:
explicit GlobalPrivacy(not_null<ApiWrap*> api);
@@ -49,10 +53,10 @@ public:
[[nodiscard]] bool newRequirePremiumCurrent() const;
[[nodiscard]] rpl::producer<bool> newRequirePremium() const;
void loadPaidReactionAnonymous();
void updatePaidReactionAnonymous(bool value);
[[nodiscard]] bool paidReactionAnonymousCurrent() const;
[[nodiscard]] rpl::producer<bool> paidReactionAnonymous() const;
void loadPaidReactionShownPeer();
void updatePaidReactionShownPeer(PeerId shownPeer);
[[nodiscard]] PeerId paidReactionShownPeerCurrent() const;
[[nodiscard]] rpl::producer<PeerId> paidReactionShownPeer() const;
private:
void apply(const MTPGlobalPrivacySettings &data);
@@ -72,9 +76,9 @@ private:
rpl::variable<bool> _showArchiveAndMute = false;
rpl::variable<bool> _hideReadTime = false;
rpl::variable<bool> _newRequirePremium = false;
rpl::variable<bool> _paidReactionAnonymous = false;
rpl::variable<PeerId> _paidReactionShownPeer = false;
std::vector<Fn<void()>> _callbacks;
bool _paidReactionAnonymousLoaded = false;
bool _paidReactionShownPeerLoaded = false;
};
+4 -1
View File
@@ -111,7 +111,8 @@ MTPInputMedia PrepareUploadedDocument(
| (info.thumb ? Flag::f_thumb : Flag())
| (item->groupId() ? Flag::f_nosound_video : Flag())
| (info.attachedStickers.empty() ? Flag::f_stickers : Flag())
| (ttlSeconds ? Flag::f_ttl_seconds : Flag());
| (ttlSeconds ? Flag::f_ttl_seconds : Flag())
| (info.videoCover ? Flag::f_video_cover : Flag());
const auto document = item->media()->document();
return MTP_inputMediaUploadedDocument(
MTP_flags(flags),
@@ -121,6 +122,8 @@ MTPInputMedia PrepareUploadedDocument(
ComposeSendingDocumentAttributes(document),
MTP_vector<MTPInputDocument>(
ranges::to<QVector<MTPInputDocument>>(info.attachedStickers)),
info.videoCover.value_or(MTPInputPhoto()),
MTP_int(0), // video_timestamp
MTP_int(ttlSeconds));
}
@@ -103,6 +103,7 @@ void MessagesSearch::searchRequest() {
_requestId = _history->session().api().request(MTPmessages_Search(
MTP_flags((fromPeer ? Flag::f_from_id : Flag())
| (savedPeer ? Flag::f_saved_peer_id : Flag())
| (_request.topMsgId ? Flag::f_top_msg_id : Flag())
| (_request.tags.empty() ? Flag() : Flag::f_saved_reaction)),
_history->peer->input,
MTP_string(_request.query),
@@ -111,7 +112,7 @@ void MessagesSearch::searchRequest() {
MTP_vector_from_range(_request.tags | ranges::views::transform(
Data::ReactionToMTP
)),
MTPint(), // top_msg_id
MTP_int(_request.topMsgId), // top_msg_id
MTP_inputMessagesFilterEmpty(),
MTP_int(0), // min_date
MTP_int(0), // max_date
@@ -32,6 +32,7 @@ public:
QString query;
PeerData *from = nullptr;
std::vector<Data::ReactionId> tags;
MsgId topMsgId;
friend inline bool operator==(
const Request &,
@@ -64,6 +64,10 @@ MessagesSearchMerged::MessagesSearchMerged(not_null<History*> history)
}
}
void MessagesSearchMerged::disableMigrated() {
_migratedSearch = std::nullopt;
}
void MessagesSearchMerged::addFound(const FoundMessages &data) {
for (const auto &message : data.messages) {
_concatedFound.messages.push_back(message);
@@ -29,6 +29,7 @@ public:
void clear();
void search(const Request &search);
void searchMore();
void disableMigrated();
[[nodiscard]] const FoundMessages &messages() const;
[[nodiscard]] const Request &request() const;
+18 -11
View File
@@ -805,8 +805,14 @@ std::optional<Data::StarGift> FromTL(
auto result = Data::StarGift{
.id = uint64(data.vid().v),
.unique = std::make_shared<Data::UniqueGift>(Data::UniqueGift{
.id = data.vid().v,
.slug = qs(data.vslug()),
.title = qs(data.vtitle()),
.ownerId = peerFromUser(UserId(data.vowner_id().v)),
.ownerAddress = qs(data.vowner_address().value_or_empty()),
.ownerName = qs(data.vowner_name().value_or_empty()),
.ownerId = (data.vowner_id()
? peerFromMTP(*data.vowner_id())
: PeerId()),
.number = data.vnum().v,
.model = *model,
.pattern = *pattern,
@@ -829,9 +835,9 @@ std::optional<Data::StarGift> FromTL(
});
}
std::optional<Data::UserStarGift> FromTL(
not_null<UserData*> to,
const MTPuserStarGift &gift) {
std::optional<Data::SavedStarGift> FromTL(
not_null<PeerData*> to,
const MTPsavedStarGift &gift) {
const auto session = &to->session();
const auto &data = gift.data();
auto parsed = FromTL(session, data.vgift());
@@ -841,8 +847,12 @@ std::optional<Data::UserStarGift> FromTL(
unique->starsForTransfer = data.vtransfer_stars().value_or(-1);
unique->exportAt = data.vcan_export_at().value_or_empty();
}
return Data::UserStarGift{
using Id = Data::SavedStarGiftId;
return Data::SavedStarGift{
.info = std::move(*parsed),
.manageId = (to->isUser()
? Id::User(data.vmsg_id().value_or_empty())
: Id::Chat(to, data.vsaved_id().value_or_empty())),
.message = (data.vmessage()
? TextWithEntities{
.text = qs(data.vmessage()->data().vtext()),
@@ -855,9 +865,8 @@ std::optional<Data::UserStarGift> FromTL(
.starsUpgradedBySender = int64(
data.vupgrade_stars().value_or_empty()),
.fromId = (data.vfrom_id()
? peerFromUser(data.vfrom_id()->v)
? peerFromMTP(*data.vfrom_id())
: PeerId()),
.messageId = data.vmsg_id().value_or_empty(),
.date = data.vdate().v,
.upgradable = data.is_can_upgrade(),
.anonymous = data.is_name_hidden(),
@@ -910,11 +919,9 @@ Data::UniqueGiftOriginalDetails FromTL(
auto result = Data::UniqueGiftOriginalDetails();
result.date = data.vdate().v;
result.senderId = data.vsender_id()
? peerFromUser(
UserId(data.vsender_id().value_or_empty()))
? peerFromMTP(*data.vsender_id())
: PeerId();
result.recipientId = peerFromUser(
UserId(data.vrecipient_id().v));
result.recipientId = peerFromMTP(data.vrecipient_id());
result.message = data.vmessage()
? ParseTextWithEntities(session, *data.vmessage())
: TextWithEntities();
+3 -3
View File
@@ -259,9 +259,9 @@ enum class RequirePremiumState {
[[nodiscard]] std::optional<Data::StarGift> FromTL(
not_null<Main::Session*> session,
const MTPstarGift &gift);
[[nodiscard]] std::optional<Data::UserStarGift> FromTL(
not_null<UserData*> to,
const MTPuserStarGift &gift);
[[nodiscard]] std::optional<Data::SavedStarGift> FromTL(
not_null<PeerData*> to,
const MTPsavedStarGift &gift);
[[nodiscard]] Data::UniqueGiftModel FromTL(
not_null<Main::Session*> session,
+12 -2
View File
@@ -272,7 +272,9 @@ void SendExistingDocument(
return MTP_inputMediaDocument(
MTP_flags(0),
document->mtpInput(),
MTPInputPhoto(), // video_cover
MTPint(), // ttl_seconds
MTPint(), // video_timestamp
MTPstring()); // query
};
SendExistingMedia(
@@ -547,9 +549,12 @@ void SendConfirmedFile(
using Flag = MTPDmessageMediaDocument::Flag;
return MTP_messageMediaDocument(
MTP_flags(Flag::f_document
| (file->spoiler ? Flag::f_spoiler : Flag())),
| (file->spoiler ? Flag::f_spoiler : Flag())
| (file->videoCover ? Flag::f_video_cover : Flag())),
file->document,
MTPVector<MTPDocument>(), // alt_documents
file->videoCover ? file->videoCover->photo : MTPPhoto(),
MTPint(), // video_timestamp
MTPint());
} else if (file->type == SendMediaType::Audio) {
const auto ttlSeconds = file->to.options.ttlSeconds;
@@ -557,9 +562,12 @@ void SendConfirmedFile(
return MTP_messageMediaDocument(
MTP_flags(Flag::f_document
| Flag::f_voice
| (ttlSeconds ? Flag::f_ttl_seconds : Flag())),
| (ttlSeconds ? Flag::f_ttl_seconds : Flag())
| (file->videoCover ? Flag::f_video_cover : Flag())),
file->document,
MTPVector<MTPDocument>(), // alt_documents
file->videoCover ? file->videoCover->photo : MTPPhoto(),
MTPint(), // video_timestamp
MTP_int(ttlSeconds));
} else if (file->type == SendMediaType::Round) {
using Flag = MTPDmessageMediaDocument::Flag;
@@ -571,6 +579,8 @@ void SendConfirmedFile(
| (file->spoiler ? Flag::f_spoiler : Flag())),
file->document,
MTPVector<MTPDocument>(), // alt_documents
MTPPhoto(), // video_cover
MTPint(), // video_timestamp
MTP_int(ttlSeconds));
} else {
Unexpected("Type in sendFilesConfirmed.");
+2 -2
View File
@@ -2717,8 +2717,8 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updatePaidReactionPrivacy: {
const auto &data = update.c_updatePaidReactionPrivacy();
_session->api().globalPrivacy().updatePaidReactionAnonymous(
mtpIsTrue(data.vprivate()));
_session->api().globalPrivacy().updatePaidReactionShownPeer(
Api::ParsePaidReactionShownPeer(_session, data.vprivate()));
} break;
}
+64 -18
View File
@@ -148,6 +148,16 @@ void ShowChannelsLimitBox(not_null<PeerData*> peer) {
action.replaceMediaOf);
}
[[nodiscard]] QString FormatVideoTimestamp(TimeId seconds) {
const auto minutes = seconds / 60;
const auto hours = minutes / 60;
return hours
? u"%1h%2m%3s"_q.arg(hours).arg(minutes % 60).arg(seconds % 60)
: minutes
? u"%1m%2s"_q.arg(minutes).arg(seconds % 60)
: QString::number(seconds);
}
} // namespace
ApiWrap::ApiWrap(not_null<Main::Session*> session)
@@ -740,7 +750,8 @@ void ApiWrap::finalizeMessageDataRequest(
QString ApiWrap::exportDirectMessageLink(
not_null<HistoryItem*> item,
bool inRepliesContext,
bool forceNonPublicLink) {
bool forceNonPublicLink,
std::optional<TimeId> videoTimestamp) {
Expects(item->history()->peer->isChannel());
const auto itemId = item->fullId();
@@ -792,19 +803,6 @@ QString ApiWrap::exportDirectMessageLink(
: linkThreadId
? (QString::number(linkThreadId.bare) + '/' + post)
: post);
if (linkChannel->hasUsername()
&& !forceNonPublicLink
&& !linkChannel->isMegagroup()
&& !linkCommentId
&& !linkThreadId) {
if (const auto media = item->media()) {
if (const auto document = media->document()) {
if (document->isVideoMessage()) {
return u"https://telesco.pe/"_q + query;
}
}
}
}
return session().createInternalLinkFull(query);
};
if (forceNonPublicLink) {
@@ -826,7 +824,14 @@ QString ApiWrap::exportDirectMessageLink(
_unlikelyMessageLinks.emplace_or_assign(itemId, link);
}
}).send();
return current;
const auto addTimestamp = channel->hasUsername()
&& !inRepliesContext
&& videoTimestamp.has_value();
const auto addedSeparator = (current.indexOf('?') >= 0) ? '&' : '?';
const auto addedTimestamp = addTimestamp
? (addedSeparator + u"t="_q + FormatVideoTimestamp(*videoTimestamp))
: QString();
return current + addedTimestamp;
}
QString ApiWrap::exportDirectStoryLink(not_null<Data::Story*> story) {
@@ -1812,7 +1817,7 @@ void ApiWrap::joinChannel(not_null<ChannelData*> channel) {
_channelAmInRequests.emplace(channel, requestId);
using Flag = ChannelDataFlag;
chatParticipants().loadSimilarChannels(channel);
chatParticipants().loadSimilarPeers(channel);
const auto settings = &AyuSettings::getInstance();
if (!settings->collapseSimilarChannels) {
@@ -3422,7 +3427,8 @@ void ApiWrap::forwardMessages(
MTP_int(topMsgId),
MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, action.options.shortcutId)
Data::ShortcutIdToMTP(_session, action.options.shortcutId),
MTPint() // video_timestamp
)).done([=](const MTPUpdates &result) {
if (!scheduled) {
this->updates().checkForSentToScheduled(result);
@@ -3623,6 +3629,18 @@ void ApiWrap::editMedia(
file.path,
file.content,
std::move(file.information),
(file.videoCover
? std::make_unique<FileLoadTask>(
&session(),
file.videoCover->path,
file.videoCover->content,
std::move(file.videoCover->information),
nullptr,
SendMediaType::Photo,
to,
TextWithTags(),
false)
: nullptr),
type,
to,
caption,
@@ -3664,6 +3682,19 @@ void ApiWrap::sendFiles(
file.path,
file.content,
std::move(file.information),
(file.videoCover
? std::make_unique<FileLoadTask>(
&session(),
file.videoCover->path,
file.videoCover->content,
std::move(file.videoCover->information),
nullptr,
SendMediaType::Photo,
to,
TextWithTags(),
false,
nullptr)
: nullptr),
uploadWithType,
to,
caption,
@@ -3689,11 +3720,13 @@ void ApiWrap::sendFile(
auto caption = TextWithTags();
const auto spoiler = false;
const auto information = nullptr;
const auto videoCover = nullptr;
_fileLoader->addTask(std::make_unique<FileLoadTask>(
&session(),
QString(),
fileContent,
information,
videoCover,
type,
to,
caption,
@@ -4208,16 +4241,29 @@ void ApiWrap::uploadAlbumMedia(
return;
}
const auto &fields = document->c_document();
const auto mtpCover = data.vvideo_cover();
const auto cover = (mtpCover && mtpCover->type() == mtpc_photo)
? &(mtpCover->c_photo())
: (const MTPDphoto*)nullptr;
using Flag = MTPDinputMediaDocument::Flag;
const auto flags = Flag()
| (data.vttl_seconds() ? Flag::f_ttl_seconds : Flag())
| (spoiler ? Flag::f_spoiler : Flag());
| (spoiler ? Flag::f_spoiler : Flag())
| (data.vvideo_timestamp() ? Flag::f_video_timestamp : Flag())
| (cover ? Flag::f_video_cover : Flag());
const auto media = MTP_inputMediaDocument(
MTP_flags(flags),
MTP_inputDocument(
fields.vid(),
fields.vaccess_hash(),
fields.vfile_reference()),
(cover
? MTP_inputPhoto(
cover->vid(),
cover->vaccess_hash(),
cover->vfile_reference())
: MTPInputPhoto()),
MTP_int(data.vvideo_timestamp().value_or_empty()),
MTP_int(data.vttl_seconds().value_or_empty()),
MTPstring()); // query
sendAlbumWithUploaded(item, groupId, media);
+2 -1
View File
@@ -166,7 +166,8 @@ public:
QString exportDirectMessageLink(
not_null<HistoryItem*> item,
bool inRepliesContext,
bool forceNonPublicLink = false);
bool forceNonPublicLink = false,
std::optional<TimeId> videoTimestamp = {});
QString exportDirectStoryLink(not_null<Data::Story*> item);
void requestContacts();
+1 -1
View File
@@ -240,7 +240,7 @@ AyuGramSettings::AyuGramSettings() {
#endif
;
simpleQuotesAndReplies = true;
replaceBottomInfoWithIcons = false;
replaceBottomInfoWithIcons = true;
deletedMark = "🧹";
editedMark = Core::IsAppLaunched() ? tr::lng_edited(tr::now) : QString("edited");
recentStickersCount = 100;
@@ -44,6 +44,8 @@ void map(not_null<HistoryItem*> item, AyuMessageBase &message) {
message.fromId = item->from()->id.value & PeerId::kChatTypeMask;
if (item->topic()) {
message.topicId = item->topicRootId().bare;
} else {
message.topicId = 0;
}
message.messageId = item->id.bare;
message.date = item->date();
@@ -247,8 +247,8 @@ QImage addPadding(const QImage &original) {
}
QImage paddedImage(
original.width() + 2 * st::messageShotPadding,
original.height() + 2 * st::messageShotPadding,
original.width() + 2 * st::messageShotPadding * style::DevicePixelRatio(),
original.height() + 2 * st::messageShotPadding * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied
);
paddedImage.setDevicePixelRatio(style::DevicePixelRatio());
@@ -352,6 +352,7 @@ QImage Make(not_null<QWidget*> box, const ShotConfig &config) {
height += view->resizeGetHeight(width);
}
width *= style::DevicePixelRatio();
height *= style::DevicePixelRatio();
// create the image
@@ -9,6 +9,7 @@
using "ui/colors.palette";
using "ui/widgets/widgets.style";
using "dialogs/dialogs.style";
ayuGhostIcon: icon {{ "ayu/ghost", menuIconColor }};
ayuMenuIcon: icon {{ "ayu/ayu_menu", menuIconColor }};
@@ -40,3 +41,14 @@ editedIconPadding: margins(0px, 0px, 0px, 0px);
deletedIcon: icon {{ "ayu/trash_bin", windowFg }};
deletedIconPadding: margins(0px, 0px, 0px, 0px);
dialogsExteraOfficialIcon: ThreeStateIcon {
icon: icon {{ "ayu/dialogs_extera_official", dialogsVerifiedIconBg }};
over: icon {{ "ayu/dialogs_extera_official", dialogsVerifiedIconBgOver }};
active: icon {{ "ayu/dialogs_extera_official", dialogsVerifiedIconBgActive }};
}
dialogsExteraSupporterIcon: ThreeStateIcon {
icon: icon {{ "ayu/dialogs_extera_supporter", dialogsVerifiedIconBg }};
over: icon {{ "ayu/dialogs_extera_supporter", dialogsVerifiedIconBgOver }};
active: icon {{ "ayu/dialogs_extera_supporter", dialogsVerifiedIconBgActive }};
}
@@ -200,7 +200,7 @@ void MessageShotBox::setupContent() {
updatePreview();
const auto boxWidth = imageView->getImage().width() + (st::boxPadding.left() + st::boxPadding.right()) * 4;
const auto boxWidth = imageView->getImage().width() / style::DevicePixelRatio() + (st::boxPadding.left() + st::boxPadding.right()) * 4;
boxClosing() | rpl::start_with_next(
[=]
@@ -27,7 +27,8 @@ void ImageView::setImage(const QImage &image) {
this->prevImage = this->image;
this->image = image;
setMinimumSize(image.size().grownBy(st::imageViewInnerPadding));
const auto size = image.size() / style::DevicePixelRatio();
setMinimumSize(size.grownBy(st::imageViewInnerPadding));
if (this->animation.animating()) {
this->animation.stop();
@@ -67,7 +68,7 @@ void ImageView::paintEvent(QPaintEvent *e) {
const auto brush = QBrush(AyuFeatures::MessageShot::makeDefaultBackgroundColor());
QPainterPath path;
path.addRoundedRect(rect(), st::bubbleRadiusLarge, st::bubbleRadiusLarge);
path.addRoundedRect(rect(), st::roundRadiusLarge, st::roundRadiusLarge);
p.fillPath(path, brush);
@@ -75,10 +76,10 @@ void ImageView::paintEvent(QPaintEvent *e) {
const auto realRect = rect().marginsRemoved(st::imageViewInnerPadding);
const auto resizedRect = QRect(
(realRect.width() - prevImage.width()) / 2 + st::imageViewInnerPadding.left(),
(realRect.height() - prevImage.height()) / 2 + st::imageViewInnerPadding.top(),
prevImage.width(),
prevImage.height());
(realRect.width() - prevImage.width() / style::DevicePixelRatio()) / 2 + st::imageViewInnerPadding.left(),
(realRect.height() - prevImage.height() / style::DevicePixelRatio()) / 2 + st::imageViewInnerPadding.top(),
prevImage.width() / style::DevicePixelRatio(),
prevImage.height() / style::DevicePixelRatio());
const auto opacity = 1.0 - animation.value(1.0);
p.setOpacity(opacity);
@@ -90,10 +91,10 @@ void ImageView::paintEvent(QPaintEvent *e) {
const auto realRect = rect().marginsRemoved(st::imageViewInnerPadding);
const auto resizedRect = QRect(
(realRect.width() - image.width()) / 2 + st::imageViewInnerPadding.left(),
(realRect.height() - image.height()) / 2 + st::imageViewInnerPadding.top(),
image.width(),
image.height());
(realRect.width() - image.width() / style::DevicePixelRatio()) / 2 + st::imageViewInnerPadding.left(),
(realRect.height() - image.height() / style::DevicePixelRatio()) / 2 + st::imageViewInnerPadding.top(),
image.width() / style::DevicePixelRatio(),
image.height() / style::DevicePixelRatio());
const auto opacity = animation.value(1.0);
p.setOpacity(opacity);
@@ -702,7 +702,7 @@ void InnerWidget::addMessages(Direction direction, const std::vector<AyuMessageB
? message.fakeId // viewing edited history
: message.messageId; // viewing deleted messages
if (_messageIds.find(id) != _messageIds.end()) {
return;
continue;
}
auto count = 0;
const auto addOne = [&](
@@ -48,6 +48,13 @@
class PainterHighQualityEnabler;
rpl::producer<QString> asBeta(rpl::producer<QString> text) {
return std::move(text) | rpl::map([=](const QString &val)
{
return val + " β";
});
}
not_null<Ui::RpWidget*> AddInnerToggle(not_null<Ui::VerticalLayout*> container,
const style::SettingsButton &st,
std::vector<not_null<Ui::AbstractCheckView*>> innerCheckViews,
@@ -1277,7 +1284,7 @@ void SetupRecentStickersLimitSlider(not_null<Ui::VerticalLayout*> container) {
updateLabel(settings->recentStickersCount);
slider->setPseudoDiscrete(
100 + 1,
200 + 1,
// thx tg
[=](int amount)
{
@@ -1461,10 +1468,7 @@ void SetupMarks(not_null<Ui::VerticalLayout*> container) {
AddButtonWithIcon(
container,
tr::ayu_ReplaceMarksWithIcons() | rpl::map([=](QString val)
{
return val + " β";
}),
tr::ayu_ReplaceMarksWithIcons(),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->replaceBottomInfoWithIcons)
@@ -36,6 +36,7 @@
#include "ayu/ayu_settings.h"
#include "ayu/ayu_state.h"
#include "ayu/data/messages_storage.h"
Main::Session *getSession(ID userId) {
for (const auto &[index, account] : Core::App().domain().accounts()) {
@@ -85,6 +86,15 @@ bool isSupporterPeer(ID peerId) {
return RCManager::getInstance().supporters().contains(peerId);
}
rpl::producer<Info::Profile::Badge::Content> ExteraBadgeTypeFromPeer(not_null<PeerData*> peer) {
if (isExteraPeer(getBareID(peer))) {
return rpl::single(Info::Profile::Badge::Content{Info::Profile::BadgeType::Extera });
} else if (isSupporterPeer(getBareID(peer))) {
return rpl::single(Info::Profile::Badge::Content{Info::Profile::BadgeType::ExteraSupporter });
}
return rpl::single(Info::Profile::Badge::Content{Info::Profile::BadgeType::None });
}
bool isMessageHidden(const not_null<HistoryItem*> item) {
if (AyuState::isHidden(item)) {
return true;
@@ -494,6 +504,28 @@ int getScheduleTime(int64 sumSize) {
return time;
}
bool isMessageSavable(const not_null<HistoryItem *> item) {
const auto settings = &AyuSettings::getInstance();
if (!settings->saveDeletedMessages) {
return false;
}
if (const auto possiblyBot = item->history()->peer->asUser()) {
return !possiblyBot->isBot() || (settings->saveForBots && possiblyBot->isBot());
}
return true;
}
void processMessageDelete(not_null<HistoryItem*> item) {
if (!isMessageSavable(item)) {
item->destroy();
} else {
item->setDeleted();
AyuMessages::addDeletedMessage(item);
}
}
void resolveUser(ID userId, const QString &username, Main::Session *session, const Callback &callback) {
auto normalized = username.trimmed().toLower();
if (normalized.isEmpty()) {
@@ -10,6 +10,7 @@
#include "core/application.h"
#include "dialogs/dialogs_main_list.h"
#include "info/profile/info_profile_badge.h"
#include "main/main_domain.h"
using Callback = Fn<void(const QString &, UserData *)>;
@@ -23,6 +24,8 @@ ID getBareID(not_null<PeerData*> peer);
bool isExteraPeer(ID peerId);
bool isSupporterPeer(ID peerId);
rpl::producer<Info::Profile::Badge::Content> ExteraBadgeTypeFromPeer(not_null<PeerData*> peer);
bool isMessageHidden(not_null<HistoryItem*> item);
void MarkAsReadChatList(not_null<Dialogs::MainList*> list);
@@ -45,6 +48,9 @@ QString getPeerDC(not_null<PeerData*> peer);
int getScheduleTime(int64 sumSize);
bool isMessageSavable(not_null<HistoryItem *> item);
void processMessageDelete(not_null<HistoryItem *> item);
void searchById(ID userId, Main::Session *session, bool retry, const Callback &callback);
void searchById(ID userId, Main::Session *session, const Callback &callback);
+23 -74
View File
@@ -25,17 +25,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
#include "window/window_controller.h"
#include "window/window_session_controller.h"
#include "window/window_session_controller_link_info.h"
namespace {
rpl::producer<TextWithEntities> Text1() {
return tr::lng_about_text1(
lt_api_link,
tr::lng_about_text1_api(
) | Ui::Text::ToLink("https://core.telegram.org/api"),
Ui::Text::WithEntities);
}
rpl::producer<TextWithEntities> Text2() {
rpl::producer<TextWithEntities> Text() {
return tr::lng_about_text2(
lt_gpl_link,
rpl::single(Ui::Text::Link(
@@ -48,34 +45,33 @@ rpl::producer<TextWithEntities> Text2() {
Ui::Text::WithEntities);
}
rpl::producer<TextWithEntities> Text3() {
return tr::lng_about_text3(
lt_faq_link,
tr::lng_about_text3_faq() | Ui::Text::ToLink(telegramFaqLink()),
Ui::Text::WithEntities);
}
} // namespace
AboutBox::AboutBox(QWidget *parent)
AboutBox::AboutBox(QWidget *parent, Window::SessionController* controller)
: _version(this, tr::lng_about_version(tr::now, lt_version, currentVersionText()), st::aboutVersionLink)
, _text1(this, Text1(), st::aboutLabel)
, _text2(this, Text2(), st::aboutLabel)
, _text3(this, Text3(), st::aboutLabel) {
, _text(this, Text(), st::aboutLabel)
, _controller(controller) {
}
void AboutBox::prepare() {
setTitle(rpl::single(u"AyuGram Desktop"_q));
addButton(tr::lng_close(), [this] { closeBox(); });
addLeftButton(
rpl::single(QString("@ayugramchat")),
[this, controller = _controller]
{
closeBox();
controller->showPeerByLink(Window::PeerByLinkInfo{
.usernameOrId = QString("ayugramchat"),
});
});
_text1->setLinksTrusted();
_text2->setLinksTrusted();
_text3->setLinksTrusted();
_text->setLinksTrusted();
_version->setClickedCallback([this] { showVersionHistory(); });
setDimensions(st::aboutWidth, st::aboutTextTop + _text1->height() + st::aboutSkip + _text2->height() + st::aboutSkip + _text3->height());
setDimensions(st::aboutWidth, st::aboutTextTop + _text->height());
}
void AboutBox::resizeEvent(QResizeEvent *e) {
@@ -85,42 +81,12 @@ void AboutBox::resizeEvent(QResizeEvent *e) {
- st::boxPadding.left()
- st::boxPadding.right();
_version->moveToLeft(st::boxPadding.left(), st::aboutVersionTop);
_text1->resizeToWidth(available);
_text1->moveToLeft(st::boxPadding.left(), st::aboutTextTop);
_text2->resizeToWidth(available);
_text2->moveToLeft(st::boxPadding.left(), _text1->y() + _text1->height() + st::aboutSkip);
_text3->resizeToWidth(available);
_text3->moveToLeft(st::boxPadding.left(), _text2->y() + _text2->height() + st::aboutSkip);
_text->resizeToWidth(available);
_text->moveToLeft(st::boxPadding.left(), st::aboutTextTop);
}
void AboutBox::showVersionHistory() {
if (cRealAlphaVersion()) {
auto url = u"https://tdesktop.com/"_q;
if (Platform::IsWindows32Bit()) {
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()) {
url += u"linux/%1.tar.xz"_q;
} else {
Unexpected("Platform value.");
}
url = url.arg(u"talpha%1_%2"_q.arg(cRealAlphaVersion()).arg(Core::countAlphaVersionSignature(cRealAlphaVersion())));
QGuiApplication::clipboard()->setText(url);
getDelegate()->show(
Ui::MakeInformBox(
"The link to the current private alpha "
"version of Telegram Desktop was copied to the clipboard."),
Ui::LayerOption::CloseOther);
} else {
File::OpenUrl(Core::App().changelogLink());
}
File::OpenUrl(Core::App().changelogLink());
}
void AboutBox::keyPressEvent(QKeyEvent *e) {
@@ -131,23 +97,6 @@ void AboutBox::keyPressEvent(QKeyEvent *e) {
}
}
QString telegramFaqLink() {
const auto result = u"https://telegram.org/faq"_q;
const auto langpacked = [&](const char *language) {
return result + '/' + language;
};
const auto current = Lang::Id();
for (const auto language : { "de", "es", "it", "ko" }) {
if (current.startsWith(QLatin1String(language))) {
return langpacked(language);
}
}
if (current.startsWith(u"pt-br"_q)) {
return langpacked("br");
}
return result;
}
QString currentVersionText() {
auto result = QString::fromLatin1(AppVersionStr);
if (cAlphaVersion()) {
+7 -4
View File
@@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/layers/box_content.h"
namespace Window {
class SessionController;
}
namespace Ui {
class LinkButton;
class FlatLabel;
@@ -16,7 +20,7 @@ class FlatLabel;
class AboutBox : public Ui::BoxContent {
public:
AboutBox(QWidget*);
AboutBox(QWidget*, Window::SessionController* controller);
protected:
void prepare() override;
@@ -28,9 +32,8 @@ private:
void showVersionHistory();
object_ptr<Ui::LinkButton> _version;
object_ptr<Ui::FlatLabel> _text1;
object_ptr<Ui::FlatLabel> _text2;
object_ptr<Ui::FlatLabel> _text3;
object_ptr<Ui::FlatLabel> _text;
Window::SessionController* _controller;
};
@@ -467,13 +467,16 @@ void EditCaptionBox::rebuildPreview() {
}
} else {
const auto &file = _preparedList.files.front();
const auto isVideoFile = file.isVideoFile();
const auto media = Ui::SingleMediaPreview::Create(
this,
st::defaultComposeControls,
gifPaused,
file,
[] { return true; },
[=](Ui::AttachActionType type) {
return (type != Ui::AttachActionType::EditCover)
|| isVideoFile;
},
Ui::AttachControls::Type::EditOnly);
_isPhoto = (media && media->isPhoto());
const auto withCheckbox = _isPhoto && CanBeCompressed(_albumType);
@@ -719,7 +722,7 @@ void EditCaptionBox::setupPhotoEditorEventHandler() {
controller->uiShow(),
&_preparedList.files.front(),
st::sendMediaPreviewSize,
[=] { rebuildPreview(); });
[=](bool ok) { if (ok) rebuildPreview(); });
} else {
EditPhotoImage(_controller, _photoMedia, hasSpoiler(), [=](
Ui::PreparedList &&list) {
File diff suppressed because it is too large Load Diff
+26 -6
View File
@@ -13,6 +13,10 @@ namespace Api {
struct GiftCode;
} // namespace Api
namespace ChatHelpers {
class Show;
} // namespace ChatHelpers
namespace Data {
struct Boost;
struct CreditsHistoryEntry;
@@ -21,6 +25,14 @@ struct GiveawayResults;
struct SubscriptionEntry;
} // namespace Data
namespace Main {
class Session;
} // namespace Main
namespace Settings {
struct CreditsEntryBoxStyleOverrides;
} // namespace Settings
namespace Ui {
class GenericBox;
class VerticalLayout;
@@ -54,29 +66,37 @@ void ResolveGiveawayInfo(
std::optional<Data::GiveawayStart> start,
std::optional<Data::GiveawayResults> results);
[[nodiscard]] QString TonAddressUrl(
not_null<Main::Session*> session,
const QString &address);
void AddStarGiftTable(
not_null<Window::SessionNavigation*> controller,
std::shared_ptr<ChatHelpers::Show> show,
not_null<Ui::VerticalLayout*> container,
Settings::CreditsEntryBoxStyleOverrides st,
const Data::CreditsHistoryEntry &entry,
Fn<void(bool)> toggleVisibility,
Fn<void()> convertToStars,
Fn<void()> startUpgrade);
void AddCreditsHistoryEntryTable(
not_null<Window::SessionNavigation*> controller,
std::shared_ptr<ChatHelpers::Show> show,
not_null<Ui::VerticalLayout*> container,
Settings::CreditsEntryBoxStyleOverrides st,
const Data::CreditsHistoryEntry &entry);
void AddSubscriptionEntryTable(
not_null<Window::SessionNavigation*> controller,
std::shared_ptr<ChatHelpers::Show> show,
not_null<Ui::VerticalLayout*> container,
Settings::CreditsEntryBoxStyleOverrides st,
const Data::SubscriptionEntry &s);
void AddSubscriberEntryTable(
not_null<Window::SessionNavigation*> controller,
std::shared_ptr<ChatHelpers::Show> show,
not_null<Ui::VerticalLayout*> container,
Settings::CreditsEntryBoxStyleOverrides st,
not_null<PeerData*> peer,
TimeId date);
void AddCreditsBoostTable(
not_null<Window::SessionNavigation*> controller,
std::shared_ptr<ChatHelpers::Show> show,
not_null<Ui::VerticalLayout*> container,
Settings::CreditsEntryBoxStyleOverrides st,
const Data::Boost &boost);
@@ -72,6 +72,16 @@ ModerateOptions CalculateModerateOptions(const HistoryItemsList &items) {
if (peer != item->history()->peer) {
return {};
}
{
const auto author = item->author();
if (author == peer) {
return {};
} else if (const auto channel = author->asChannel()) {
if (channel->linkedChat() == peer) {
return {};
}
}
}
if (!item->suggestBanReport()) {
result.allCanBan = false;
}
+17 -1
View File
@@ -39,6 +39,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <xxhash.h> // XXH64.
#include <QtWidgets/QApplication>
// AyuGram includes
#include "styles/style_ayu_icons.h"
[[nodiscard]] PeerListRowId UniqueRowIdFromString(const QString &d) {
return XXH64(d.data(), d.size() * sizeof(ushort), 0);
}
@@ -800,6 +804,12 @@ int PeerListRow::paintNameIconGetWidth(
.verified = &(selected
? st::dialogsVerifiedIconOver
: st::dialogsVerifiedIcon),
.exteraOfficial = &(selected
? st::dialogsExteraOfficialIcon.over
: st::dialogsExteraOfficialIcon.icon),
.exteraSupporter = &(selected
? st::dialogsExteraSupporterIcon.over
: st::dialogsExteraSupporterIcon.icon),
.premium = &(selected
? st::dialogsPremiumIcon.over
: st::dialogsPremiumIcon.icon),
@@ -912,7 +922,13 @@ void PeerListRow::paintDisabledCheckUserpic(
p.setPen(userpicBorderPen);
p.setBrush(Qt::NoBrush);
p.drawEllipse(userpicEllipse);
if (peer()->forum()) {
const auto radius = userpicDiameter
* Ui::ForumUserpicRadiusMultiplier();
p.drawRoundedRect(userpicEllipse, radius, radius);
} else {
p.drawEllipse(userpicEllipse);
}
p.setPen(iconBorderPen);
p.setBrush(st.disabledCheckFg);
@@ -265,7 +265,7 @@ struct IconSelector {
const auto manager = &controller->session().data().customEmojiManager();
auto factory = [=](DocumentId id, Fn<void()> repaint)
-> std::unique_ptr<Ui::Text::CustomEmoji> {
-> std::unique_ptr<Ui::Text::CustomEmoji> {
const auto tag = Data::CustomEmojiManager::SizeTag::Large;
if (id == kDefaultIconId) {
return std::make_unique<DefaultIconEmoji>(
@@ -288,7 +288,7 @@ struct IconSelector {
.show = controller->uiShow(),
.mode = EmojiListWidget::Mode::TopicIcon,
.paused = Window::PausedIn(controller, PauseReason::Layer),
.customRecentList = recent(),
.customRecentList = DocumentListToRecent(recent()),
.customRecentFactory = std::move(factory),
.st = &st::reactPanelEmojiPan,
}),
@@ -297,7 +297,7 @@ struct IconSelector {
icons->requestDefaultIfUnknown();
icons->defaultUpdates(
) | rpl::start_with_next([=] {
selector->provideRecent(recent());
selector->provideRecent(DocumentListToRecent(recent()));
}, selector->lifetime());
placeFooter(selector->createFooter());
@@ -313,9 +313,11 @@ PreviewWrap::PreviewWrap(
WebPageCollage(),
nullptr, // iv
nullptr, // stickerSet
nullptr, // uniqueGift
0, // duration
QString(), // author
false, // hasLargeMedia
false, // photoIsVideoCover
0)) // pendingTill
, _theme(theme)
, _style(style)
@@ -525,7 +527,7 @@ void LevelBadge::paintEvent(QPaintEvent *e) {
struct SetValues {
uint8 colorIndex = 0;
DocumentId backgroundEmojiId = 0;
DocumentId statusId = 0;
EmojiStatusId statusId;
TimeId statusUntil = 0;
bool statusChanged = false;
};
@@ -807,7 +809,7 @@ int ColorSelector::resizeGetHeight(int newWidth) {
const auto state = right->lifetime().make_state<State>();
state->panel.someCustomChosen(
) | rpl::start_with_next([=](EmojiStatusPanel::CustomChosen chosen) {
emojiIdChosen(chosen.id);
emojiIdChosen(chosen.id.documentId);
}, raw->lifetime());
std::move(colorIndexValue) | rpl::start_with_next([=](uint8 index) {
@@ -871,13 +873,12 @@ int ColorSelector::resizeGetHeight(int newWidth) {
const auto customTextColor = [=] {
return style->coloredValues(false, state->index).name;
};
const auto controller = show->resolveWindow(
ChatHelpers::WindowUsage::PremiumPromo);
const auto controller = show->resolveWindow();
if (controller) {
state->panel.show({
.controller = controller,
.button = right,
.ensureAddedEmojiId = state->emojiId,
.ensureAddedEmojiId = { state->emojiId },
.customTextColor = customTextColor,
.backgroundEmojiMode = true,
});
@@ -901,8 +902,8 @@ int ColorSelector::resizeGetHeight(int newWidth) {
not_null<Ui::RpWidget*> parent,
std::shared_ptr<ChatHelpers::Show> show,
not_null<ChannelData*> channel,
rpl::producer<DocumentId> statusIdValue,
Fn<void(DocumentId,TimeId)> statusIdChosen,
rpl::producer<EmojiStatusId> statusIdValue,
Fn<void(EmojiStatusId,TimeId)> statusIdChosen,
bool group) {
const auto button = ButtonStyleWithRightEmoji(
parent,
@@ -924,20 +925,20 @@ int ColorSelector::resizeGetHeight(int newWidth) {
struct State {
EmojiStatusPanel panel;
std::unique_ptr<Ui::Text::CustomEmoji> emoji;
DocumentId statusId = 0;
EmojiStatusId statusId;
};
const auto state = right->lifetime().make_state<State>();
state->panel.someCustomChosen(
) | rpl::start_with_next([=](EmojiStatusPanel::CustomChosen chosen) {
statusIdChosen(chosen.id, chosen.until);
statusIdChosen({ chosen.id }, chosen.until);
}, raw->lifetime());
const auto session = &show->session();
std::move(statusIdValue) | rpl::start_with_next([=](DocumentId id) {
std::move(statusIdValue) | rpl::start_with_next([=](EmojiStatusId id) {
state->statusId = id;
state->emoji = id
? session->data().customEmojiManager().create(
id,
Data::EmojiStatusCustomId(id),
[=] { right->update(); })
: nullptr;
right->resize(
@@ -985,13 +986,12 @@ int ColorSelector::resizeGetHeight(int newWidth) {
}, right->lifetime());
raw->setClickedCallback([=] {
const auto controller = show->resolveWindow(
ChatHelpers::WindowUsage::PremiumPromo);
const auto controller = show->resolveWindow();
if (controller) {
state->panel.show({
.controller = controller,
.button = right,
.ensureAddedEmojiId = state->statusId,
.ensureAddedEmojiId = { state->statusId },
.channelStatusMode = true,
});
}
@@ -1183,7 +1183,7 @@ void EditPeerColorBox(
struct State {
rpl::variable<uint8> index;
rpl::variable<DocumentId> emojiId;
rpl::variable<DocumentId> statusId;
rpl::variable<EmojiStatusId> statusId;
TimeId statusUntil = 0;
bool statusChanged = false;
bool changing = false;
@@ -1263,8 +1263,7 @@ void EditPeerColorBox(
{ &st::menuBlueIconWallpaper }
);
button->setClickedCallback([=] {
const auto usage = ChatHelpers::WindowUsage::PremiumPromo;
if (const auto strong = show->resolveWindow(usage)) {
if (const auto strong = show->resolveWindow()) {
show->show(Box<BackgroundBox>(strong, channel));
}
});
@@ -1321,7 +1320,7 @@ void EditPeerColorBox(
show,
channel,
state->statusId.value(),
[=](DocumentId id, TimeId until) {
[=](EmojiStatusId id, TimeId until) {
state->statusId = id;
state->statusUntil = until;
state->statusChanged = true;
@@ -1471,8 +1470,7 @@ void CheckBoostLevel(
return;
}
const auto openStatistics = [=] {
if (const auto controller = show->resolveWindow(
ChatHelpers::WindowUsage::PremiumPromo)) {
if (const auto controller = show->resolveWindow()) {
controller->showSection(Info::Boosts::Make(peer));
}
};
@@ -1242,7 +1242,9 @@ void Controller::fillManageSection() {
? channel->canViewMembers()
: chat->amIn();
const auto canViewKicked = isChannel
&& (channel->isBroadcast() || channel->isGigagroup());
&& (channel->isMegagroup()
? (channel->isBroadcast() || channel->isGigagroup())
: true);
const auto hasRecentActions = isChannel
&& (channel->hasAdminRights() || channel->amCreator());
const auto hasStarRef = Info::BotStarRef::Join::Allowed(_peer)
@@ -1019,7 +1019,8 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
Ui::AddSkip(content);
Ui::AddSkip(content);
AddSubscriberEntryTable(controller, content, row->peer(), data.date);
const auto show = controller->uiShow();
AddSubscriberEntryTable(show, content, {}, row->peer(), data.date);
Ui::AddSkip(content);
Ui::AddSkip(content);
@@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "window/window_controller.h"
#include "main/main_session.h"
#include "mtproto/mtproto_config.h" // megagroupSizeMax
#include "apiwrap.h"
#include "settings/settings_common.h"
#include "styles/style_layers.h"
@@ -49,7 +50,6 @@ namespace {
constexpr auto kSlowmodeValues = 7;
constexpr auto kBoostsUnrestrictValues = 5;
constexpr auto kSuggestGigagroupThreshold = 199000;
constexpr auto kForceDisableTooltipDuration = 3 * crl::time(1000);
[[nodiscard]] auto Dependencies(PowerSaving::Flags)
@@ -1191,8 +1191,11 @@ void ShowEditPeerPermissionsBox(
});
if (const auto channel = peer->asChannel()) {
constexpr auto kThresholdOffset = int(1000);
const auto threshold = -kThresholdOffset
+ channel->session().serverConfig().megagroupSizeMax;
if (channel->amCreator()
&& channel->membersCount() >= kSuggestGigagroupThreshold) {
&& channel->membersCount() >= threshold) {
AddSuggestGigagroup(
inner,
AboutGigagroupCallback(
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "base/event_filter.h"
#include "chat_helpers/emoji_list_widget.h"
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "core/ui_integration.h"
@@ -491,7 +492,8 @@ object_ptr<Ui::RpWidget> AddReactionsSelector(
panelList.erase(
ranges::remove(panelList, paid->selectAnimation->id),
end(panelList));
panel->selector()->provideRecentEmoji(panelList);
panel->selector()->provideRecentEmoji(
ChatHelpers::DocumentListToRecent(panelList));
panel->setDesiredHeightValues(
1.,
st::emojiPanMinHeight / 2,
@@ -106,6 +106,8 @@ PeerShortInfoCover::PeerShortInfoCover(
, _statusStyle(std::make_unique<CustomLabelStyle>(_st.status))
, _status(_widget.get(), std::move(status), _statusStyle->st)
, _roundMask(Images::CornersMask(_st.radius))
, _roundMaskRetina(
Images::CornersMask(_st.radius / style::DevicePixelRatio()))
, _videoPaused(std::move(videoPaused)) {
_widget->setCursor(_cursor);
@@ -190,7 +192,7 @@ void PeerShortInfoCover::paint(QPainter &p) {
if (!frame.isNull()) {
frame = Images::Round(
std::move(frame),
_roundMask,
_roundMaskRetina,
RectPart::TopLeft | RectPart::TopRight);
} else if (_userpicImage.isNull()) {
auto image = QImage(
@@ -226,10 +228,11 @@ void PeerShortInfoCover::paintCoverImage(QPainter &p, const QImage &image) {
const auto top = _widget->height() - fill;
const auto factor = style::DevicePixelRatio();
if (fill > 0) {
const auto t = roundedHeight + _scrollTop;
p.drawImage(
QRect(0, top, roundedWidth, fill),
QRect(0, t, roundedWidth * factor, (roundedWidth - t) * factor),
image,
QRect(0, top * factor, roundedWidth * factor, fill * factor));
QRect(0, t, roundedWidth * factor, (roundedWidth - t) * factor));
}
if (covered <= 0) {
return;
@@ -238,9 +241,9 @@ void PeerShortInfoCover::paintCoverImage(QPainter &p, const QImage &image) {
const auto from = top - rounded;
auto q = QPainter(&_roundedTopImage);
q.drawImage(
QRect(0, 0, roundedWidth, rounded),
QRect(0, 0, roundedWidth * factor, rounded * factor),
image,
QRect(0, from * factor, roundedWidth * factor, rounded * factor));
QRect(0, _scrollTop, roundedWidth * factor, rounded * factor));
q.end();
_roundedTopImage = Images::Round(
std::move(_roundedTopImage),
@@ -123,6 +123,7 @@ private:
object_ptr<Ui::FlatLabel> _additionalStatus = { nullptr };
std::array<QImage, 4> _roundMask;
std::array<QImage, 4> _roundMaskRetina;
QImage _userpicImage;
QImage _roundedTopImage;
QImage _barSmall;
@@ -486,20 +486,23 @@ object_ptr<Ui::BoxContent> PrepareShortInfoBox(
object_ptr<Ui::BoxContent> PrepareShortInfoBox(
not_null<PeerData*> peer,
not_null<Window::SessionNavigation*> navigation,
std::shared_ptr<ChatHelpers::Show> show,
const style::ShortInfoBox *stOverride) {
const auto open = [=] { navigation->showPeerHistory(peer); };
const auto open = [=] {
if (const auto window = show->resolveWindow()) {
window->showPeerHistory(peer);
}
};
const auto videoIsPaused = [=] {
return navigation->parentController()->isGifPausedAtLeastFor(
Window::GifPauseReason::Layer);
return show->paused(Window::GifPauseReason::Layer);
};
auto menuFiller = [=](Ui::Menu::MenuCallback addAction) {
const auto controller = navigation->parentController();
const auto peerSeparateId = Window::SeparateId(peer);
if (controller->windowId() != peerSeparateId) {
const auto window = show->resolveWindow();
if (window && window->windowId() != peerSeparateId) {
addAction(tr::lng_context_new_window(tr::now), [=] {
Ui::PreventDelayedActivation();
controller->showInNewWindow(peer);
window->showInNewWindow(peer);
}, &st::menuIconNewWindow);
}
};
@@ -511,6 +514,13 @@ object_ptr<Ui::BoxContent> PrepareShortInfoBox(
stOverride);
}
object_ptr<Ui::BoxContent> PrepareShortInfoBox(
not_null<PeerData*> peer,
not_null<Window::SessionNavigation*> navigation,
const style::ShortInfoBox *stOverride) {
return PrepareShortInfoBox(peer, navigation->uiShow(), stOverride);
}
rpl::producer<QString> PrepareShortInfoStatus(not_null<PeerData*> peer) {
return StatusValue(peer);
}
@@ -16,6 +16,10 @@ struct ShortInfoCover;
struct ShortInfoBox;
} // namespace style
namespace ChatHelpers {
class Show;
} // namespace ChatHelpers
namespace Ui::Menu {
struct MenuCallback;
} // namespace Ui::Menu
@@ -42,6 +46,11 @@ struct PreparedShortInfoUserpic {
Fn<void(Ui::Menu::MenuCallback)> menuFiller,
const style::ShortInfoBox *stOverride = nullptr);
[[nodiscard]] object_ptr<Ui::BoxContent> PrepareShortInfoBox(
not_null<PeerData*> peer,
std::shared_ptr<ChatHelpers::Show> show,
const style::ShortInfoBox *stOverride = nullptr);
[[nodiscard]] object_ptr<Ui::BoxContent> PrepareShortInfoBox(
not_null<PeerData*> peer,
not_null<Window::SessionNavigation*> navigation,
@@ -1139,8 +1139,7 @@ void PreviewBox(
button->resizeToWidth(width);
if (!descriptor.fromSettings) {
button->setClickedCallback([=] {
const auto window = show->resolveWindow(
ChatHelpers::WindowUsage::PremiumPromo);
const auto window = show->resolveWindow();
if (!window) {
return;
}
@@ -1527,6 +1526,18 @@ void DoubledLimitsPreviewBox(
Main::Domain::kPremiumMaxAccounts,
till,
});
{
const auto premium = limits.similarChannelsPremium();
entries.push_back({
tr::lng_premium_double_limits_subtitle_similar_channels(),
tr::lng_premium_double_limits_about_similar_channels(
lt_count,
rpl::single(float64(premium)),
Ui::Text::RichLangValue),
limits.similarChannelsDefault(),
premium,
});
}
Ui::Premium::ShowListBox(
box,
st::defaultPremiumLimits,
+122 -5
View File
@@ -250,7 +250,7 @@ SendFilesBox::Block::Block(
int till,
Fn<bool()> gifPaused,
SendFilesWay way,
Fn<bool()> canToggleSpoiler)
Fn<bool(const Ui::PreparedFile &, Ui::AttachActionType)> actionAllowed)
: _items(items)
, _from(from)
, _till(till) {
@@ -268,7 +268,9 @@ SendFilesBox::Block::Block(
st,
my,
way,
std::move(canToggleSpoiler));
[=](int index, Ui::AttachActionType type) {
return actionAllowed((*_items)[from + index], type);
});
_preview.reset(preview);
} else {
const auto media = Ui::SingleMediaPreview::Create(
@@ -276,7 +278,9 @@ SendFilesBox::Block::Block(
st,
gifPaused,
first,
std::move(canToggleSpoiler));
[=](Ui::AttachActionType type) {
return actionAllowed((*_items)[from], type);
});
if (media) {
_isSingleMedia = true;
_preview.reset(media);
@@ -352,6 +356,38 @@ rpl::producer<int> SendFilesBox::Block::itemModifyRequest() const {
}
}
rpl::producer<int> SendFilesBox::Block::itemEditCoverRequest() const {
using namespace rpl::mappers;
const auto preview = _preview.get();
const auto from = _from;
if (_isAlbum) {
const auto album = static_cast<Ui::AlbumPreview*>(preview);
return album->thumbEditCoverRequested() | rpl::map(_1 + from);
} else if (_isSingleMedia) {
const auto media = static_cast<Ui::SingleMediaPreview*>(preview);
return media->editCoverRequests() | rpl::map_to(from);
} else {
return rpl::never<int>();
}
}
rpl::producer<int> SendFilesBox::Block::itemClearCoverRequest() const {
using namespace rpl::mappers;
const auto preview = _preview.get();
const auto from = _from;
if (_isAlbum) {
const auto album = static_cast<Ui::AlbumPreview*>(preview);
return album->thumbClearCoverRequested() | rpl::map(_1 + from);
} else if (_isSingleMedia) {
const auto media = static_cast<Ui::SingleMediaPreview*>(preview);
return media->clearCoverRequests() | rpl::map_to(from);
} else {
return rpl::never<int>();
}
}
rpl::producer<> SendFilesBox::Block::orderUpdated() const {
if (_isAlbum) {
const auto album = static_cast<Ui::AlbumPreview*>(_preview.get());
@@ -1046,7 +1082,16 @@ void SendFilesBox::pushBlock(int from, int till) {
till,
gifPaused,
_sendWay.current(),
[=] { return !hasPrice(); });
[=](const Ui::PreparedFile &file, Ui::AttachActionType type) {
return (type == Ui::AttachActionType::ToggleSpoiler)
? !hasPrice()
: (type == Ui::AttachActionType::EditCover)
? (file.isVideoFile()
&& _captionToPeer
&& (_captionToPeer->isBroadcast()
|| _captionToPeer->isSelf()))
: (file.videoCover != nullptr);
});
auto &block = _blocks.back();
const auto widget = _inner->add(
block.takeWidget(),
@@ -1167,7 +1212,79 @@ void SendFilesBox::pushBlock(int from, int till) {
show,
&_list.files[index],
st::sendMediaPreviewSize,
[=] { refreshAllAfterChanges(from); });
[=](bool ok) { if (ok) refreshAllAfterChanges(from); });
}, widget->lifetime());
block.itemEditCoverRequest(
) | rpl::start_with_next([=, show = _show](int index) {
applyBlockChanges();
const auto replace = [=](Ui::PreparedList list) {
if (list.files.empty()) {
return;
}
auto &entry = _list.files[index];
const auto video = entry.information
? std::get_if<Ui::PreparedFileInformation::Video>(
&entry.information->media)
: nullptr;
if (!video) {
return;
}
auto old = std::shared_ptr<Ui::PreparedFile>(
std::move(entry.videoCover));
entry.videoCover = std::make_unique<Ui::PreparedFile>(
std::move(list.files.front()));
Editor::OpenWithPreparedFile(
this,
show,
entry.videoCover.get(),
st::sendMediaPreviewSize,
crl::guard(this, [=](bool ok) {
if (!ok) {
_list.files[index].videoCover = old
? std::make_unique<Ui::PreparedFile>(
std::move(*old))
: nullptr;
}
refreshAllAfterChanges(from);
}),
video->thumbnail.size());
};
const auto checkResult = [=](const Ui::PreparedList &list) {
if (list.files.empty()) {
return true;
}
if (list.files.front().type != Ui::PreparedFile::Type::Photo) {
show->showToast(tr::lng_choose_cover_bad(tr::now));
return false;
}
return true;
};
const auto callback = [=](FileDialog::OpenResult &&result) {
const auto premium = _show->session().premium();
FileDialogCallback(
std::move(result),
checkResult,
replace,
premium,
show);
};
FileDialog::GetOpenPath(
this,
tr::lng_choose_cover(tr::now),
FileDialog::ImagesFilter(),
crl::guard(this, callback));
}, widget->lifetime());
block.itemClearCoverRequest(
) | rpl::start_with_next([=](int index) {
applyBlockChanges();
refreshAllAfterChanges(from, [&] {
auto &entry = _list.files[index];
entry.videoCover = nullptr;
});
}, widget->lifetime());
block.orderUpdated() | rpl::start_with_next([=]{
+5 -1
View File
@@ -153,7 +153,9 @@ private:
int till,
Fn<bool()> gifPaused,
Ui::SendFilesWay way,
Fn<bool()> canToggleSpoiler);
Fn<bool(
const Ui::PreparedFile &,
Ui::AttachActionType)> actionAllowed);
Block(Block &&other) = default;
Block &operator=(Block &&other) = default;
@@ -164,6 +166,8 @@ private:
[[nodiscard]] rpl::producer<int> itemDeleteRequest() const;
[[nodiscard]] rpl::producer<int> itemReplaceRequest() const;
[[nodiscard]] rpl::producer<int> itemModifyRequest() const;
[[nodiscard]] rpl::producer<int> itemEditCoverRequest() const;
[[nodiscard]] rpl::producer<int> itemClearCoverRequest() const;
[[nodiscard]] rpl::producer<> orderUpdated() const;
void setSendWay(Ui::SendFilesWay way);
+62 -16
View File
@@ -53,6 +53,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "styles/style_calls.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_menu_icons.h"
@@ -202,16 +203,16 @@ ShareBox::ShareBox(QWidget*, Descriptor &&descriptor)
, _api(&_descriptor.session->mtp())
, _select(
this,
(_descriptor.stMultiSelect
? *_descriptor.stMultiSelect
(_descriptor.st.multiSelect
? *_descriptor.st.multiSelect
: st::defaultMultiSelect),
tr::lng_participant_filter())
, _comment(
this,
object_ptr<Ui::InputField>(
this,
(_descriptor.stComment
? *_descriptor.stComment
(_descriptor.st.comment
? *_descriptor.st.comment
: st::shareComment),
Ui::InputField::Mode::MultiLine,
tr::lng_photos_comment()),
@@ -256,7 +257,7 @@ void ShareBox::prepareCommentField() {
.session = _descriptor.session,
.show = Main::MakeSessionShow(show, _descriptor.session),
.field = field,
.fieldStyle = _descriptor.stLabel,
.fieldStyle = _descriptor.st.label,
});
}
field->setSubmitSettings(Core::App().settings().sendSubmitWay());
@@ -281,7 +282,9 @@ void ShareBox::prepare() {
_select->resizeToWidth(st::boxWideWidth);
Ui::SendPendingMoveResizeEvents(_select);
setTitle(tr::lng_share_title());
setTitle(_descriptor.titleOverride
? std::move(_descriptor.titleOverride)
: tr::lng_share_title());
_inner = setInnerWidget(
object_ptr<Inner>(this, _descriptor, uiShow()),
@@ -566,6 +569,9 @@ void ShareBox::showMenu(not_null<Ui::RpWidget*> parent) {
submit(action.options);
return;
}
const auto st = _descriptor.st.scheduleBox
? *_descriptor.st.scheduleBox
: HistoryView::ScheduleBoxStyleArgs();
uiShow()->showBox(
HistoryView::PrepareScheduleBox(
this,
@@ -574,7 +580,7 @@ void ShareBox::showMenu(not_null<Ui::RpWidget*> parent) {
[=](Api::SendOptions options) { submit(options); },
action.options,
HistoryView::DefaultScheduleTime(),
_descriptor.scheduleBoxStyle));
st));
});
_menu->setForcedVerticalOrigin(Ui::PopupMenu::VerticalOrigin::Bottom);
const auto result = FillSendMenu(
@@ -709,7 +715,9 @@ ShareBox::Inner::Inner(
: RpWidget(parent)
, _descriptor(descriptor)
, _show(std::move(show))
, _st(_descriptor.st ? *_descriptor.st : st::shareBoxList)
, _st(_descriptor.st.peerList
? *_descriptor.st.peerList
: st::shareBoxList)
, _defaultChatsIndexed(
std::make_unique<Dialogs::IndexedList>(
Dialogs::SortMode::Add))
@@ -1494,7 +1502,8 @@ ChatHelpers::ForwardedMessagePhraseArgs CreateForwardedMessagePhraseArgs(
ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
std::shared_ptr<Ui::Show> show,
not_null<History*> history,
MessageIdsList msgIds) {
MessageIdsList msgIds,
std::optional<TimeId> videoTimestamp) {
struct State final {
base::flat_set<mtpRequestId> requests;
};
@@ -1530,6 +1539,9 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
: Flag(0))
| ((forwardOptions == Data::ForwardOptions::NoNamesAndCaptions)
? Flag::f_drop_media_captions
: Flag(0))
| (videoTimestamp.has_value()
? Flag::f_video_timestamp
: Flag(0));
auto mtpMsgIds = QVector<MTPint>();
mtpMsgIds.reserve(existingIds.size());
@@ -1586,7 +1598,8 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
MTP_int(topMsgId),
MTP_int(options.scheduled),
MTP_inputPeerEmpty(), // send_as
Data::ShortcutIdToMTP(session, options.shortcutId)
Data::ShortcutIdToMTP(session, options.shortcutId),
MTP_int(videoTimestamp.value_or(0))
)).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
threadHistory->session().api().applyUpdates(updates);
state->requests.remove(reqId);
@@ -1624,9 +1637,37 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
};
}
ShareBoxStyleOverrides DarkShareBoxStyle() {
using namespace HistoryView;
const auto schedule = [&] {
auto date = Ui::ChooseDateTimeStyleArgs();
date.labelStyle = &st::groupCallBoxLabel;
date.dateFieldStyle = &st::groupCallScheduleDateField;
date.timeFieldStyle = &st::groupCallScheduleTimeField;
date.separatorStyle = &st::callMuteButtonLabel;
date.atStyle = &st::callMuteButtonLabel;
date.calendarStyle = &st::groupCallCalendarColors;
auto st = ScheduleBoxStyleArgs();
st.topButtonStyle = &st::groupCallMenuToggle;
st.popupMenuStyle = &st::groupCallPopupMenu;
st.chooseDateTimeArgs = std::move(date);
return st;
};
return {
.multiSelect = &st::groupCallMultiSelect,
.comment = &st::groupCallShareBoxComment,
.peerList = &st::groupCallShareBoxList,
.label = &st::groupCallField,
.scheduleBox = std::make_shared<ScheduleBoxStyleArgs>(schedule()),
};
}
void FastShareMessage(
std::shared_ptr<Main::SessionShow> show,
not_null<HistoryItem*> item) {
not_null<HistoryItem*> item,
ShareBoxStyleOverrides st) {
const auto history = item->history();
const auto owner = &history->owner();
const auto session = &history->session();
@@ -1695,6 +1736,7 @@ void FastShareMessage(
history,
msgIds),
.filterCallback = std::move(filterCallback),
.st = st,
.forwardOptions = {
.sendersCount = ItemsForwardSendersCount(items),
.captionsCount = ItemsForwardCaptionsCount(items),
@@ -1706,19 +1748,22 @@ void FastShareMessage(
void FastShareMessage(
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item) {
FastShareMessage(controller->uiShow(), item);
not_null<HistoryItem*> item,
ShareBoxStyleOverrides st) {
FastShareMessage(controller->uiShow(), item, st);
}
void FastShareLink(
not_null<Window::SessionController*> controller,
const QString &url) {
FastShareLink(controller->uiShow(), url);
const QString &url,
ShareBoxStyleOverrides st) {
FastShareLink(controller->uiShow(), url, st);
}
void FastShareLink(
std::shared_ptr<Main::SessionShow> show,
const QString &url) {
const QString &url,
ShareBoxStyleOverrides st) {
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
const auto sending = std::make_shared<bool>();
auto copyCallback = [=] {
@@ -1782,6 +1827,7 @@ void FastShareLink(
.copyCallback = std::move(copyCallback),
.submitCallback = std::move(submitCallback),
.filterCallback = std::move(filterCallback),
.st = st,
.premiumRequiredError = SharePremiumRequiredError(),
}),
Ui::LayerOption::KeepOther,
+22 -10
View File
@@ -61,18 +61,31 @@ class PopupMenu;
class ShareBox;
struct ShareBoxStyleOverrides {
const style::MultiSelect *multiSelect = nullptr;
const style::InputField *comment = nullptr;
const style::PeerList *peerList = nullptr;
const style::InputField *label = nullptr;
std::shared_ptr<HistoryView::ScheduleBoxStyleArgs> scheduleBox;
};
[[nodiscard]] ShareBoxStyleOverrides DarkShareBoxStyle();
void FastShareMessage(
std::shared_ptr<Main::SessionShow> show,
not_null<HistoryItem*> item);
not_null<HistoryItem*> item,
ShareBoxStyleOverrides st = {});
void FastShareMessage(
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item);
not_null<HistoryItem*> item,
ShareBoxStyleOverrides st = {});
void FastShareLink(
not_null<Window::SessionController*> controller,
const QString &url);
const QString &url,
ShareBoxStyleOverrides st = {});
void FastShareLink(
std::shared_ptr<Main::SessionShow> show,
const QString &url);
const QString &url,
ShareBoxStyleOverrides st = {});
struct RecipientPremiumRequiredError;
[[nodiscard]] auto SharePremiumRequiredError()
@@ -91,7 +104,8 @@ public:
[[nodiscard]] static SubmitCallback DefaultForwardCallback(
std::shared_ptr<Ui::Show> show,
not_null<History*> history,
MessageIdsList msgIds);
MessageIdsList msgIds,
std::optional<TimeId> videoTimestamp = {});
struct Descriptor {
not_null<Main::Session*> session;
@@ -100,16 +114,14 @@ public:
FilterCallback filterCallback;
object_ptr<Ui::RpWidget> bottomWidget = { nullptr };
rpl::producer<QString> copyLinkText;
const style::MultiSelect *stMultiSelect = nullptr;
const style::InputField *stComment = nullptr;
const style::PeerList *st = nullptr;
const style::InputField *stLabel = nullptr;
rpl::producer<QString> titleOverride;
ShareBoxStyleOverrides st;
std::optional<TimeId> videoTimestamp;
struct {
int sendersCount = 0;
int captionsCount = 0;
bool show = false;
} forwardOptions;
HistoryView::ScheduleBoxStyleArgs scheduleBoxStyle;
using PremiumRequiredError = RecipientPremiumRequiredError;
Fn<PremiumRequiredError(not_null<UserData*>)> premiumRequiredError;
+482 -75
View File
@@ -8,14 +8,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/star_gift_box.h"
#include "apiwrap.h"
#include "api/api_credits.h"
#include "api/api_premium.h"
#include "base/event_filter.h"
#include "base/random.h"
#include "base/timer_rpl.h"
#include "base/unixtime.h"
#include "api/api_premium.h"
#include "boxes/filters/edit_filter_chats_list.h"
#include "boxes/peers/edit_peer_color_box.h"
#include "boxes/gift_premium_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/premium_preview_box.h"
#include "boxes/send_credits_box.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/message_field.h"
@@ -24,10 +27,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#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_credits.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_emoji_statuses.h"
#include "data/data_file_origin.h"
#include "data/data_peer_values.h"
#include "data/data_premium_limits.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "data/stickers/data_custom_emoji.h"
@@ -51,6 +58,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_credits.h"
#include "settings/settings_credits_graphics.h"
#include "settings/settings_premium.h"
#include "ui/boxes/boost_box.h"
#include "ui/chat/chat_style.h"
#include "ui/chat/chat_theme.h"
#include "ui/controls/emoji_button.h"
@@ -70,6 +78,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/shadow.h"
#include "window/themes/window_theme.h"
#include "window/section_widget.h"
@@ -82,6 +91,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_menu_icons.h"
#include "styles/style_premium.h"
#include "styles/style_settings.h"
#include "styles/style_widgets.h"
#include <QtWidgets/QApplication>
@@ -89,8 +99,8 @@ namespace Ui {
namespace {
constexpr auto kPriceTabAll = 0;
constexpr auto kPriceTabLimited = -1;
constexpr auto kPriceTabInStock = -2;
constexpr auto kPriceTabLimited = -2;
constexpr auto kPriceTabInStock = -1;
constexpr auto kGiftMessageLimit = 255;
constexpr auto kSentToastDuration = 3 * crl::time(1000);
constexpr auto kSwitchUpgradeCoverInterval = 3 * crl::time(1000);
@@ -206,8 +216,12 @@ auto GenerateGiftMedia(
Element *replacing,
not_null<PeerData*> recipient,
const GiftDetails &data)
-> Fn<void(Fn<void(std::unique_ptr<MediaGenericPart>)>)> {
return [=](Fn<void(std::unique_ptr<MediaGenericPart>)> push) {
-> Fn<void(
not_null<MediaGeneric*>,
Fn<void(std::unique_ptr<MediaGenericPart>)>)> {
return [=](
not_null<MediaGeneric*> media,
Fn<void(std::unique_ptr<MediaGenericPart>)> push) {
const auto &descriptor = data.descriptor;
auto pushText = [&](
TextWithEntities text,
@@ -241,18 +255,26 @@ auto GenerateGiftMedia(
replacing,
sticker,
st::giftBoxPreviewStickerPadding));
const auto title = v::match(descriptor, [&](GiftTypePremium gift) {
auto title = v::match(descriptor, [&](GiftTypePremium gift) {
return tr::lng_action_gift_premium_months(
tr::now,
lt_count,
gift.months);
gift.months,
Text::Bold);
}, [&](const GiftTypeStars &gift) {
return recipient->isSelf()
? tr::lng_action_gift_self_subtitle(tr::now)
? tr::lng_action_gift_self_subtitle(tr::now, Text::Bold)
: tr::lng_action_gift_got_subtitle(
tr::now,
lt_user,
recipient->session().user()->shortName());
TextWithEntities()
.append(Text::SingleCustomEmoji(
recipient->owner().customEmojiManager(
).peerUserpicEmojiData(
recipient->session().user())))
.append(' ')
.append(recipient->session().user()->shortName()),
Text::Bold);
});
auto textFallback = v::match(descriptor, [&](GiftTypePremium gift) {
return tr::lng_action_gift_premium_about(
@@ -267,8 +289,14 @@ auto GenerateGiftMedia(
? tr::lng_action_gift_self_about_unique(
tr::now,
Text::RichLangValue)
: (recipient->isBroadcast() && gift.info.starsToUpgrade)
? tr::lng_action_gift_channel_about_unique(
tr::now,
Text::RichLangValue)
: (recipient->isSelf()
? tr::lng_action_gift_self_about
: recipient->isBroadcast()
? tr::lng_action_gift_channel_about
: tr::lng_action_gift_got_stars_text)(
tr::now,
lt_count,
@@ -278,15 +306,20 @@ auto GenerateGiftMedia(
auto description = data.text.empty()
? std::move(textFallback)
: data.text;
pushText(Text::Bold(title), st::giftBoxPreviewTitlePadding);
const auto context = Core::MarkedTextContext{
.session = &parent->history()->session(),
.customEmojiRepaint = [parent] { parent->repaint(); },
};
pushText(
std::move(title),
st::giftBoxPreviewTitlePadding,
{},
context);
pushText(
std::move(description),
st::giftBoxPreviewTextPadding,
{},
Core::MarkedTextContext{
.session = &parent->history()->session(),
.customEmojiRepaint = [parent] { parent->repaint(); },
});
context);
push(HistoryView::MakeGenericButtonPart(
(data.upgraded
@@ -475,6 +508,15 @@ void PreviewWrap::prepare(rpl::producer<GiftDetails> details) {
? tr::lng_action_gift_unique_received(tr::now, lt_user, name)
: _recipient->isSelf()
? tr::lng_action_gift_self_bought(tr::now, lt_cost, cost)
: _recipient->isBroadcast()
? tr::lng_action_gift_sent_channel(
tr::now,
lt_user,
name,
lt_name,
_recipient->name(),
lt_cost,
cost)
: tr::lng_action_gift_received(
tr::now,
lt_user,
@@ -652,6 +694,21 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
for (auto &gift : gifts) {
list.push_back({ .info = gift });
}
ranges::sort(list, [](
const GiftTypeStars &a,
const GiftTypeStars &b) {
if (!a.info.limitedCount && !b.info.limitedCount) {
return a.info.stars <= b.info.stars;
} else if (!a.info.limitedCount) {
return true;
} else if (!b.info.limitedCount) {
return false;
} else if (a.info.limitedLeft != b.info.limitedLeft) {
return a.info.limitedLeft > b.info.limitedLeft;
}
return a.info.stars <= b.info.stars;
});
auto &map = Map[session];
if (map.last != list) {
map.last = list;
@@ -1060,7 +1117,7 @@ void SendGift(
.giftId = gift.info.id,
.randomId = details.randomId,
.message = details.text,
.user = peer->asUser(),
.recipient = peer,
.limitedCount = gift.info.limitedCount,
.anonymous = details.anonymous,
.upgraded = details.upgraded,
@@ -1145,7 +1202,7 @@ void SendStarsFormRequest(
void UpgradeGift(
not_null<Window::SessionController*> window,
MsgId messageId,
Data::SavedStarGiftId savedId,
bool keepDetails,
int stars,
Fn<void(Payments::CheckoutResult)> done) {
@@ -1165,7 +1222,7 @@ void UpgradeGift(
using Flag = MTPpayments_UpgradeStarGift::Flag;
session->api().request(MTPpayments_UpgradeStarGift(
MTP_flags(keepDetails ? Flag::f_keep_original_details : Flag()),
MTP_int(messageId.bare)
Api::InputSavedStarGiftId(savedId)
)).done([=](const MTPUpdates &result) {
session->api().applyUpdates(result);
formDone(Payments::CheckoutResult::Paid, &result);
@@ -1182,7 +1239,7 @@ void UpgradeGift(
window,
MTP_inputInvoiceStarGiftUpgrade(
MTP_flags(keepDetails ? Flag::f_keep_original_details : Flag()),
MTP_int(messageId.bare)),
Api::InputSavedStarGiftId(savedId)),
std::move(formDone));
}
@@ -1211,7 +1268,7 @@ void AddUpgradeButton(
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session,
int cost,
QString name,
not_null<PeerData*> peer,
Fn<void(bool)> toggled,
Fn<void()> preview) {
const auto button = container->add(
@@ -1258,25 +1315,105 @@ void AddUpgradeButton(
AddSkip(container);
const auto about = AddDividerText(
container,
tr::lng_gift_send_unique_about(
lt_user,
rpl::single(TextWithEntities{ name }),
lt_link,
tr::lng_gift_send_unique_link() | Text::ToLink(),
Text::WithEntities));
(peer->isBroadcast()
? tr::lng_gift_send_unique_about_channel(
lt_name,
rpl::single(TextWithEntities{ peer->name() }),
lt_link,
tr::lng_gift_send_unique_link() | Text::ToLink(),
Text::WithEntities)
: tr::lng_gift_send_unique_about(
lt_user,
rpl::single(TextWithEntities{ peer->shortName() }),
lt_link,
tr::lng_gift_send_unique_link() | Text::ToLink(),
Text::WithEntities)));
about->setClickHandlerFilter([=](const auto &...) {
preview();
return false;
});
}
void AddSoldLeftSlider(
not_null<RoundButton*> button,
const GiftTypeStars &gift) {
const auto still = gift.info.limitedLeft;
const auto total = gift.info.limitedCount;
const auto slider = CreateChild<RpWidget>(button->parentWidget());
struct State {
Text::String still;
Text::String sold;
int height = 0;
};
const auto state = slider->lifetime().make_state<State>();
const auto sold = total - still;
state->still.setText(
st::semiboldTextStyle,
tr::lng_gift_send_limited_left(tr::now, lt_count_decimal, still));
state->sold.setText(
st::semiboldTextStyle,
tr::lng_gift_send_limited_sold(tr::now, lt_count_decimal, sold));
state->height = st::giftLimitedPadding.top()
+ st::semiboldFont->height
+ st::giftLimitedPadding.bottom();
button->geometryValue() | rpl::start_with_next([=](QRect geometry) {
const auto space = st::giftLimitedBox.buttonPadding.top();
const auto skip = (space - state->height) / 2;
slider->setGeometry(
geometry.x(),
geometry.y() - skip - state->height,
geometry.width(),
state->height);
}, slider->lifetime());
slider->paintRequest() | rpl::start_with_next([=] {
const auto &padding = st::giftLimitedPadding;
const auto left = (padding.left() * 2) + state->still.maxWidth();
const auto right = (padding.right() * 2) + state->sold.maxWidth();
const auto space = slider->width() - left - right;
if (space <= 0) {
return;
}
const auto edge = left + ((space * still) / total);
const auto radius = st::buttonRadius;
auto p = QPainter(slider);
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(st::windowBgOver);
p.drawRoundedRect(
edge - (radius * 3),
0,
slider->width() - (edge - (radius * 3)),
state->height,
radius,
radius);
p.setBrush(st::windowBgActive);
p.drawRoundedRect(0, 0, edge, state->height, radius, radius);
p.setPen(st::windowFgActive);
state->still.draw(p, {
.position = { padding.left(), padding.top() },
.availableWidth = left,
});
p.setPen(st::windowSubTextFg);
state->sold.draw(p, {
.position = { left + space + padding.right(), padding.top() },
.availableWidth = right,
});
}, slider->lifetime());
}
void SendGiftBox(
not_null<GenericBox*> box,
not_null<Window::SessionController*> window,
not_null<PeerData*> peer,
std::shared_ptr<Api::PremiumGiftCodeOptions> api,
const GiftDescriptor &descriptor) {
box->setStyle(st::giftBox);
const auto stars = std::get_if<GiftTypeStars>(&descriptor);
const auto limited = stars
&& (stars->info.limitedCount > stars->info.limitedLeft)
&& (stars->info.limitedLeft > 0);
box->setStyle(limited ? st::giftLimitedBox : st::giftBox);
box->setWidth(st::boxWideWidth);
box->setTitle(tr::lng_gift_send_title());
box->addTopButton(st::boxTitleClose, [=] {
@@ -1373,18 +1510,14 @@ void SendGiftBox(
session,
{ .suggestCustomEmoji = true, .allowCustomWithoutPremium = allow });
if (const auto stars = std::get_if<GiftTypeStars>(&descriptor)) {
if (stars) {
const auto cost = stars->info.starsToUpgrade;
if (cost > 0 && !peer->isSelf()) {
const auto user = peer->asUser();
Assert(user != nullptr);
const auto id = stars->info.id;
const auto name = user->shortName();
const auto showing = std::make_shared<bool>();
AddDivider(container);
AddSkip(container);
AddUpgradeButton(container, session, cost, name, [=](bool on) {
AddUpgradeButton(container, session, cost, peer, [=](bool on) {
auto now = state->details.current();
now.upgraded = on;
state->details = std::move(now);
@@ -1397,7 +1530,7 @@ void SendGiftBox(
.controller = window,
.stargiftId = id,
.ready = [=](bool) { *showing = false; },
.user = user,
.peer = peer,
.cost = int(cost),
});
});
@@ -1425,6 +1558,8 @@ void SendGiftBox(
}, [&](const GiftTypeStars &) {
AddDividerText(container, peer->isSelf()
? tr::lng_gift_send_anonymous_self()
: peer->isBroadcast()
? tr::lng_gift_send_anonymous_about_channel()
: tr::lng_gift_send_anonymous_about(
lt_user,
rpl::single(peer->shortName()),
@@ -1454,6 +1589,9 @@ void SendGiftBox(
};
SendGift(window, peer, api, details, done);
});
if (limited) {
AddSoldLeftSlider(button, *stars);
}
SetButtonMarkedLabel(
button,
(peer->isSelf()
@@ -1704,7 +1842,7 @@ void GiftBox(
window->showSettings(Settings::CreditsId());
return false;
};
if (!peer->isSelf()) {
if (peer->isUser() && !peer->isSelf()) {
const auto premiumClickHandlerFilter = [=](const auto &...) {
Settings::ShowPremium(window, u"gift_send"_q);
return false;
@@ -1724,9 +1862,16 @@ void GiftBox(
AddBlock(content, window, {
.subtitle = (peer->isSelf()
? tr::lng_gift_self_title()
: peer->isBroadcast()
? tr::lng_gift_channel_title()
: tr::lng_gift_stars_subtitle()),
.about = (peer->isSelf()
? tr::lng_gift_self_about(Text::WithEntities)
: peer->isBroadcast()
? tr::lng_gift_channel_about(
lt_name,
rpl::single(Text::Bold(peer->name())),
Text::WithEntities)
: tr::lng_gift_stars_about(
lt_name,
rpl::single(Text::Bold(peer->shortName())),
@@ -2112,6 +2257,242 @@ void AddUniqueGiftCover(
}, cover->lifetime());
}
void AddWearGiftCover(
not_null<VerticalLayout*> container,
const Data::UniqueGift &data,
not_null<PeerData*> peer) {
const auto cover = container->add(object_ptr<RpWidget>(container));
const auto title = CreateChild<FlatLabel>(
cover,
rpl::single(peer->name()),
st::uniqueGiftTitle);
title->setTextColorOverride(QColor(255, 255, 255));
const auto subtitle = CreateChild<FlatLabel>(
cover,
(peer->isChannel()
? tr::lng_chat_status_subscribers(
lt_count,
rpl::single(peer->asChannel()->membersCount() * 1.))
: tr::lng_status_online()),
st::uniqueGiftSubtitle);
subtitle->setTextColorOverride(data.backdrop.textColor);
struct State {
QImage gradient;
Data::UniqueGift gift;
Ui::PeerUserpicView view;
std::unique_ptr<Text::CustomEmoji> emoji;
base::flat_map<float64, QImage> emojis;
rpl::lifetime lifetime;
};
const auto state = cover->lifetime().make_state<State>(State{
.gift = data,
});
state->emoji = peer->owner().customEmojiManager().create(
state->gift.pattern.document,
[=] { cover->update(); },
Data::CustomEmojiSizeTag::Large);
cover->widthValue() | rpl::start_with_next([=](int width) {
const auto skip = st::uniqueGiftBottom;
if (width <= 3 * skip) {
return;
}
const auto available = width - 2 * skip;
title->resizeToWidth(available);
title->moveToLeft(skip, st::uniqueGiftTitleTop);
subtitle->resizeToWidth(available);
subtitle->moveToLeft(skip, st::uniqueGiftSubtitleTop);
cover->resize(width, subtitle->y() + subtitle->height() + skip);
}, cover->lifetime());
cover->paintRequest() | rpl::start_with_next([=] {
auto p = Painter(cover);
const auto width = cover->width();
const auto pointsHeight = st::uniqueGiftSubtitleTop;
const auto ratio = style::DevicePixelRatio();
if (state->gradient.size() != cover->size() * ratio) {
state->gradient = CreateGradient(cover->size(), state->gift);
}
p.drawImage(0, 0, state->gradient);
PaintPoints(
p,
PatternPoints(),
state->emojis,
state->emoji.get(),
state->gift,
QRect(0, 0, width, pointsHeight),
1.);
peer->paintUserpic(
p,
state->view,
(width - st::uniqueGiftUserpicSize) / 2,
st::uniqueGiftUserpicTop,
st::uniqueGiftUserpicSize);
}, cover->lifetime());
}
void ShowUniqueGiftWearBox(
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> peer,
const Data::UniqueGift &gift,
Settings::GiftWearBoxStyleOverride st) {
show->show(Box([=](not_null<Ui::GenericBox*> box) {
box->setNoContentMargin(true);
box->setWidth((st::boxWidth + st::boxWideWidth) / 2); // =)
box->setStyle(st.box ? *st.box : st::upgradeGiftBox);
const auto channel = peer->isChannel();
const auto content = box->verticalLayout();
AddWearGiftCover(content, gift, peer);
AddSkip(content, st::defaultVerticalListSkip * 2);
const auto infoRow = [&](
rpl::producer<QString> title,
rpl::producer<QString> text,
not_null<const style::icon*> icon) {
auto raw = content->add(
object_ptr<Ui::VerticalLayout>(content));
raw->add(
object_ptr<Ui::FlatLabel>(
raw,
std::move(title) | Ui::Text::ToBold(),
st.infoTitle ? *st.infoTitle : st::defaultFlatLabel),
st::settingsPremiumRowTitlePadding);
raw->add(
object_ptr<Ui::FlatLabel>(
raw,
std::move(text),
st.infoAbout ? *st.infoAbout : st::upgradeGiftSubtext),
st::settingsPremiumRowAboutPadding);
object_ptr<Info::Profile::FloatingIcon>(
raw,
*icon,
st::starrefInfoIconPosition);
};
content->add(
object_ptr<Ui::FlatLabel>(
content,
tr::lng_gift_wear_title(
lt_name,
rpl::single(UniqueGiftName(gift))),
st.title ? *st.title : st::uniqueGiftTitle),
st::settingsPremiumRowTitlePadding);
content->add(
object_ptr<Ui::FlatLabel>(
content,
tr::lng_gift_wear_about(),
st.subtitle ? *st.subtitle : st::uniqueGiftSubtitle),
st::settingsPremiumRowAboutPadding);
infoRow(
tr::lng_gift_wear_badge_title(),
(channel
? tr::lng_gift_wear_badge_about_channel()
: tr::lng_gift_wear_badge_about()),
st.radiantIcon ? st.radiantIcon : &st::menuIconUnique);
//infoRow(
// tr::lng_gift_wear_design_title(),
// tr::lng_gift_wear_design_about(),
// &st::menuIconUniqueProfile);
infoRow(
tr::lng_gift_wear_proof_title(),
(channel
? tr::lng_gift_wear_proof_about_channel()
: tr::lng_gift_wear_proof_about()),
st.proofIcon ? st.proofIcon : &st::menuIconFactcheck);
const auto session = &show->session();
const auto checking = std::make_shared<bool>();
const auto button = box->addButton(rpl::single(QString()), [=] {
const auto emojiStatuses = &session->data().emojiStatuses();
const auto id = emojiStatuses->fromUniqueGift(gift);
if (!peer->isSelf()) {
if (*checking) {
return;
}
*checking = true;
const auto weak = Ui::MakeWeak(box);
CheckBoostLevel(show, peer, [=](int level) {
const auto limits = Data::LevelLimits(&peer->session());
const auto wanted = limits.channelEmojiStatusLevelMin();
if (level >= wanted) {
if (const auto strong = weak.data()) {
strong->closeBox();
}
emojiStatuses->set(peer, id);
return std::optional<Ui::AskBoostReason>();
}
const auto reason = [&]() -> Ui::AskBoostReason {
return { Ui::AskBoostWearCollectible{
wanted
} };
}();
return std::make_optional(reason);
}, [=] { *checking = false; });
} else if (session->premium()) {
box->closeBox();
emojiStatuses->set(peer, id);
} else {
const auto link = Ui::Text::Bold(
tr::lng_send_as_premium_required_link(tr::now));
Settings::ShowPremiumPromoToast(
show,
tr::lng_gift_wear_subscribe(
tr::now,
lt_link,
Ui::Text::Link(link),
Ui::Text::WithEntities),
u"wear_collectibles"_q);
}
});
const auto lock = Ui::Text::SingleCustomEmoji(
session->data().customEmojiManager().registerInternalEmoji(
st::historySendDisabledIcon,
st::giftBoxLockMargins,
true));
auto label = rpl::combine(
tr::lng_gift_wear_start(),
Data::AmPremiumValue(&show->session())
) | rpl::map([=](const QString &text, bool premium) {
auto result = TextWithEntities();
if (!premium && peer->isSelf()) {
result.append(lock);
}
result.append(text);
return result;
});
SetButtonMarkedLabel(
button,
std::move(label),
session,
st::creditsBoxButtonLabel,
&st::giftBox.button.textFg);
rpl::combine(
box->widthValue(),
button->widthValue()
) | rpl::start_with_next([=](int outer, int inner) {
const auto padding = st::giftBox.buttonPadding;
const auto wanted = outer - padding.left() - padding.right();
if (inner != wanted) {
button->resizeToWidth(wanted);
button->moveToLeft(padding.left(), padding.top());
}
}, box->lifetime());
AddUniqueCloseButton(box, {});
}));
}
struct UpgradeArgs : StarGiftUpgradeArgs {
std::vector<Data::UniqueGiftModel> models;
std::vector<Data::UniqueGiftPattern> patterns;
@@ -2162,7 +2543,7 @@ struct UpgradeArgs : StarGiftUpgradeArgs {
auto &patterns = state->data.patterns;
auto &backdrops = state->data.backdrops;
consumer.put_next(Data::UniqueGift{
.title = (state->data.itemId
.title = (state->data.savedId
? tr::lng_gift_upgrade_title(tr::now)
: tr::lng_gift_upgrade_preview_title(tr::now)),
.model = models[index(state->modelIndices, models)],
@@ -2186,11 +2567,13 @@ void AddUpgradeGiftCover(
AddUniqueGiftCover(
container,
MakeUpgradeGiftStream(args),
(args.itemId
(args.savedId
? tr::lng_gift_upgrade_about()
: tr::lng_gift_upgrade_preview_about(
lt_name,
rpl::single(args.user->shortName()))));
: (args.peer->isBroadcast()
? tr::lng_gift_upgrade_preview_about_channel
: tr::lng_gift_upgrade_preview_about)(
lt_name,
rpl::single(args.peer->shortName()))));
}
void UpgradeBox(
@@ -2207,31 +2590,20 @@ void UpgradeBox(
const auto infoRow = [&](
rpl::producer<QString> title,
rpl::producer<QString> text,
not_null<const style::icon*> icon,
bool newBadge = false) {
not_null<const style::icon*> icon) {
auto raw = container->add(
object_ptr<Ui::VerticalLayout>(container));
const auto widget = raw->add(
raw->add(
object_ptr<Ui::FlatLabel>(
raw,
std::move(title) | Ui::Text::ToBold(),
st::defaultFlatLabel),
st::settingsPremiumRowTitlePadding);
if (newBadge) {
const auto badge = NewBadge::CreateNewBadge(
raw,
tr::lng_soon_badge(Ui::Text::Upper));
widget->geometryValue(
) | rpl::start_with_next([=](QRect geometry) {
badge->move(st::settingsPremiumNewBadgePosition
+ QPoint(widget->x() + widget->width(), widget->y()));
}, badge->lifetime());
}
raw->add(
object_ptr<Ui::FlatLabel>(
raw,
std::move(text),
st::boxDividerLabel),
st::upgradeGiftSubtext),
st::settingsPremiumRowAboutPadding);
object_ptr<Info::Profile::FloatingIcon>(
raw,
@@ -2250,15 +2622,14 @@ void UpgradeBox(
infoRow(
tr::lng_gift_upgrade_tradable_title(),
tr::lng_gift_upgrade_tradable_about(),
&st::menuIconTradable,
true);
&st::menuIconTradable);
struct State {
bool sent = false;
bool preserveDetails = false;
};
const auto state = std::make_shared<State>();
const auto preview = !args.itemId;
const auto preview = !args.savedId;
if (!preview) {
const auto skip = st::defaultVerticalListSkip;
@@ -2303,13 +2674,13 @@ void UpgradeBox(
if (result != Payments::CheckoutResult::Paid) {
state->sent = false;
} else {
controller->showPeerHistory(args.user);
controller->showPeerHistory(args.peer);
if (const auto strong = weak.data()) {
strong->closeBox();
}
}
};
UpgradeGift(controller, args.itemId, keepDetails, cost, done);
UpgradeGift(controller, args.savedId, keepDetails, cost, done);
});
if (!preview) {
auto star = session->data().customEmojiManager().creditsEmoji();
@@ -2339,7 +2710,7 @@ void UpgradeBox(
}
}, box->lifetime());
AddUniqueCloseButton(box);
AddUniqueCloseButton(box, {});
}
const std::vector<PatternPoint> &PatternPoints() {
@@ -2458,7 +2829,7 @@ void PaintPoints(
void ShowStarGiftUpgradeBox(StarGiftUpgradeArgs &&args) {
const auto weak = base::make_weak(args.controller);
const auto session = &args.user->session();
const auto session = &args.peer->session();
session->api().request(MTPpayments_GetStarGiftUpgradePreview(
MTP_long(args.stargiftId)
)).done([=](const MTPpayments_StarGiftUpgradePreview &result) {
@@ -2494,19 +2865,51 @@ void ShowStarGiftUpgradeBox(StarGiftUpgradeArgs &&args) {
}).send();
}
void AddUniqueCloseButton(not_null<GenericBox*> box) {
const auto button = Ui::CreateChild<IconButton>(
void AddUniqueCloseButton(
not_null<GenericBox*> box,
Settings::CreditsEntryBoxStyleOverrides st,
Fn<void(not_null<Ui::PopupMenu*>)> fillMenu) {
const auto close = Ui::CreateChild<IconButton>(
box,
st::uniqueCloseButton);
button->show();
button->raise();
const auto menu = fillMenu
? Ui::CreateChild<IconButton>(box, st::uniqueMenuButton)
: nullptr;
close->show();
close->raise();
if (menu) {
menu->show();
menu->raise();
}
box->widthValue() | rpl::start_with_next([=](int width) {
button->moveToRight(0, 0, width);
button->raise();
}, button->lifetime());
button->setClickedCallback([=] {
close->moveToRight(0, 0, width);
close->raise();
if (menu) {
menu->moveToRight(close->width(), 0, width);
menu->raise();
}
}, close->lifetime());
close->setClickedCallback([=] {
box->closeBox();
});
if (menu) {
const auto state = menu->lifetime().make_state<
base::unique_qptr<Ui::PopupMenu>
>();
menu->setClickedCallback([=] {
if (*state) {
*state = nullptr;
return;
}
*state = base::make_unique_q<Ui::PopupMenu>(
menu,
st.menu ? *st.menu : st::popupMenuWithIcons);
fillMenu(state->get());
if (!(*state)->empty()) {
(*state)->popup(QCursor::pos());
}
});
}
}
void RequestStarsFormAndSubmit(
@@ -2539,25 +2942,29 @@ void RequestStarsFormAndSubmit(
done(Payments::CheckoutResult::Failed, nullptr);
});
}).fail([=](const MTP::Error &error) {
if (const auto strong = weak.get()) {
strong->showToast(error.type());
const auto type = error.type();
if (type == u"STARGIFT_EXPORT_IN_PROGRESS"_q) {
done(Payments::CheckoutResult::Cancelled, nullptr);
} else {
if (const auto strong = weak.get()) {
strong->showToast(type);
}
done(Payments::CheckoutResult::Failed, nullptr);
}
done(Payments::CheckoutResult::Failed, nullptr);
}).send();
}
void ShowGiftTransferredToast(
base::weak_ptr<Window::SessionController> weak,
not_null<PeerData*> to,
const MTPUpdates &result) {
const auto gift = FindUniqueGift(&to->session(), result);
if (const auto strong = gift ? weak.get() : nullptr) {
const Data::UniqueGift &gift) {
if (const auto strong = weak.get()) {
strong->showToast({
.title = tr::lng_gift_transferred_title(tr::now),
.text = tr::lng_gift_transferred_about(
tr::now,
lt_name,
Text::Bold(Data::UniqueGiftName(*gift)),
Text::Bold(Data::UniqueGiftName(gift)),
lt_recipient,
Text::Bold(to->shortName()),
Ui::Text::WithEntities),
+29 -4
View File
@@ -7,6 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_star_gift.h"
namespace ChatHelpers {
class Show;
} // namespace ChatHelpers
namespace Data {
struct UniqueGift;
struct GiftCode;
@@ -17,6 +23,11 @@ namespace Payments {
enum class CheckoutResult;
} // namespace Payments
namespace Settings {
struct GiftWearBoxStyleOverride;
struct CreditsEntryBoxStyleOverrides;
} // namespace Settings
namespace Window {
class SessionController;
} // namespace Window
@@ -27,6 +38,7 @@ class CustomEmoji;
namespace Ui {
class PopupMenu;
class GenericBox;
class VerticalLayout;
@@ -41,6 +53,16 @@ void AddUniqueGiftCover(
not_null<VerticalLayout*> container,
rpl::producer<Data::UniqueGift> data,
rpl::producer<QString> subtitleOverride = nullptr);
void AddWearGiftCover(
not_null<VerticalLayout*> container,
const Data::UniqueGift &data,
not_null<PeerData*> peer);
void ShowUniqueGiftWearBox(
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> peer,
const Data::UniqueGift &gift,
Settings::GiftWearBoxStyleOverride st);
struct PatternPoint {
QPointF position;
@@ -63,8 +85,8 @@ struct StarGiftUpgradeArgs {
not_null<Window::SessionController*> controller;
base::required<uint64> stargiftId;
Fn<void(bool)> ready;
not_null<UserData*> user;
MsgId itemId = 0;
not_null<PeerData*> peer;
Data::SavedStarGiftId savedId;
int cost = 0;
bool canAddSender = false;
bool canAddComment = false;
@@ -73,7 +95,10 @@ struct StarGiftUpgradeArgs {
};
void ShowStarGiftUpgradeBox(StarGiftUpgradeArgs &&args);
void AddUniqueCloseButton(not_null<GenericBox*> box);
void AddUniqueCloseButton(
not_null<GenericBox*> box,
Settings::CreditsEntryBoxStyleOverrides st,
Fn<void(not_null<PopupMenu*>)> fillMenu = nullptr);
void RequestStarsFormAndSubmit(
not_null<Window::SessionController*> window,
@@ -83,6 +108,6 @@ void RequestStarsFormAndSubmit(
void ShowGiftTransferredToast(
base::weak_ptr<Window::SessionController> weak,
not_null<PeerData*> to,
const MTPUpdates &result);
const Data::UniqueGift &gift);
} // namespace Ui
@@ -817,9 +817,7 @@ void StickerSetBox::updateButtons() {
- st.buttonPadding.left()
- st.buttonPadding.left());
button->setClickedCallback([=] {
using namespace ChatHelpers;
const auto usage = WindowUsage::PremiumPromo;
if (const auto window = _show->resolveWindow(usage)) {
if (const auto window = _show->resolveWindow()) {
Settings::ShowPremium(window, u"animated_emoji"_q);
}
});
+197 -45
View File
@@ -8,7 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/transfer_gift_box.h"
#include "apiwrap.h"
#include "api/api_credits.h"
#include "api/api_cloud_password.h"
#include "base/unixtime.h"
#include "boxes/passcode_box.h"
#include "data/data_session.h"
#include "data/data_star_gift.h"
#include "data/data_user.h"
#include "boxes/filters/edit_filter_chats_list.h" // CreatePe...tionSubtitle.
@@ -21,12 +25,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/confirm_box.h"
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
#include "ui/basic_click_handlers.h"
#include "ui/empty_userpic.h"
#include "ui/painter.h"
#include "ui/vertical_list.h"
#include "window/window_session_controller.h"
#include "styles/style_boxes.h" // peerListSingleRow.
#include "styles/style_dialogs.h" // recentPeersSpecialName.
#include "styles/style_layers.h" // boxLabel.
namespace {
@@ -41,13 +47,15 @@ public:
Controller(
not_null<Window::SessionController*> window,
std::shared_ptr<Data::UniqueGift> gift,
Fn<void(not_null<PeerData*>)> choose);
Data::SavedStarGiftId savedId,
Fn<void(not_null<PeerData*>, Fn<void()>)> choose);
void init(not_null<PeerListBox*> box);
void noSearchSubmit();
private:
void prepareViewHook() override;
void setupExportOption();
bool overrideKeyboardNavigation(
int direction,
@@ -58,34 +66,150 @@ private:
not_null<UserData*> user) override;
void rowClicked(not_null<PeerListRow*> row) override;
const not_null<Window::SessionController*> _window;
const std::shared_ptr<Data::UniqueGift> _gift;
const Fn<void(not_null<PeerData*>)> _choose;
const Data::SavedStarGiftId _giftId;
const Fn<void(not_null<PeerData*>, Fn<void()>)> _choose;
ExportOption _exportOption;
QPointer<PeerListBox> _box;
};
void ConfirmExportBox(
not_null<Ui::GenericBox*> box,
std::shared_ptr<Data::UniqueGift> gift,
Fn<void(Fn<void()> close)> confirmed) {
box->setTitle(tr::lng_gift_transfer_confirm_title());
box->addRow(object_ptr<Ui::FlatLabel>(
box,
tr::lng_gift_transfer_confirm_text(
lt_name,
rpl::single(Ui::Text::Bold(UniqueGiftName(*gift))),
Ui::Text::WithEntities),
st::boxLabel));
box->addButton(tr::lng_gift_transfer_confirm_button(), [=] {
confirmed([weak = Ui::MakeWeak(box)] {
if (const auto strong = weak.data()) {
strong->closeBox();
}
});
});
box->addButton(tr::lng_cancel(), [=] {
box->closeBox();
});
}
void ExportOnBlockchain(
not_null<Window::SessionController*> window,
not_null<Ui::RpWidget*> parent,
std::shared_ptr<Data::UniqueGift> gift,
Data::SavedStarGiftId giftId,
Fn<void()> boxShown,
Fn<void()> wentToUrl) {
struct State {
bool loading = false;
rpl::lifetime lifetime;
};
const auto state = std::make_shared<State>();
const auto session = &window->session();
const auto show = window->uiShow();
session->api().cloudPassword().reload();
session->api().request(
MTPpayments_GetStarGiftWithdrawalUrl(
Api::InputSavedStarGiftId(giftId),
MTP_inputCheckPasswordEmpty())
).fail([=](const MTP::Error &error) {
auto box = PrePasswordErrorBox(
error.type(),
session,
TextWithEntities{
tr::lng_gift_transfer_password_about(tr::now),
});
if (box) {
show->show(std::move(box));
boxShown();
return;
}
state->lifetime = session->api().cloudPassword().state(
) | rpl::take(
1
) | rpl::start_with_next([=](const Core::CloudPasswordState &pass) {
auto fields = PasscodeBox::CloudFields::From(pass);
fields.customTitle = tr::lng_gift_transfer_password_title();
fields.customDescription
= tr::lng_gift_transfer_password_description(tr::now);
fields.customSubmitButton = tr::lng_passcode_submit();
fields.customCheckCallback = crl::guard(parent, [=](
const Core::CloudPasswordResult &result,
QPointer<PasscodeBox> box) {
using ExportUrl = MTPpayments_StarGiftWithdrawalUrl;
session->api().request(
MTPpayments_GetStarGiftWithdrawalUrl(
Api::InputSavedStarGiftId(giftId),
result.result)
).done([=](const ExportUrl &result) {
UrlClickHandler::Open(qs(result.data().vurl()));
wentToUrl();
if (box) {
box->closeBox();
}
}).fail([=](const MTP::Error &error) {
const auto message = error.type();
if (box && !box->handleCustomCheckError(message)) {
show->showToast(message);
}
}).send();
});
show->show(Box<PasscodeBox>(session, fields));
boxShown();
});
}).send();
}
[[nodiscard]] ExportOption MakeExportOption(
not_null<Window::SessionController*> window,
not_null<PeerListBox*> box,
std::shared_ptr<Data::UniqueGift> gift,
Data::SavedStarGiftId giftId,
TimeId when) {
struct State {
bool exporting = false;
};
const auto state = std::make_shared<State>();
const auto activate = [=] {
const auto now = base::unixtime::now();
const auto weak = Ui::MakeWeak(box);
const auto left = (when > now) ? (when - now) : 0;
const auto hours = left ? std::max((left + 1800) / 3600, 1) : 0;
if (!hours) {
window->show(Box(ConfirmExportBox, gift, [=](Fn<void()> close) {
if (state->exporting) {
return;
}
state->exporting = true;
ExportOnBlockchain(window, box, gift, giftId, [=] {
state->exporting = false;
close();
}, [=] {
if (const auto strong = weak.data()) {
strong->closeBox();
}
close();
});
}));
return;
}
window->show(Ui::MakeInformBox({
.text = (!hours
? tr::lng_gift_transfer_unlocks_update_about()
: tr::lng_gift_transfer_unlocks_about(
lt_when,
((hours >= 24)
? tr::lng_gift_transfer_unlocks_when_days(
lt_count,
rpl::single((hours / 24) * 1.))
: tr::lng_gift_transfer_unlocks_when_hours(
lt_count,
rpl::single(hours * 1.))))),
.title = (!hours
? tr::lng_gift_transfer_unlocks_update_title()
: tr::lng_gift_transfer_unlocks_title()),
.text = tr::lng_gift_transfer_unlocks_about(
lt_when,
((hours >= 24)
? tr::lng_gift_transfer_unlocks_when_days(
lt_count,
rpl::single((hours / 24) * 1.))
: tr::lng_gift_transfer_unlocks_when_hours(
lt_count,
rpl::single(hours * 1.)))),
.title = tr::lng_gift_transfer_unlocks_title(),
}));
};
@@ -130,10 +254,14 @@ private:
const style::PeerListItem &computeSt(
const style::PeerListItem &st) const override {
return _available ? st::recentPeersSpecialName : st;
_st = st;
_st.namePosition.setY(
st::recentPeersSpecialName.namePosition.y());
return _available ? _st : st;
}
private:
mutable style::PeerListItem _st;
bool _available = false;
};
@@ -228,18 +356,27 @@ private:
Controller::Controller(
not_null<Window::SessionController*> window,
std::shared_ptr<Data::UniqueGift> gift,
Fn<void(not_null<PeerData*>)> choose)
Data::SavedStarGiftId giftId,
Fn<void(not_null<PeerData*>, Fn<void()>)> choose)
: ContactsBoxController(&window->session())
, _window(window)
, _gift(std::move(gift))
, _giftId(giftId)
, _choose(std::move(choose)) {
if (const auto when = _gift->exportAt) {
_exportOption = MakeExportOption(window, when);
}
if (_exportOption.content) {
if (_gift->exportAt) {
setStyleOverrides(&st::peerListSmallSkips);
}
}
void Controller::init(not_null<PeerListBox*> box) {
_box = box;
if (const auto when = _gift->exportAt) {
_exportOption = MakeExportOption(_window, box, _gift, _giftId, when);
delegate()->peerListSetAboveWidget(std::move(_exportOption.content));
delegate()->peerListRefreshRows();
}
}
void Controller::noSearchSubmit() {
if (const auto onstack = _exportOption.activate) {
onstack();
@@ -258,11 +395,6 @@ void Controller::prepareViewHook() {
delegate()->peerListSetTitle(tr::lng_gift_transfer_title(
lt_name,
rpl::single(UniqueGiftName(*_gift))));
setupExportOption();
}
void Controller::setupExportOption() {
delegate()->peerListSetAboveWidget(std::move(_exportOption.content));
}
std::unique_ptr<PeerListRow> Controller::createRow(
@@ -277,14 +409,18 @@ std::unique_ptr<PeerListRow> Controller::createRow(
}
void Controller::rowClicked(not_null<PeerListRow*> row) {
_choose(row->peer());
_choose(row->peer(), [parentBox = _box] {
if (const auto strong = parentBox.data()) {
strong->closeBox();
}
});
}
void TransferGift(
not_null<Window::SessionController*> window,
not_null<PeerData*> to,
std::shared_ptr<Data::UniqueGift> gift,
MsgId messageId,
Data::SavedStarGiftId savedId,
Fn<void(Payments::CheckoutResult)> done) {
Expects(to->isUser());
@@ -293,33 +429,37 @@ void TransferGift(
auto formDone = [=](
Payments::CheckoutResult result,
const MTPUpdates *updates) {
if (result == Payments::CheckoutResult::Paid && updates) {
done(result);
if (result == Payments::CheckoutResult::Paid) {
if (const auto strong = weak.get()) {
Ui::ShowGiftTransferredToast(strong, to, *updates);
strong->session().data().notifyGiftUpdate({
.id = savedId,
.action = Data::GiftUpdate::Action::Transfer,
});
Ui::ShowGiftTransferredToast(strong, to, *gift);
}
}
done(result);
};
if (gift->starsForTransfer <= 0) {
session->api().request(MTPpayments_TransferStarGift(
MTP_int(messageId.bare),
to->asUser()->inputUser
Api::InputSavedStarGiftId(savedId),
to->input
)).done([=](const MTPUpdates &result) {
session->api().applyUpdates(result);
formDone(Payments::CheckoutResult::Paid, &result);
}).fail([=](const MTP::Error &error) {
formDone(Payments::CheckoutResult::Failed, nullptr);
if (const auto strong = weak.get()) {
strong->showToast(error.type());
}
formDone(Payments::CheckoutResult::Failed, nullptr);
}).send();
return;
}
Ui::RequestStarsFormAndSubmit(
window,
MTP_inputInvoiceStarGiftTransfer(
MTP_int(messageId.bare),
to->asUser()->inputUser),
Api::InputSavedStarGiftId(savedId),
to->input),
std::move(formDone));
}
@@ -327,7 +467,8 @@ void ShowTransferToBox(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer,
std::shared_ptr<Data::UniqueGift> gift,
MsgId msgId) {
Data::SavedStarGiftId savedId,
Fn<void()> closeParentBox) {
const auto stars = gift->starsForTransfer;
controller->show(Box([=](not_null<Ui::GenericBox*> box) {
box->setTitle(tr::lng_gift_transfer_title(
@@ -353,16 +494,24 @@ void ShowTransferToBox(
state->sent = true;
const auto weak = Ui::MakeWeak(box);
const auto done = [=](Payments::CheckoutResult result) {
if (result != Payments::CheckoutResult::Paid) {
if (result == Payments::CheckoutResult::Cancelled) {
closeParentBox();
if (const auto strong = weak.data()) {
strong->closeBox();
}
} else if (result != Payments::CheckoutResult::Paid) {
state->sent = false;
} else {
controller->showPeerHistory(peer);
if (savedId.isUser()) {
controller->showPeerHistory(peer);
}
closeParentBox();
if (const auto strong = weak.data()) {
strong->closeBox();
}
}
};
TransferGift(controller, peer, gift, msgId, done);
TransferGift(controller, peer, gift, savedId, done);
};
Ui::ConfirmBox(box, {
@@ -395,15 +544,18 @@ void ShowTransferToBox(
void ShowTransferGiftBox(
not_null<Window::SessionController*> window,
std::shared_ptr<Data::UniqueGift> gift,
MsgId msgId) {
Data::SavedStarGiftId savedId) {
auto controller = std::make_unique<Controller>(
window,
gift,
[=](not_null<PeerData*> peer) {
ShowTransferToBox(window, peer, gift, msgId);
savedId,
[=](not_null<PeerData*> peer, Fn<void()> done) {
ShowTransferToBox(window, peer, gift, savedId, done);
});
const auto controllerRaw = controller.get();
auto initBox = [=](not_null<PeerListBox*> box) {
controllerRaw->init(box);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
box->noSearchSubmits() | rpl::start_with_next([=] {
@@ -13,9 +13,10 @@ class SessionController;
namespace Data {
struct UniqueGift;
class SavedStarGiftId;
} // namespace Data
void ShowTransferGiftBox(
not_null<Window::SessionController*> window,
std::shared_ptr<Data::UniqueGift> gift,
MsgId msgId);
Data::SavedStarGiftId savedId);
+13
View File
@@ -1441,6 +1441,19 @@ groupCallScheduleTimeField: InputField(groupCallScheduleDateField) {
placeholderFont: font(14px);
}
groupCallVolumeSettings: Menu(groupCallPopupVolumeMenu) {
widthMin: 210px;
itemBg: groupCallMembersBg;
itemBgOver: groupCallMembersBgOver;
}
groupCallVolumeSettingsPadding: margins(24px, 8px, 24px, 6px);
groupCallVolumeSettingsSlider: MediaSlider(groupCallMenuVolumeSlider) {
activeFg: groupCallMenuBg;
inactiveFg: groupCallMenuBg;
activeFgOver: groupCallMenuBg;
inactiveFgOver: groupCallMenuBg;
}
//
groupCallCalendarPreviousDisabled: icon {{ "calendar_down-flip_vertical", groupCallMemberNotJoinedStatus }};
groupCallCalendarNextDisabled: icon {{ "calendar_down", groupCallMemberNotJoinedStatus }};
@@ -578,8 +578,7 @@ void Instance::handleCallUpdate(
if (inCall()
&& _currentCall->type() == Call::Type::Outgoing
&& _currentCall->user()->id == session->userPeerId()
&& (peerFromUser(phoneCall.vparticipant_id())
== _currentCall->user()->session().userPeerId())) {
&& (user->id == _currentCall->user()->session().userPeerId())) {
// Ignore call from the same running app, other account.
return;
}
+130 -9
View File
@@ -42,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/emoji_config.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/integration.h"
#include "core/application.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
@@ -65,6 +66,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Calls {
namespace {
constexpr auto kHideControlsTimeout = 5 * crl::time(1000);
constexpr auto kHideControlsQuickTimeout = 2 * crl::time(1000);
[[nodiscard]] QByteArray BatterySvg(
const QSize &s,
const QColor &c) {
@@ -118,7 +122,9 @@ Panel::Panel(not_null<Call*> call)
st::callMicrophoneMute,
&st::callMicrophoneUnmute))
, _name(widget(), st::callName)
, _status(widget(), st::callStatus) {
, _status(widget(), st::callStatus)
, _hideControlsTimer([=] { requestControlsHidden(true); })
, _controlsShownForceTimer([=] { controlsShownForce(false); }) {
_layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox);
_layerBg->setHideByBackgroundClick(true);
@@ -188,6 +194,25 @@ void Panel::initWindow() {
&& window()->isFullScreen()) {
window()->showNormal();
}
} else if (e->type() == QEvent::WindowStateChange) {
const auto state = window()->windowState();
_fullScreenOrMaximized = (state & Qt::WindowFullScreen)
|| (state & Qt::WindowMaximized);
} else if (e->type() == QEvent::Enter) {
_mouseInside = true;
Ui::Integration::Instance().registerLeaveSubscription(
window().get());
if (!_fullScreenOrMaximized.current()) {
requestControlsHidden(false);
_hideControlsTimer.cancel();
}
} else if (e->type() == QEvent::Leave) {
_mouseInside = false;
Ui::Integration::Instance().unregisterLeaveSubscription(
window().get());
if (!_fullScreenOrMaximized.current()) {
_hideControlsTimer.callOnce(kHideControlsQuickTimeout);
}
}
return base::EventFilterResult::Continue;
});
@@ -415,7 +440,11 @@ void Panel::refreshIncomingGeometry() {
void Panel::reinitWithCall(Call *call) {
_callLifetime.destroy();
_call = call;
const auto guard = gsl::finally([&] {
updateControlsShown();
});
if (!_call) {
_fingerprint.destroy();
_incoming = nullptr;
_outgoingVideoBubble = nullptr;
_powerSaveBlocker = nullptr;
@@ -457,6 +486,51 @@ void Panel::reinitWithCall(Call *call) {
_window.backend());
_incoming->widget()->hide();
_incoming->rp()->shownValue() | rpl::start_with_next([=] {
updateControlsShown();
}, _incoming->rp()->lifetime());
_hideControlsFilter = nullptr;
_fullScreenOrMaximized.value(
) | rpl::start_with_next([=](bool fullScreenOrMaximized) {
if (fullScreenOrMaximized) {
class Filter final : public QObject {
public:
explicit Filter(Fn<void(QObject*)> moved) : _moved(moved) {
qApp->installEventFilter(this);
}
bool eventFilter(QObject *watched, QEvent *event) {
if (event->type() == QEvent::MouseMove) {
_moved(watched);
}
return false;
}
private:
Fn<void(QObject*)> _moved;
};
_hideControlsFilter.reset(new Filter([=](QObject *what) {
_mouseInside = true;
if (what->isWidgetType()
&& window()->isAncestorOf(static_cast<QWidget*>(what))) {
_hideControlsTimer.callOnce(kHideControlsTimeout);
requestControlsHidden(false);
updateControlsShown();
}
}));
_hideControlsTimer.callOnce(kHideControlsTimeout);
} else {
_hideControlsFilter = nullptr;
_hideControlsTimer.cancel();
if (_mouseInside) {
requestControlsHidden(false);
updateControlsShown();
}
}
}, _incoming->rp()->lifetime());
_call->mutedValue(
) | rpl::start_with_next([=](bool mute) {
_mute->entity()->setProgress(mute ? 1. : 0.);
@@ -603,6 +677,8 @@ void Panel::createRemoteAudioMute() {
const auto r = _remoteAudioMute->rect();
auto hq = PainterHighQualityEnabler(p);
p.setOpacity(_controlsShownAnimation.value(
_controlsShown ? 1. : 0.));
p.setBrush(st::videoPlayIconBg);
p.setPen(Qt::NoPen);
p.drawRoundedRect(r, r.height() / 2, r.height() / 2);
@@ -661,6 +737,8 @@ void Panel::createRemoteLowBattery() {
const auto r = _remoteLowBattery->rect();
auto hq = PainterHighQualityEnabler(p);
p.setOpacity(_controlsShownAnimation.value(
_controlsShown ? 1. : 0.));
p.setBrush(st::videoPlayIconBg);
p.setPen(Qt::NoPen);
p.drawRoundedRect(r, r.height() / 2, r.height() / 2);
@@ -782,6 +860,9 @@ void Panel::showDevicesMenu(
}
Core::App().saveSettingsDelayed();
};
controlsShownForce(true);
updateControlsShown();
_devicesMenu = MakeDeviceSelectionMenu(
widget(),
&Core::App().mediaDevices(),
@@ -791,6 +872,9 @@ void Panel::showDevicesMenu(
Ui::PopupMenu::VerticalOrigin::Bottom);
_devicesMenu->popup(button->mapToGlobal(QPoint())
- QPoint(st::callDeviceSelectionMenu.menu.widthMin / 2, 0));
QObject::connect(_devicesMenu.get(), &QObject::destroyed, window(), [=] {
_controlsShownForceTimer.callOnce(kHideControlsQuickTimeout);
});
}
void Panel::refreshOutgoingPreviewInBody(State state) {
@@ -823,6 +907,33 @@ QRect Panel::outgoingFrameGeometry() const {
return _outgoingVideoBubble->geometry();
}
void Panel::requestControlsHidden(bool hidden) {
_hideControlsRequested = hidden;
updateControlsShown();
}
void Panel::controlsShownForce(bool shown) {
_controlsShownForce = shown;
if (shown) {
_controlsShownForceTimer.cancel();
}
updateControlsShown();
}
void Panel::updateControlsShown() {
const auto shown = !_incoming
|| _incoming->widget()->isHidden()
|| _controlsShownForce
|| !_hideControlsRequested;
if (_controlsShown != shown) {
_controlsShown = shown;
_controlsShownAnimation.start([=] {
updateControlsGeometry();
}, shown ? 0. : 1., shown ? 1. : 0., st::slideDuration);
updateControlsGeometry();
}
}
void Panel::updateControlsGeometry() {
if (widget()->size().isEmpty()) {
return;
@@ -830,6 +941,8 @@ void Panel::updateControlsGeometry() {
if (_incoming) {
refreshIncomingGeometry();
}
const auto shown = _controlsShownAnimation.value(
_controlsShown ? 1. : 0.);
if (_fingerprint) {
#ifndef Q_OS_MAC
const auto controlsGeometry = _controls->controls.geometry();
@@ -848,14 +961,14 @@ void Panel::updateControlsGeometry() {
const auto minRight = 0;
#endif // _controls
const auto desired = (widget()->width() - _fingerprint->width()) / 2;
const auto top = anim::interpolate(
-_fingerprint->height(),
st::callFingerprintTop,
shown);
if (minLeft) {
_fingerprint->moveToLeft(
std::max(desired, minLeft),
st::callFingerprintTop);
_fingerprint->moveToLeft(std::max(desired, minLeft), top);
} else {
_fingerprint->moveToRight(
std::max(desired, minRight),
st::callFingerprintTop);
_fingerprint->moveToRight(std::max(desired, minRight), top);
}
}
const auto innerHeight = std::max(widget()->height(), st::callHeightMin);
@@ -885,7 +998,11 @@ void Panel::updateControlsGeometry() {
/ (_outgoingPreviewInBody ? 3 : 2);
_bodyTop = availableTop + skipHeight;
_buttonsTop = availableTop + available;
_buttonsTopShown = availableTop + available;
_buttonsTop = anim::interpolate(
widget()->height(),
_buttonsTopShown,
shown);
const auto previewTop = _bodyTop + _bodySt->height + skipHeight;
_userpic->setGeometry(
@@ -908,6 +1025,8 @@ void Panel::updateControlsGeometry() {
(_buttonsTop
- st::callRemoteAudioMuteSkip
- _remoteAudioMute->height()));
_remoteAudioMute->update();
_remoteAudioMute->entity()->setOpacity(shown);
}
if (_remoteLowBattery) {
_remoteLowBattery->moveToLeft(
@@ -915,6 +1034,8 @@ void Panel::updateControlsGeometry() {
(_buttonsTop
- st::callRemoteAudioMuteSkip
- _remoteLowBattery->height()));
_remoteLowBattery->update();
_remoteLowBattery->entity()->setOpacity(shown);
}
if (_outgoingPreviewInBody) {
@@ -925,7 +1046,7 @@ void Panel::updateControlsGeometry() {
previewTop,
bodyPreviewSize.width(),
bodyPreviewSize.height()));
} else {
} else if (_outgoingVideoBubble) {
updateOutgoingVideoBubbleGeometry();
}
+14
View File
@@ -111,6 +111,9 @@ private:
[[nodiscard]] bool handleClose() const;
void requestControlsHidden(bool hidden);
void controlsShownForce(bool shown);
void updateControlsShown();
void updateControlsGeometry();
void updateHangupGeometry();
void updateStatusGeometry();
@@ -177,8 +180,19 @@ private:
std::unique_ptr<VideoBubble> _outgoingVideoBubble;
QPixmap _bottomShadow;
int _bodyTop = 0;
int _buttonsTopShown = 0;
int _buttonsTop = 0;
base::Timer _hideControlsTimer;
base::Timer _controlsShownForceTimer;
std::unique_ptr<QObject> _hideControlsFilter;
bool _hideControlsRequested = false;
rpl::variable<bool> _fullScreenOrMaximized;
Ui::Animations::Simple _controlsShownAnimation;
bool _controlsShownForce = false;
bool _controlsShown = true;
bool _mouseInside = false;
base::unique_qptr<Ui::PopupMenu> _devicesMenu;
base::Timer _updateDurationTimer;
@@ -1431,10 +1431,12 @@ void Members::Controller::addMuteActionsToContextMenu(
auto volumeItem = base::make_unique_q<MenuVolumeItem>(
menu->menu(),
st::groupCallPopupVolumeMenu,
st::groupCallMenuVolumeSlider,
otherParticipantStateValue,
_call->rtmp() ? _call->rtmpVolume() : row->volume(),
Group::kMaxVolume,
muted);
muted,
st::groupCallMenuVolumePadding);
mutesFromVolume = volumeItem->toggleMuteRequests();
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/group/calls_group_menu.h" // LeaveBox.
#include "calls/group/calls_group_common.h"
#include "calls/group/calls_choose_join_as.h"
#include "calls/group/calls_volume_item.h"
#include "calls/calls_instance.h"
#include "ui/widgets/level_meter.h"
#include "ui/widgets/continuous_sliders.h"
@@ -44,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "webrtc/webrtc_audio_input_tester.h"
#include "webrtc/webrtc_device_resolver.h"
#include "settings/settings_calls.h"
#include "settings/settings_credits_graphics.h"
#include "main/main_session.h"
#include "apiwrap.h"
#include "api/api_invite_links.h"
@@ -183,22 +185,7 @@ object_ptr<ShareBox> ShareInviteLinkBox(
return Data::CanSend(thread, ChatRestriction::SendOther);
};
const auto scheduleStyle = [&] {
auto date = Ui::ChooseDateTimeStyleArgs();
date.labelStyle = &st::groupCallBoxLabel;
date.dateFieldStyle = &st::groupCallScheduleDateField;
date.timeFieldStyle = &st::groupCallScheduleTimeField;
date.separatorStyle = &st::callMuteButtonLabel;
date.atStyle = &st::callMuteButtonLabel;
date.calendarStyle = &st::groupCallCalendarColors;
auto st = HistoryView::ScheduleBoxStyleArgs();
st.topButtonStyle = &st::groupCallMenuToggle;
st.popupMenuStyle = &st::groupCallPopupMenu;
st.chooseDateTimeArgs = std::move(date);
return st;
};
const auto st = ::Settings::DarkCreditsEntryBoxStyle();
auto result = Box<ShareBox>(ShareBox::Descriptor{
.session = &peer->session(),
.copyCallback = std::move(copyCallback),
@@ -211,11 +198,7 @@ object_ptr<ShareBox> ShareInviteLinkBox(
: rpl::single(false)),
tr::lng_group_call_copy_speaker_link(),
tr::lng_group_call_copy_listener_link()),
.stMultiSelect = &st::groupCallMultiSelect,
.stComment = &st::groupCallShareBoxComment,
.st = &st::groupCallShareBoxList,
.stLabel = &st::groupCallField,
.scheduleBoxStyle = scheduleStyle(),
.st = st.shareBox ? *st.shareBox : ShareBoxStyleOverrides(),
.premiumRequiredError = SharePremiumRequiredError(),
});
*box = result.data();
@@ -731,6 +714,50 @@ void SettingsBox(
addDivider();
Ui::AddSkip(layout);
}
if (rtmp) {
const auto volumeItem = layout->add(
object_ptr<MenuVolumeItem>(
layout,
st::groupCallVolumeSettings,
st::groupCallVolumeSettingsSlider,
call->otherParticipantStateValue(
) | rpl::filter([=](const Group::ParticipantState &data) {
return data.peer == peer;
}),
call->rtmpVolume(),
Group::kMaxVolume,
false,
st::groupCallVolumeSettingsPadding));
const auto toggleMute = crl::guard(layout, [=](bool m, bool local) {
if (call) {
call->toggleMute({
.peer = peer,
.mute = m,
.locallyOnly = local,
});
}
});
const auto changeVolume = crl::guard(layout, [=](int v, bool local) {
if (call) {
call->changeVolume({
.peer = peer,
.volume = std::clamp(v, 1, Group::kMaxVolume),
.locallyOnly = local,
});
}
});
volumeItem->toggleMuteLocallyRequests(
) | rpl::start_with_next([=](bool muted) {
toggleMute(muted, true);
}, volumeItem->lifetime());
volumeItem->changeVolumeLocallyRequests(
) | rpl::start_with_next([=](int volume) {
changeVolume(volume, true);
}, volumeItem->lifetime());
}
if (peer->canManageGroupCall()) {
layout->add(object_ptr<Ui::SettingsButton>(
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animation_value.h"
#include "ui/effects/cross_line.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/rect.h"
#include "styles/style_calls.h"
#include "ui/paint/arcs.h"
@@ -41,20 +42,21 @@ constexpr auto kVolumeStickedValues
MenuVolumeItem::MenuVolumeItem(
not_null<RpWidget*> parent,
const style::Menu &st,
const style::MediaSlider &stSlider,
rpl::producer<Group::ParticipantState> participantState,
int startVolume,
int maxVolume,
bool muted)
bool muted,
const QMargins &padding)
: Ui::Menu::ItemBase(parent, st)
, _maxVolume(maxVolume)
, _cloudMuted(muted)
, _localMuted(muted)
, _slider(base::make_unique_q<Ui::MediaSlider>(
this,
st::groupCallMenuVolumeSlider))
, _slider(base::make_unique_q<Ui::MediaSlider>(this, stSlider))
, _dummyAction(new QAction(parent))
, _st(st)
, _stCross(st::groupCallMuteCrossLine)
, _padding(padding)
, _crossLineMute(std::make_unique<Ui::CrossLineAnimation>(_stCross, true))
, _arcs(std::make_unique<Ui::Paint::ArcsAnimation>(
st::groupCallSpeakerArcsAnimation,
@@ -71,7 +73,7 @@ MenuVolumeItem::MenuVolumeItem(
sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
const auto geometry = QRect(QPoint(), size);
_itemRect = geometry - st::groupCallMenuVolumePadding;
_itemRect = geometry - _padding;
_speakerRect = QRect(_itemRect.topLeft(), _stCross.icon.size());
_arcPosition = _speakerRect.center()
+ QPoint(0, st::groupCallMenuSpeakerArcsSkip);
@@ -280,9 +282,7 @@ bool MenuVolumeItem::isEnabled() const {
}
int MenuVolumeItem::contentHeight() const {
return st::groupCallMenuVolumePadding.top()
+ st::groupCallMenuVolumePadding.bottom()
+ _stCross.icon.height();
return rect::m::sum::v(_padding) + _stCross.icon.height();
}
rpl::producer<bool> MenuVolumeItem::toggleMuteRequests() const {
@@ -31,10 +31,12 @@ public:
MenuVolumeItem(
not_null<RpWidget*> parent,
const style::Menu &st,
const style::MediaSlider &stSlider,
rpl::producer<Group::ParticipantState> participantState,
int startVolume,
int maxVolume,
bool muted);
bool muted,
const QMargins &padding);
not_null<QAction*> action() const override;
bool isEnabled() const override;
@@ -71,6 +73,7 @@ private:
const not_null<QAction*> _dummyAction;
const style::Menu &_st;
const style::CrossLineAnimation &_stCross;
const QMargins &_padding;
const std::unique_ptr<Ui::CrossLineAnimation> _crossLineMute;
Ui::Animations::Simple _crossLineAnimation;
@@ -40,6 +40,7 @@ TabbedSearch {
ComposeIcons {
settings: icon;
collectibles: icon;
recent: icon;
recentActive: icon;
@@ -587,6 +588,7 @@ sendBoxAlbumGroupButtonMediaDelete: icon {{ "send_media/send_media_delete", roun
defaultComposeIcons: ComposeIcons {
settings: icon {{ "emoji/emoji_settings", emojiIconFg }};
collectibles: icon {{ "menu/unique", emojiIconFg }};
recent: icon {{ "emoji/emoji_recent", emojiIconFg }};
recentActive: icon {{ "emoji/emoji_recent", emojiSubIconFgActive }};
@@ -853,6 +855,17 @@ historyComposeButton: FlatButton {
color: historyComposeButtonBgRipple;
}
}
historyGiftToChannel: IconButton(defaultIconButton) {
width: 46px;
height: 46px;
icon: icon{{ "menu/gift_premium", windowActiveTextFg }};
iconOver: icon{{ "menu/gift_premium", windowActiveTextFg }};
rippleAreaPosition: point(3px, 3px);
rippleAreaSize: 40px;
ripple: universalRippleAnimation;
}
historyUnblock: FlatButton(historyComposeButton) {
color: attentionButtonFg;
overColor: attentionButtonFgOver;
@@ -18,6 +18,7 @@ struct ComposeFeatures {
bool attachBotsMenu : 1 = true;
bool inlineBots : 1 = true;
bool megagroupSet : 1 = true;
bool collectibleStatus : 1 = false;
bool stickersSettings : 1 = true;
bool openStickerSets : 1 = true;
bool autocompleteHashtags : 1 = true;
@@ -19,7 +19,7 @@ rpl::producer<bool> Show::adjustShadowLeft() const {
}
ResolveWindow ResolveWindowDefault() {
return [](not_null<Main::Session*> session, WindowUsage usage)
return [](not_null<Main::Session*> session)
-> Window::SessionController* {
const auto check = [&](Window::Controller *window) {
if (const auto controller = window->sessionController()) {
@@ -45,8 +45,8 @@ ResolveWindow ResolveWindowDefault() {
};
}
Window::SessionController *Show::resolveWindow(WindowUsage usage) const {
return ResolveWindowDefault()(&session(), usage);
Window::SessionController *Show::resolveWindow() const {
return ResolveWindowDefault()(&session());
}
} // namespace ChatHelpers
@@ -40,13 +40,8 @@ enum class PauseReason {
using PauseReasons = base::flags<PauseReason>;
inline constexpr bool is_flag_type(PauseReason) { return true; };
enum class WindowUsage {
PremiumPromo,
};
using ResolveWindow = Fn<Window::SessionController*(
not_null<Main::Session*>,
WindowUsage)>;
not_null<Main::Session*>)>;
[[nodiscard]] ResolveWindow ResolveWindowDefault();
class Show : public Main::SessionShow {
@@ -68,8 +63,7 @@ public:
virtual void processChosenSticker(FileChosen &&chosen) const = 0;
[[nodiscard]] virtual Window::SessionController *resolveWindow(
WindowUsage) const;
[[nodiscard]] virtual Window::SessionController *resolveWindow() const;
};
} // namespace ChatHelpers
@@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/sticker_set_box.h"
#include "lang/lang_keys.h"
#include "layout/layout_position.h"
#include "data/data_emoji_statuses.h"
#include "data/data_session.h"
#include "data/data_changes.h"
#include "data/data_channel.h"
@@ -134,6 +135,7 @@ struct EmojiListWidget::CustomEmojiInstance {
};
struct EmojiListWidget::RecentOne {
std::shared_ptr<Data::EmojiStatusCollectible> collectible;
Ui::Text::CustomEmoji *custom = nullptr;
RecentEmojiId id;
mutable QImage premiumLock;
@@ -447,6 +449,13 @@ void EmojiColorPicker::drawVariant(QPainter &p, int variant) {
w.y() + _innerPosition.y());
}
std::vector<EmojiStatusId> DocumentListToRecent(
const std::vector<DocumentId> &documents) {
return documents | ranges::views::transform([](DocumentId id) {
return EmojiStatusId{ .documentId = id };
}) | ranges::to_vector;
}
EmojiListWidget::EmojiListWidget(
QWidget *parent,
not_null<Window::SessionController*> controller,
@@ -510,6 +519,11 @@ EmojiListWidget::EmojiListWidget(
refreshCustom();
}
}, lifetime());
} else if (_mode == Mode::EmojiStatus && _features.collectibleStatus) {
session().data().emojiStatuses().collectiblesUpdates(
) | rpl::start_with_next([=] {
refreshCustom();
}, lifetime());
}
_customSingleSize = Data::FrameSizeFromTag(
@@ -614,6 +628,13 @@ void EmojiListWidget::applyNextSearchQuery() {
const auto modeChanged = (_searchMode != searching);
clearSelection();
if (modeChanged) {
if (_picker) {
_picker->hideAnimated();
}
_colorAllRipple = nullptr;
for (auto &set : _custom) {
set.ripple = nullptr;
}
_searchMode = searching;
}
if (!searching) {
@@ -656,7 +677,8 @@ void EmojiListWidget::applyNextSearchQuery() {
void EmojiListWidget::showPreview() {
if (const auto over = std::get_if<OverEmoji>(&_pressed)) {
if (const auto custom = lookupCustomEmoji(over)) {
_show->showMediaPreview(custom->stickerSetOrigin(), custom);
const auto document = custom.document;
_show->showMediaPreview(document->stickerSetOrigin(), document);
_previewShown = true;
}
}
@@ -706,7 +728,7 @@ void EmojiListWidget::appendPremiumSearchResults() {
}
void EmojiListWidget::provideRecent(
const std::vector<DocumentId> &customRecentList) {
const std::vector<EmojiStatusId> &customRecentList) {
clearSelection();
fillRecentFrom(customRecentList);
resizeToWidth(width());
@@ -1094,7 +1116,8 @@ void EmojiListWidget::fillRecent() {
}
}
void EmojiListWidget::fillRecentFrom(const std::vector<DocumentId> &list) {
void EmojiListWidget::fillRecentFrom(
const std::vector<EmojiStatusId> &list) {
const auto test = session().isTestMode();
_recent.clear();
_recent.reserve(list.size());
@@ -1114,10 +1137,15 @@ void EmojiListWidget::fillRecentFrom(const std::vector<DocumentId> &list) {
_recentCustomIds.emplace(fakeId);
} else {
_recent.push_back({
.collectible = id.collectible,
.custom = resolveCustomRecent(id),
.id = { RecentEmojiDocument{ .id = id, .test = test } },
.id = {
RecentEmojiDocument{ .id = id.documentId, .test = test },
},
});
_recentCustomIds.emplace(id);
_recentCustomIds.emplace(id.collectible
? id.collectible->documentId
: id.documentId);
}
}
}
@@ -1158,8 +1186,12 @@ void EmojiListWidget::fillRecentMenu(
const auto over = OverEmoji{ section, index };
const auto emoji = lookupOverEmoji(&over);
const auto custom = lookupCustomEmoji(&over);
if (custom && custom->sticker()) {
const auto sticker = custom->sticker();
if (custom.collectible) {
return;
}
const auto document = custom.document;
if (document && document->sticker()) {
const auto sticker = document->sticker();
const auto emoji = sticker->alt;
const auto setId = sticker->set.id;
if (!emoji.isEmpty()) {
@@ -1168,7 +1200,7 @@ void EmojiListWidget::fillRecentMenu(
EntityType::CustomEmoji,
0,
int(emoji.size()),
Data::SerializeCustomEmojiId(custom)
Data::SerializeCustomEmojiId(document)
});
addAction(tr::lng_emoji_copy(tr::now), [=] {
TextUtilities::SetClipboardText(data);
@@ -1192,8 +1224,8 @@ void EmojiListWidget::fillRecentMenu(
auto id = RecentEmojiId{ emoji };
if (custom) {
id.data = RecentEmojiDocument{
.id = custom->id,
.test = custom->session().isTestMode(),
.id = custom.document->id,
.test = custom.document->session().isTestMode(),
};
}
addAction(tr::lng_emoji_remove_recent(tr::now), crl::guard(this, [=] {
@@ -1229,7 +1261,7 @@ void EmojiListWidget::fillEmojiStatusMenu(
int section,
int index) {
const auto chosen = lookupCustomEmoji(index, section);
if (!chosen) {
if (!chosen || chosen.collectible) {
return;
}
const auto selectWith = [=](TimeId scheduled) {
@@ -1573,12 +1605,14 @@ bool EmojiListWidget::checkPickerHide() {
return false;
}
DocumentData *EmojiListWidget::lookupCustomEmoji(
EmojiListWidget::ResolvedCustom EmojiListWidget::lookupCustomEmoji(
const OverEmoji *over) const {
return over ? lookupCustomEmoji(over->index, over->section) : nullptr;
return over
? lookupCustomEmoji(over->index, over->section)
: ResolvedCustom();
}
DocumentData *EmojiListWidget::lookupCustomEmoji(
EmojiListWidget::ResolvedCustom EmojiListWidget::lookupCustomEmoji(
int index,
int section) const {
if (_searchMode) {
@@ -1586,22 +1620,30 @@ DocumentData *EmojiListWidget::lookupCustomEmoji(
const auto document = std::get_if<RecentEmojiDocument>(
&_searchResults[index].id.data);
if (document) {
return session().data().document(document->id);
return { session().data().document(document->id) };
}
}
return nullptr;
return {};
} else if (section == int(Section::Recent) && index < _recent.size()) {
const auto &recent = _recent[index];
if (recent.collectible) {
return {
session().data().document(recent.collectible->documentId),
recent.collectible,
};
}
const auto document = std::get_if<RecentEmojiDocument>(
&_recent[index].id.data);
&recent.id.data);
if (document) {
return session().data().document(document->id);
return { session().data().document(document->id) };
}
} else if (section >= _staticCount
&& index < _custom[section - _staticCount].list.size()) {
auto &set = _custom[section - _staticCount];
return set.list[index].document;
auto &entry = set.list[index];
return { entry.document, entry.collectible };
}
return nullptr;
return {};
}
EmojiPtr EmojiListWidget::lookupOverEmoji(const OverEmoji *over) const {
@@ -1643,9 +1685,11 @@ EmojiChosen EmojiListWidget::lookupChosen(
}
FileChosen EmojiListWidget::lookupChosen(
not_null<DocumentData*> custom,
ResolvedCustom custom,
const OverEmoji *over,
Api::SendOptions options) {
Expects(custom.document != nullptr);
_grabbingChosen = true;
const auto guard = gsl::finally([&] { _grabbingChosen = false; });
const auto rect = over ? emojiRect(over->section, over->index) : QRect();
@@ -1655,13 +1699,14 @@ FileChosen EmojiListWidget::lookupChosen(
) : QRect();
return {
.document = custom,
.document = custom.document,
.options = options,
.messageSendingFrom = {
.type = Ui::MessageSendingAnimationFrom::Type::Emoji,
.globalStartGeometry = over ? mapToGlobal(emoji) : QRect(),
.frame = over ? Ui::GrabWidgetToImage(this, emoji) : QImage(),
},
.collectible = custom.collectible,
};
}
@@ -1751,7 +1796,6 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
const auto id = hasColorButton(button->section)
? 0
: _custom[button->section - _staticCount].id;
const auto usage = ChatHelpers::WindowUsage::PremiumPromo;
if (hasColorButton(button->section)) {
_pickerSelected = pressed;
showPicker();
@@ -1759,7 +1803,7 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
removeSet(id);
} else if (hasAddButton(button->section)) {
_localSetsManager->install(id);
} else if (const auto resolved = _show->resolveWindow(usage)) {
} else if (const auto resolved = _show->resolveWindow()) {
_jumpedToPremium.fire({});
switch (_mode) {
case Mode::Full:
@@ -1792,6 +1836,8 @@ void EmojiListWidget::displaySet(uint64 setId) {
} else {
return;
}
} else if (setId == Data::Stickers::CollectibleSetId) {
return;
}
const auto &sets = session().data().stickers().sets();
auto it = sets.find(setId);
@@ -1830,6 +1876,7 @@ void EmojiListWidget::removeSet(uint64 setId) {
Assert(i != end(_custom));
const auto removeLocally = !_megagroupSet->canEditEmoji();
removeMegagroupSet(removeLocally);
} else if (setId == Data::Stickers::CollectibleSetId) {
} else if (auto box = MakeConfirmRemoveSetBox(&session(), labelSt, setId)) {
checkHideWithBox(std::move(box));
}
@@ -1933,6 +1980,8 @@ bool EmojiListWidget::hasRemoveButton(int index) const {
return true;
}
return !set.list.empty() && _megagroupSet->canEditEmoji();
} else if (set.id == Data::Stickers::CollectibleSetId) {
return false;
}
return set.canRemove && !set.premiumRequired;
}
@@ -1962,7 +2011,8 @@ bool EmojiListWidget::hasAddButton(int index) const {
const auto &set = _custom[index - _staticCount];
return !set.canRemove
&& !set.premiumRequired
&& set.id != Data::Stickers::MegagroupSetId;
&& set.id != Data::Stickers::MegagroupSetId
&& set.id != Data::Stickers::CollectibleSetId;
}
QRect EmojiListWidget::addButtonRect(int index) const {
@@ -1991,8 +2041,9 @@ bool EmojiListWidget::hasButton(int index) const {
} else if (index >= _staticCount
&& index < _staticCount + _custom.size()) {
const auto &custom = _custom[index - _staticCount];
return (custom.id != Data::Stickers::MegagroupSetId)
|| custom.canRemove;
return (custom.id != Data::Stickers::CollectibleSetId)
&& ((custom.id != Data::Stickers::MegagroupSetId)
|| custom.canRemove);
}
return false;
}
@@ -2020,6 +2071,7 @@ QRect EmojiListWidget::buttonRect(
auto EmojiListWidget::rightButton(int index) const -> const RightButton & {
Expects(index >= _staticCount
&& index < _staticCount + _custom.size());
return hasAddButton(index)
? _add
: _custom[index - _staticCount].canRemove
@@ -2279,11 +2331,12 @@ void EmojiListWidget::refreshCustom() {
auto set = std::vector<CustomOne>();
set.reserve(list.size());
for (const auto document : list) {
if (_restrictedCustomList.contains(document->id)) {
const auto id = EmojiStatusId{ document->id };
if (_restrictedCustomList.contains(id.documentId)) {
continue;
} else if (const auto sticker = document->sticker()) {
set.push_back({
.custom = resolveCustomEmoji(document, lookupId),
.custom = resolveCustomEmoji(id, document, lookupId),
.document = document,
.emoji = Ui::Emoji::Find(sticker->alt),
});
@@ -2305,6 +2358,7 @@ void EmojiListWidget::refreshCustom() {
.premiumRequired = premium && premiumMayBeBought,
});
};
refreshEmojiStatusCollectibles();
refreshMegagroupStickers(push, GroupStickersPlace::Visible);
for (const auto setId : owner->stickers().emojiSetsOrder()) {
push(setId, true);
@@ -2337,18 +2391,17 @@ Fn<void()> EmojiListWidget::repaintCallback(
}
not_null<Ui::Text::CustomEmoji*> EmojiListWidget::resolveCustomEmoji(
EmojiStatusId id,
not_null<DocumentData*> document,
uint64 setId) {
Expects(document->sticker() != nullptr);
const auto documentId = document->id;
const auto i = _customEmoji.find(documentId);
const auto i = _customEmoji.find(id);
const auto recentOnly = (i != end(_customEmoji)) && i->second.recentOnly;
if (i != end(_customEmoji) && !recentOnly) {
return i->second.emoji.get();
}
auto instance = document->owner().customEmojiManager().create(
document,
Data::EmojiStatusCustomId(id),
repaintCallback(documentId, setId),
Data::CustomEmojiManager::SizeTag::Large);
if (recentOnly) {
@@ -2362,7 +2415,7 @@ not_null<Ui::Text::CustomEmoji*> EmojiListWidget::resolveCustomEmoji(
return i->second.emoji.get();
}
return _customEmoji.emplace(
documentId,
id,
CustomEmojiInstance{ .emoji = std::move(instance) }
).first->second.emoji.get();
}
@@ -2380,31 +2433,78 @@ Ui::Text::CustomEmoji *EmojiListWidget::resolveCustomRecent(
not_null<Ui::Text::CustomEmoji*> EmojiListWidget::resolveCustomRecent(
DocumentId documentId) {
const auto i = _customRecent.find(documentId);
return resolveCustomRecent(EmojiStatusId{ documentId });
}
not_null<Ui::Text::CustomEmoji*> EmojiListWidget::resolveCustomRecent(
EmojiStatusId id) {
const auto i = id.collectible
? end(_customRecent)
: _customRecent.find(id.documentId);
if (i != end(_customRecent)) {
return i->second.get();
}
const auto j = _customEmoji.find(documentId);
const auto j = _customEmoji.find(id);
if (j != end(_customEmoji)) {
return j->second.emoji.get();
}
const auto documentId = id.collectible
? id.collectible->documentId
: id.documentId;
auto repaint = repaintCallback(documentId, RecentEmojiSectionSetId());
if (_customRecentFactory) {
if (_customRecentFactory && !id.collectible) {
return _customRecent.emplace(
documentId,
_customRecentFactory(documentId, std::move(repaint))
id.documentId,
_customRecentFactory(id.documentId, std::move(repaint))
).first->second.get();
}
auto custom = session().data().customEmojiManager().create(
documentId,
Data::EmojiStatusCustomId(id),
std::move(repaint),
Data::CustomEmojiManager::SizeTag::Large);
return _customEmoji.emplace(
documentId,
id,
CustomEmojiInstance{ .emoji = std::move(custom), .recentOnly = true }
).first->second.emoji.get();
}
void EmojiListWidget::refreshEmojiStatusCollectibles() {
if (_mode != Mode::EmojiStatus || !_features.collectibleStatus) {
return;
}
const auto type = Data::EmojiStatuses::Type::Collectibles;
const auto &list = session().data().emojiStatuses().list(type);
const auto setId = Data::Stickers::CollectibleSetId;
auto set = std::vector<CustomOne>();
set.reserve(list.size());
for (const auto &status : list) {
const auto documentId = status.collectible
? status.collectible->documentId
: status.documentId;
const auto document = session().data().document(documentId);
const auto sticker = document->sticker();
set.push_back({
.collectible = status.collectible,
.custom = resolveCustomEmoji(status, document, setId),
.document = document,
.emoji = sticker ? Ui::Emoji::Find(sticker->alt) : nullptr,
});
}
if (set.empty()) {
return;
}
const auto collectibles = session().data().stickers().collectibleSet();
_custom.push_back({
.id = setId,
.set = collectibles,
.thumbnailDocument = nullptr,
.title = collectibles->title,
.list = std::move(set),
.canRemove = false,
.premiumRequired = !session().premium(),
});
}
void EmojiListWidget::refreshMegagroupStickers(
Fn<void(uint64 setId, bool installed)> push,
GroupStickersPlace place) {
@@ -2639,8 +2739,11 @@ void EmojiListWidget::setSelected(OverState newSelected) {
} else if (_previewShown && _pressed != _selected) {
if (const auto over = std::get_if<OverEmoji>(&_selected)) {
if (const auto custom = lookupCustomEmoji(over)) {
const auto document = custom.document;
_pressed = _selected;
_show->showMediaPreview(custom->stickerSetOrigin(), custom);
_show->showMediaPreview(
document->stickerSetOrigin(),
document);
}
}
}
@@ -83,12 +83,15 @@ enum class EmojiListMode {
MessageEffects,
};
[[nodiscard]] std::vector<EmojiStatusId> DocumentListToRecent(
const std::vector<DocumentId> &documents);
struct EmojiListDescriptor {
std::shared_ptr<Show> show;
EmojiListMode mode = EmojiListMode::Full;
Fn<QColor()> customTextColor;
Fn<bool()> paused;
std::vector<DocumentId> customRecentList;
std::vector<EmojiStatusId> customRecentList;
Fn<std::unique_ptr<Ui::Text::CustomEmoji>(
DocumentId,
Fn<void()>)> customRecentFactory;
@@ -137,7 +140,7 @@ public:
[[nodiscard]] rpl::producer<> jumpedToPremium() const;
[[nodiscard]] rpl::producer<> escapes() const;
void provideRecent(const std::vector<DocumentId> &customRecentList);
void provideRecent(const std::vector<EmojiStatusId> &customRecentList);
void prepareExpanding();
void paintExpanding(
@@ -186,6 +189,7 @@ private:
bool collapsed = false;
};
struct CustomOne {
std::shared_ptr<Data::EmojiStatusCollectible> collectible;
not_null<Ui::Text::CustomEmoji*> custom;
not_null<DocumentData*> document;
EmojiPtr emoji = nullptr;
@@ -253,6 +257,14 @@ private:
int finalHeight = 0;
bool expanding = false;
};
struct ResolvedCustom {
DocumentData *document = nullptr;
std::shared_ptr<Data::EmojiStatusCollectible> collectible;
explicit operator bool() const {
return document != nullptr;
}
};
template <typename Callback>
bool enumerateSections(Callback callback) const;
@@ -271,6 +283,7 @@ private:
Visible,
Hidden,
};
void refreshEmojiStatusCollectibles();
void refreshMegagroupStickers(
Fn<void(uint64 setId, bool installed)> push,
GroupStickersPlace place);
@@ -296,16 +309,16 @@ private:
int index);
[[nodiscard]] EmojiPtr lookupOverEmoji(const OverEmoji *over) const;
[[nodiscard]] DocumentData *lookupCustomEmoji(
[[nodiscard]] ResolvedCustom lookupCustomEmoji(
const OverEmoji *over) const;
[[nodiscard]] DocumentData *lookupCustomEmoji(
[[nodiscard]] ResolvedCustom lookupCustomEmoji(
int index,
int section) const;
[[nodiscard]] EmojiChosen lookupChosen(
EmojiPtr emoji,
not_null<const OverEmoji*> over);
[[nodiscard]] FileChosen lookupChosen(
not_null<DocumentData*> custom,
ResolvedCustom custom,
const OverEmoji *over,
Api::SendOptions options = Api::SendOptions());
void selectEmoji(EmojiChosen data);
@@ -370,14 +383,17 @@ private:
void repaintCustom(uint64 setId);
void fillRecent();
void fillRecentFrom(const std::vector<DocumentId> &list);
void fillRecentFrom(const std::vector<EmojiStatusId> &list);
[[nodiscard]] not_null<Ui::Text::CustomEmoji*> resolveCustomEmoji(
EmojiStatusId id,
not_null<DocumentData*> document,
uint64 setId);
[[nodiscard]] Ui::Text::CustomEmoji *resolveCustomRecent(
Core::RecentEmojiId customId);
[[nodiscard]] not_null<Ui::Text::CustomEmoji*> resolveCustomRecent(
DocumentId documentId);
[[nodiscard]] not_null<Ui::Text::CustomEmoji*> resolveCustomRecent(
EmojiStatusId id);
[[nodiscard]] Fn<void()> repaintCallback(
DocumentId documentId,
uint64 setId);
@@ -415,7 +431,7 @@ private:
QVector<EmojiPtr> _emoji[kEmojiSectionCount];
std::vector<CustomSet> _custom;
base::flat_set<DocumentId> _restrictedCustomList;
base::flat_map<DocumentId, CustomEmojiInstance> _customEmoji;
base::flat_map<EmojiStatusId, CustomEmojiInstance> _customEmoji;
base::flat_map<
DocumentId,
std::unique_ptr<Ui::Text::CustomEmoji>> _customRecent;

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