Compare commits

...

215 Commits

Author SHA1 Message Date
John Preston
6229c0020c Version 6.1.3.
- Fix dead keys / alt combinations / input methods issues.
2025-09-07 08:46:44 +04:00
23rd
8db1bf9052 Added ability to share link for monoforum of owned channels. 2025-09-07 08:46:44 +04:00
23rd
2740c5db23 Extracted URL protocol stripping to Ui::Text::StripUrlProtocol. 2025-09-07 08:46:44 +04:00
23rd
512e6de39b Fixed position of tag preview in chats filter settings.
Fixed #29750.
2025-09-07 08:46:44 +04:00
23rd
e3c328c7e3 Reverted "Removed ability to clear history from replies chat.".
This reverts commit 86daf2a9dc.
2025-09-07 08:46:44 +04:00
John Preston
9eb5ea599e Fix build with MSVC. 2025-09-06 19:09:21 +03:00
Ilya Fedin
929032d598 Add GStreamer H264, H265, AAC and VA-API plugins to snap 2025-09-06 20:05:21 +04:00
Ilya Fedin
09fb42291d Revert "Self-build openh264 in snap for lesser package size"
This reverts commit a8430d8ecf.
2025-09-06 20:05:21 +04:00
Ilya Fedin
e6665a8305 Fix playing audio files via webkitgtk in snap 2025-09-06 20:04:48 +04:00
John Preston
a785d83791 Don't cancel special KeyPress events (key = 0).
I hope this fixes #29747, #29745, #29744, #29743, #29741.
2025-09-06 08:39:51 +04:00
23rd
08a64f0dc0 Fixed color of lottie icon from transcribe voices while loading. 2025-09-04 13:05:37 +03:00
John Preston
0babad837d Remove legacy "quicklaunchicon" InnoSetup task. 2025-09-04 13:03:25 +04:00
John Preston
fb1f70f5de Version 6.1.2.
- Close selected chat in Ctrl+Tab chat switch by 'Q'.
- Auto-split pasted multiple lines to checklist task creation.
- Fix color picker in userpic builder.
2025-09-04 12:03:07 +04:00
John Preston
d4dd1b7ba0 Fix build with GCC. 2025-09-04 12:03:07 +04:00
John Preston
82c921d739 Fix build with Xcode. 2025-09-04 12:00:49 +04:00
John Preston
180a570f99 Handle robustly Ctrl+Q/Ctrl+Arrows in Ctrl+Tab chat switch. 2025-09-04 12:00:35 +04:00
John Preston
9960a7a7e4 Remove unneeded openssl/engine.h include. 2025-09-04 11:43:59 +04:00
John Preston
2164d08c3c Fixed a crash in gifts section closing.
Fixes #29737.
2025-09-04 11:28:21 +04:00
John Preston
4b2d0d1a67 Allow a bit more theme previews. 2025-09-02 21:36:08 +04:00
John Preston
d0341d191b Close selected chat in switch by Q. 2025-09-02 19:22:19 +04:00
John Preston
10237a2d90 Correct gift symbol sizes and rotations. 2025-09-02 17:28:38 +04:00
John Preston
59390ddaa1 Support pasting multi-line as multi-tasks. 2025-09-02 13:54:42 +04:00
John Preston
312c94f386 Fill suggested prices for gifts between stars/TON. 2025-09-02 12:04:47 +04:00
John Preston
d697677fab Fix time input colon. 2025-09-02 08:38:10 +04:00
John Preston
bf484b5518 Update lib_webview. 2025-09-02 08:33:15 +04:00
John Preston
74b530259d Fix color picker in userpic builder again. 2025-09-02 08:26:04 +04:00
John Preston
4052c3d101 Fix color picker in userpic builder. 2025-09-02 08:17:29 +04:00
John Preston
86262333a4 Version 6.1.1.
- Ctrl+Tab / Ctrl+Shift+Tab collect chats on every launch.
- Fix Ctrl+Tab / Ctrl+Shift+Tab shortcut override.
- Fix possible crashes in saved music.
- Fix webview error label alignment.
2025-09-01 17:53:59 +04:00
John Preston
3419b49381 Hide gift value details tooltips on click. 2025-09-01 17:52:15 +04:00
John Preston
ec5725d8de Fix finalizing intersecting quotes. 2025-09-01 17:34:27 +04:00
John Preston
9df98352a8 Show correct error on gift limit reaching. 2025-09-01 17:14:55 +04:00
John Preston
c0c5ad21b6 Don't push recent peers to chat switch. 2025-09-01 16:03:30 +04:00
John Preston
4a23da6ce1 Fix build with GCC. 2025-09-01 12:42:29 +04:00
John Preston
6d1e5243d8 Fix possible crash in recent chat switches. 2025-09-01 11:58:05 +04:00
John Preston
c52a31384e Fix couple of possible crashes in saved music. 2025-09-01 11:57:19 +04:00
John Preston
36447eb1e3 Try fixing macOS action build. 2025-09-01 11:28:52 +04:00
John Preston
811dfee42c Separately bought upgrade doesn't auto-add details. 2025-09-01 11:23:33 +04:00
John Preston
b075bb9f4e Fix checkbox in background settings. 2025-09-01 10:59:54 +04:00
John Preston
00919f3232 Support arrows in chat switch. 2025-09-01 10:27:55 +04:00
John Preston
6dcd52deb5 Support all userpic types in Switch. 2025-09-01 10:27:55 +04:00
John Preston
edc70e9acb Improve phrases for currency switching. 2025-09-01 10:27:55 +04:00
John Preston
4ad12b6862 Fix alignment of webview crashed error. 2025-09-01 10:27:55 +04:00
John Preston
d63f3b6da1 Fix ctrl+tab/ctrl+shift+tab as overriden shortcuts. 2025-09-01 10:27:55 +04:00
John Preston
84c70a0905 Suggest upgrading next gift. 2025-09-01 10:27:55 +04:00
GitHub Action
8f65a7ac19 Update User-Agent for DNS to Chrome 139.0.0.0. 2025-09-01 10:26:56 +04:00
dependabot[bot]
c78262be91 Bump endersonmenezes/free-disk-space from 2.1.0 to 2.1.1
Bumps [endersonmenezes/free-disk-space](https://github.com/endersonmenezes/free-disk-space) from 2.1.0 to 2.1.1.
- [Release notes](https://github.com/endersonmenezes/free-disk-space/releases)
- [Commits](dd13eb48e7...713d134e24)

---
updated-dependencies:
- dependency-name: endersonmenezes/free-disk-space
  dependency-version: 2.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 10:26:29 +04:00
dependabot[bot]
cb51238bfc Bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 10:26:03 +04:00
John Preston
87f873c8b6 Version 6.1.
- Music on Profiles.
- Gift Themes.
- Gift Value Information.
- Upgrading Gifts for Other Users.
2025-09-01 00:05:29 +04:00
John Preston
a37528b377 Fix crash in premium preview. 2025-09-01 00:05:29 +04:00
John Preston
51a58182ee Use saved music as is for now. 2025-09-01 00:05:29 +04:00
John Preston
cc4b768f54 Implement nice gift theme message layout. 2025-08-31 23:04:49 +04:00
John Preston
b016be6eb5 Fix emoji repaint crashing. 2025-08-31 22:18:17 +04:00
John Preston
881d73d4ab Fix build with MSVC. 2025-08-31 22:18:17 +04:00
John Preston
8859e352f9 Fix crash with widget double delete. 2025-08-31 22:06:04 +04:00
John Preston
5b0fc5b97b Fix build with Xcode. 2025-08-31 20:53:24 +04:00
John Preston
76cc59acab Show gift sticker in the background. 2025-08-31 20:53:12 +04:00
John Preston
e792088ceb Insert gift pattern symbols in background. 2025-08-31 20:53:12 +04:00
John Preston
1dc70d5a8d Load gift themes while scrolling. 2025-08-31 20:53:12 +04:00
John Preston
e876a0f6bd Set gift as theme from gift view. 2025-08-31 20:53:12 +04:00
John Preston
50450de3e4 Confirm transfer gift theme. 2025-08-31 20:53:12 +04:00
John Preston
c8abc84c3c Put gift themes in beginning. 2025-08-31 20:53:12 +04:00
John Preston
1f3996032c Fix theme preview pattern loading. 2025-08-31 20:53:12 +04:00
John Preston
b868cfdca9 Fix pattern caching on themed chat open. 2025-08-31 20:53:12 +04:00
John Preston
d51944e6a5 Update API scheme on layer 214. 2025-08-31 20:53:12 +04:00
John Preston
4419ba55e8 Start gift theme background parsing. 2025-08-31 20:53:12 +04:00
John Preston
9e8ae54821 Update API scheme to layer 214. Start themes. 2025-08-31 20:53:12 +04:00
John Preston
4ed266780a Fix emoji status texts. 2025-08-31 20:53:12 +04:00
John Preston
46886b4dcc Support channel direct messages links. 2025-08-31 20:53:12 +04:00
John Preston
1e2531f0b1 Support locked gifts. 2025-08-31 20:53:12 +04:00
John Preston
0e43fd4d00 Update API scheme on layer 213. 2025-08-31 20:53:12 +04:00
John Preston
10448bcc3d Support fileref refresh in saved music. 2025-08-31 20:53:12 +04:00
John Preston
4f1c2788b8 Improve phrasing for saved music. 2025-08-31 20:53:11 +04:00
John Preston
dbaa7b5e67 Allow forward / delete saved music. 2025-08-31 20:53:11 +04:00
John Preston
6211b7733d Support saved music playlist. 2025-08-31 20:53:11 +04:00
John Preston
0d6ea0845e Make nice music button in profile. 2025-08-31 20:53:11 +04:00
John Preston
b8e10fb34b Proof-of-concept saved music API support. 2025-08-31 20:53:11 +04:00
John Preston
b9c6e595d7 Update API scheme on layer 213. 2025-08-31 20:53:11 +04:00
John Preston
7d701d3e9c Fix upgrading of gifts with gifted upgrade. 2025-08-31 20:53:11 +04:00
John Preston
92d9c3c92b Finish gift value display. 2025-08-31 20:53:11 +04:00
John Preston
7007977891 Reuse some table values in gift table. 2025-08-31 20:53:11 +04:00
John Preston
9f5d24bbc9 Start gift value information display. 2025-08-31 20:53:11 +04:00
John Preston
2834a83eec Update API scheme on layer 212. 2025-08-31 20:53:11 +04:00
John Preston
eb82473395 Update API scheme on layer 212, gift upgrades. 2025-08-31 20:53:11 +04:00
John Preston
4a33d3227e Suggest upgrading next gift. 2025-08-31 20:53:11 +04:00
John Preston
235456e18e Update API scheme to layer 212. 2025-08-31 20:53:11 +04:00
23rd
1f9e532fbc Added link to info to box about sponsored messages. 2025-08-31 00:34:07 +03:00
23rd
8bc2d3184a Added lottie icon to transcribe voices while loading. 2025-08-30 23:39:36 +03:00
23rd
9ef54d1218 Added LottieCustomEmoji. 2025-08-30 23:39:36 +03:00
23rd
7e48cb97b0 Fixed first frame animation of opening schedule section for send button. 2025-08-30 23:19:40 +03:00
23rd
a226f0b58c Added toasts after review of unconfirmed authorizations. 2025-08-30 23:19:40 +03:00
23rd
65fd47a082 Fixed handle of dialogs resize for unconfirmed authorizations. 2025-08-30 23:19:40 +03:00
23rd
600b7d1d54 Slightly improved generation of stars in low credits balance list. 2025-08-30 23:19:40 +03:00
23rd
b8f4d4877e Added loading animation to list of options for low credits balance. 2025-08-30 23:19:40 +03:00
23rd
6a114e1275 Added ability to provide to loading text element style::TextStyle. 2025-08-30 23:19:40 +03:00
23rd
5d3d8640e3 Added unconfirmed authorizations to top bar suggestions in dialogs. 2025-08-30 23:19:40 +03:00
23rd
f09d2d22eb Added api support of new unconfirmed authorizations. 2025-08-30 23:19:40 +03:00
23rd
94ccd8f8b6 Added ability to store unconfirmed authorizations. 2025-08-30 23:19:40 +03:00
23rd
c96ce890d6 Added initial struct data for unreviewed authorizations. 2025-08-30 23:19:40 +03:00
23rd
de55be397b Improved style of withdrawal button in credits settings. 2025-08-30 23:19:40 +03:00
23rd
1420714cae Fixed display of "Show more" button in channel currency earn list. 2025-08-30 23:19:40 +03:00
23rd
b15305bc69 Respected localized number format in statistics. 2025-08-30 23:19:40 +03:00
23rd
db25b111e0 Fixed display of currency minor part from earn stats in some cases. 2025-08-30 23:19:40 +03:00
23rd
eeea9932ed Removed simple colorizing of query of hash or cashtag in found messages. 2025-08-30 23:19:40 +03:00
23rd
c710e9a54d Fixed display of label in self destruction box.
Fixed #29694.
2025-08-30 23:19:40 +03:00
23rd
62613e7da1 Added support of custom brush to loading element for peer list. 2025-08-30 23:19:40 +03:00
23rd
946b597471 Added support of custom bg to loading element for peer list. 2025-08-30 23:19:40 +03:00
23rd
691c55bedd Simplified master branch updater Github Action. 2025-08-30 23:19:40 +03:00
23rd
8387969467 Removed unused trigger for Github CI that updates user-agent for DNS. 2025-08-30 23:19:40 +03:00
23rd
90d21375c8 Removed ability to create todo lists in replies chat. 2025-08-30 23:19:40 +03:00
23rd
86daf2a9dc Removed ability to clear history from replies chat. 2025-08-30 23:19:40 +03:00
23rd
90473957cb Added process of error when collectible info can't be revealed. 2025-08-30 23:19:40 +03:00
23rd
3549c00141 Added ability to forward to Saved Messages on fast action with Ctrl. 2025-08-30 23:19:40 +03:00
23rd
1c1d13545b Added special check of balance on channel earn out just in case. 2025-08-30 23:19:40 +03:00
23rd
f1b3db89fb Fixed hover rect of entry item in channel earn history. 2025-08-30 23:19:40 +03:00
23rd
8c77baca6f Fixed wrong focus while recording voice in sections. 2025-08-30 23:19:40 +03:00
Ilya Fedin
36a40d97a7 Update submodules 2025-08-30 17:50:39 +04:00
Ilya Fedin
1eb9b40607 Update Qt to 6.9.2 2025-08-30 17:50:39 +04:00
Ilya Fedin
05c10e3f57 Switch patches to nil plugin in snap 2025-08-30 16:04:57 +04:00
Ilya Fedin
0cf3325655 Fix macOS action 2025-08-26 07:48:17 +04:00
Ilya Fedin
48525de714 Restore icon key in snapcraft.yaml
Looks like it didn't help with themed icon after all and snapcraft chooses a 64x64 icon without it which looks blurry with HiDPI
2025-08-23 19:58:56 -07:00
John Preston
7e15722eab Beta version 6.0.3.
- Ctrl+Tab / Ctrl+Shift+Tab for last opened chats switching.
- New topic dividers for groups with topics and tabs.
- Adjust volume of notification sounds.
- Show stars required for the next rating level.
- IV support on Linux (in case WebView works).
2025-08-22 21:17:48 +04:00
John Preston
719c209c7b Fix build with Xcode. 2025-08-22 21:17:48 +04:00
John Preston
d05ad44b84 Fix build with GCC. 2025-08-22 21:15:53 +04:00
Ilya Fedin
b352c97479 Switch from custom URI scheme to local HTTP server for webview on Linux 2025-08-22 18:05:11 +04:00
Ilya Fedin
063085a6bb Format Linux webview socket path using std::format 2025-08-22 18:05:11 +04:00
Ilya Fedin
b4bca16109 Get rid of wayland-scanner downgrade 2025-08-22 17:48:43 +04:00
John Preston
03770c52fe Support floating topic bars in forums. 2025-08-22 13:13:20 +04:00
John Preston
10fe5cdd5d Keep recent peers userpics in memory. 2025-08-22 13:06:04 +04:00
John Preston
57d459b917 Show nice topic/sublist userpics. 2025-08-22 13:06:03 +04:00
John Preston
fe26594f12 Improve Ctrl+Tab switch design. 2025-08-22 13:05:27 +04:00
John Preston
0d8065fc1f First attempt of Ctrl+Tab/Ctrl+Shift+Tab UI. 2025-08-22 13:05:26 +04:00
John Preston
a3cdae1e94 Fix refreshing story file reference. 2025-08-22 13:05:26 +04:00
John Preston
29d77b649b Nice selection of gifts for a collection. 2025-08-22 13:05:26 +04:00
John Preston
9e190cee81 Show next-level stars in rating. 2025-08-22 13:05:26 +04:00
John Preston
687bfd0f17 Remove some FixedHeightWidget-s. 2025-08-22 13:01:49 +04:00
23rd
7d7df4f749 Fixed display of about text in low credits balance box in some cases. 2025-08-20 12:40:16 +03:00
23rd
958dede319 Fixed active color of recording voice button. 2025-08-19 20:51:32 +03:00
23rd
76e814944d Added shortcuts to start recording of voice or round message.
Fixed #29633.
2025-08-19 16:52:48 +03:00
23rd
fbc1d75e9a Added ability to start and lock voice recording immediately. 2025-08-19 16:43:47 +03:00
23rd
b3c7ce05dc Added ability to send recording voice with submit key. 2025-08-19 16:43:47 +03:00
23rd
feea881e09 Slightly improved position of tooltip for voice record bar. 2025-08-19 13:21:51 +03:00
23rd
b978bc4876 Added ability to bottom scroll in HistoryView::Chat section with submit.
Fixed #29662.
2025-08-19 12:34:46 +03:00
23rd
f84181e7a5 Added event for scrolling to bottom to compose controls. 2025-08-19 12:33:38 +03:00
John Preston
313ae0f86c Remove CenterWrap layout. 2025-08-18 17:55:20 +04:00
John Preston
a39c018359 Scroll to top on global posts search. 2025-08-18 17:25:31 +04:00
John Preston
a09f57d908 Fix natural width for LabelWithNumbers. 2025-08-18 17:25:31 +04:00
John Preston
596828cf78 Fix webview blocking popups. 2025-08-18 17:25:31 +04:00
23rd
44843aa9cd Added hint about archive and its features. 2025-08-18 13:26:26 +03:00
23rd
5f930cc4d1 Added initial ability to rate voice transcriptions. 2025-08-18 13:26:23 +03:00
23rd
7defad5d95 Added ability to clear history for channels and megagroups.
Fixed #28778.
2025-08-17 14:47:49 +03:00
23rd
707af8a295 Got rid Ui::CreateLabelWithCustomEmoji. 2025-08-17 14:47:49 +03:00
23rd
e0fb9ffbb0 Added support of setting up of login email from intro. 2025-08-17 14:47:49 +03:00
23rd
d614de6f5e Fixed display of error in cloud password section with new naturalWidth. 2025-08-17 14:47:49 +03:00
Ilya Fedin
86b94b4723 Change notification volume visibility on support change
Cross platform VolumeSupported wrapper
2025-08-17 14:47:49 +03:00
23rd
5070300050 Added ability to test on place volume of notifications. 2025-08-17 14:47:49 +03:00
23rd
1919546441 Added ability to change volume of specific notifications from settings. 2025-08-17 14:47:49 +03:00
23rd
1c3cd8d44b Added ability to change master volume of notifications from settings. 2025-08-17 14:47:49 +03:00
23rd
292296266f Added convenient utils to pass and change notifications volume from ui. 2025-08-17 14:47:49 +03:00
23rd
a3e8848bc8 Added ability to check if OS has support of notifications volume. 2025-08-17 14:47:49 +03:00
23rd
ced146fc63 Added session-specific settings for volume of notifications for peers. 2025-08-17 14:47:49 +03:00
23rd
6ed79f6a0d Added global settings for master volume of notifications. 2025-08-17 14:47:49 +03:00
23rd
15e4d86e92 Added ability to override volume in media audio tracks. 2025-08-17 14:47:49 +03:00
23rd
5a29a7d2a3 Moved out DefaultNotify to data_peer_notify_settings. 2025-08-17 14:47:49 +03:00
23rd
034740ce46 Removed unused PeerListWidget. 2025-08-17 14:47:49 +03:00
23rd
a2847246e6 Replaced custom GroupMembersWidget with PeerList. 2025-08-17 14:47:49 +03:00
23rd
04479ad660 Moved out GroupMembersWidget to correspond file and namespace. 2025-08-17 14:47:49 +03:00
23rd
35d2fff593 Added initial support of highlighting for found query in quotes. 2025-08-17 14:47:49 +03:00
23rd
2b93fe9a30 Added simple colorizing of query in found messages from compose search. 2025-08-17 14:47:49 +03:00
23rd
5c33a2bd5c Added simple colorizing of query in found messages. 2025-08-17 14:47:49 +03:00
23rd
a28f113105 Moved FindSearchQueryHighlight to use as util. 2025-08-17 14:47:49 +03:00
23rd
eb7976a2ef Replaced alignment of title bar with left side for macOS 26. 2025-08-17 14:47:49 +03:00
23rd
15db1c0c30 Added ability to withdrawal personal currency balance. 2025-08-17 14:47:49 +03:00
23rd
2b83c95869 Moved out ui util for channel earn list to static function. 2025-08-17 14:47:49 +03:00
dependabot[bot]
273d5596d7 Bump endersonmenezes/free-disk-space
Bumps [endersonmenezes/free-disk-space](https://github.com/endersonmenezes/free-disk-space) from 4cae28d0d8e716a770938d92630f23db5184f61f to dd13eb48e709830a7ec4e50692e841be6b400d90.
- [Release notes](https://github.com/endersonmenezes/free-disk-space/releases)
- [Commits](4cae28d0d8...dd13eb48e7)

---
updated-dependencies:
- dependency-name: endersonmenezes/free-disk-space
  dependency-version: dd13eb48e709830a7ec4e50692e841be6b400d90
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-15 17:21:13 +04:00
Ilya Fedin
b423dab152 Fix Windows action execution on SDK update 2025-08-15 17:20:47 +04:00
Ilya Fedin
1b104bcf29 Update Windows SDK version 2025-08-15 17:20:47 +04:00
John Preston
5cad9a8c44 Fix build on layer 211. 2025-08-15 09:32:40 +04:00
John Preston
ec8a475a2c Fix top bar selection label resizing. 2025-08-14 18:45:52 +04:00
John Preston
e656cb8118 Fix vertical layout once again. 2025-08-14 18:30:17 +04:00
John Preston
7f2a0b6630 Scroll a bit further when switching tabs. 2025-08-14 18:30:01 +04:00
John Preston
f9bf40a771 Fix crash in forum removal with new tabs. 2025-08-14 18:30:01 +04:00
John Preston
96360619e1 Add some asserts in stats. 2025-08-14 18:30:01 +04:00
John Preston
4cefc21819 Close forum when searching in different chat. 2025-08-14 18:30:01 +04:00
Ilya Fedin
192a56ee15 Shorter notifications proxy check for Linux notifications 2025-08-13 11:19:38 +04:00
John Preston
cf16472dd0 Fix user profiles. 2025-08-13 11:18:34 +04:00
John Preston
b2fb2d5821 Attempt to fix the naturalWidth feature. 2025-08-13 11:18:34 +04:00
John Preston
c939eda7bb Hide "Use for calls" in Socks5 while not supported. 2025-08-13 11:18:34 +04:00
John Preston
428e059895 Write unknown cons to mtproto logs. 2025-08-13 11:18:34 +04:00
John Preston
1b4ea2e9c6 Use Api::ParseTextWithEntities more. 2025-08-13 11:18:34 +04:00
John Preston
d60bfa238f Merge TWidget* into Ui::RpWidget*. 2025-08-11 13:55:16 +04:00
John Preston
cc98f7da36 Fix topic unread count badges. 2025-08-11 10:36:26 +04:00
John Preston
87cdc22990 Fix marking read in new forum layout. 2025-08-11 10:36:26 +04:00
John Preston
60f6ea3252 Show informative empty posts search. 2025-08-11 10:29:11 +04:00
John Preston
86cffae001 Remove unused function. 2025-08-11 10:29:11 +04:00
John Preston
77cec0e338 Make big Upgrade button for gifts. 2025-08-11 10:29:11 +04:00
John Preston
ee85bb9a1b Improve rating layout. 2025-08-11 10:29:11 +04:00
John Preston
bf0b85afd1 Improve posts search transaction entry view. 2025-08-11 10:29:11 +04:00
John Preston
5473696491 Fix dice-type game display. 2025-08-11 10:29:11 +04:00
Ilya Fedin
0bc59a82ba Use symbolic links for icons in snap 2025-08-09 15:44:08 +04:00
Ilya Fedin
a8430d8ecf Self-build openh264 in snap for lesser package size 2025-08-09 15:44:08 +04:00
Ilya Fedin
9672b2ec23 Use double dash consistently in snap ldflags 2025-08-09 15:44:08 +04:00
Ilya Fedin
60447f073f Partially revert "Move packages to a separate snap part"
This partially reverts commit ef2e9406c7 restoring per-part build packages so changes to them don't lead to rebuild of all parts.
2025-08-09 10:39:48 +04:00
Ilya Fedin
62329c104c Fix accidentally broken CMAKE_INSTALL_PREFIX in snap
Regression was introduced in a6a8363527
2025-08-09 10:39:48 +04:00
John Preston
536884bb46 Fix build with new Xcode. 2025-08-08 11:26:39 +04:00
John Preston
efb213fb9f Fix build with latest Xcode. 2025-08-08 09:52:57 +04:00
Ilya Fedin
02b43c9375 Remove systemd-detect-virt from snap
It could be used from the core snap since hardware-observe plug was connected
2025-08-08 09:08:05 +04:00
Ilya Fedin
9a2679efaa Remove unity7 plug from snap
Looks like the desktop plug has everything needed since snapd 2.67
2025-08-08 09:07:53 +04:00
Ilya Fedin
00a396c3e7 Fix stage paths in snap 2025-08-06 07:43:33 +04:00
John Preston
b4272c306d Get rid of registerImageEmoji/registerInternalEmoji. 2025-08-04 22:27:08 +04:00
John Preston
b876605e93 Remove per-star options in giveaway creation. 2025-08-04 16:02:01 +04:00
John Preston
7f896a9f40 Use better ton icon in strings. 2025-08-04 12:50:24 +04:00
John Preston
a96874ac55 Allow '@' in search posts queries. 2025-08-04 11:51:42 +04:00
John Preston
1f8d3ed911 Fix incorrect filesize check.
Fixes #29630.
2025-08-04 11:46:19 +04:00
23rd
56be0ef4be Fixed incorrect state of disabled calls for account in notifications. 2025-08-04 11:15:44 +04:00
John Preston
67079545b3 Ease filtering on posts search queries. 2025-08-04 11:15:09 +04:00
411 changed files with 11721 additions and 4633 deletions

View File

@@ -17,7 +17,7 @@ jobs:
steps:
- name: Clone.
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: recursive

View File

@@ -59,7 +59,7 @@ jobs:
steps:
- name: Clone.
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: recursive

View File

@@ -56,7 +56,7 @@ jobs:
run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
- name: Clone.
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: recursive
path: ${{ env.REPO_NAME }}

View File

@@ -60,7 +60,7 @@ jobs:
run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
- name: Clone.
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: recursive
path: ${{ env.REPO_NAME }}

View File

@@ -5,33 +5,9 @@ on:
types: released
jobs:
updater:
User-agent:
runs-on: ubuntu-latest
env:
SKIP: "0"
to_branch: "master"
steps:
- uses: actions/checkout@v4
- uses: desktop-app/action_code_updater@master
with:
fetch-depth: 0
if: env.SKIP == '0'
- name: Push the code to the master branch.
if: env.SKIP == '0'
run: |
token=${{ secrets.TOKEN_FOR_MASTER_UPDATER }}
if [ -z "${token}" ]; then
echo "Token is unset. Nothing to do."
exit 0
fi
url=https://x-access-token:$token@github.com/$GITHUB_REPOSITORY
latest_tag=$(git describe --tags --abbrev=0)
echo "Latest tag: $latest_tag"
git remote set-url origin $url
git remote -v
git checkout master
git merge $latest_tag
git push origin HEAD:refs/heads/$to_branch
echo "Done!"
type: "dev-to-master"

View File

@@ -47,7 +47,7 @@ jobs:
steps:
- name: Clone.
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
submodules: recursive
@@ -61,7 +61,7 @@ jobs:
sudo lxd waitready
- name: Free up some disk space.
uses: endersonmenezes/free-disk-space@4cae28d0d8e716a770938d92630f23db5184f61f
uses: endersonmenezes/free-disk-space@713d134e243b926eba4a5cce0cf608bfd1efb89a
with:
remove_android: true
remove_dotnet: true

View File

@@ -6,8 +6,6 @@ on:
schedule:
# At 00:00 on day-of-month 1.
- cron: "0 0 1 * *"
pull_request_target:
types: [closed]
jobs:
User-agent:

View File

@@ -4,8 +4,8 @@ on:
push:
paths-ignore:
- 'docs/**'
- '!docs/building-win*.md'
- '**.md'
- '!docs/building-win*.md'
- 'changelog.txt'
- 'LEGAL'
- 'LICENSE'
@@ -23,8 +23,8 @@ on:
pull_request:
paths-ignore:
- 'docs/**'
- '!docs/building-win*.md'
- '**.md'
- '!docs/building-win*.md'
- 'changelog.txt'
- 'LEGAL'
- 'LICENSE'
@@ -72,7 +72,7 @@ jobs:
run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
- name: Clone.
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: recursive
path: ${{ env.TBUILD }}\${{ env.REPO_NAME }}

View File

@@ -523,6 +523,8 @@ PRIVATE
data/notify/data_notify_settings.h
data/notify/data_peer_notify_settings.cpp
data/notify/data_peer_notify_settings.h
data/notify/data_peer_notify_volume.cpp
data/notify/data_peer_notify_volume.h
data/stickers/data_custom_emoji.cpp
data/stickers/data_custom_emoji.h
data/stickers/data_stickers_set.cpp
@@ -634,6 +636,8 @@ PRIVATE
data/data_report.h
data/data_saved_messages.cpp
data/data_saved_messages.h
data/data_saved_music.cpp
data/data_saved_music.h
data/data_saved_sublist.cpp
data/data_saved_sublist.h
data/data_search_controller.cpp
@@ -813,6 +817,8 @@ PRIVATE
history/view/media/history_view_poll.h
history/view/media/history_view_premium_gift.cpp
history/view/media/history_view_premium_gift.h
history/view/media/history_view_save_document_action.cpp
history/view/media/history_view_save_document_action.h
history/view/media/history_view_service_box.cpp
history/view/media/history_view_service_box.h
history/view/media/history_view_similar_channels.cpp
@@ -876,6 +882,8 @@ PRIVATE
history/view/history_view_fake_items.h
history/view/history_view_group_call_bar.cpp
history/view/history_view_group_call_bar.h
history/view/history_view_group_members_widget.cpp
history/view/history_view_group_members_widget.h
history/view/history_view_item_preview.h
history/view/history_view_list_widget.cpp
history/view/history_view_list_widget.h
@@ -1046,6 +1054,11 @@ PRIVATE
info/reactions_list/info_reactions_list_widget.h
info/requests_list/info_requests_list_widget.cpp
info/requests_list/info_requests_list_widget.h
info/saved/info_saved_music_common.h
info/saved/info_saved_music_provider.cpp
info/saved/info_saved_music_provider.h
info/saved/info_saved_music_widget.cpp
info/saved/info_saved_music_widget.h
info/saved/info_saved_sublists_widget.cpp
info/saved/info_saved_sublists_widget.h
info/settings/info_settings_widget.cpp
@@ -1119,6 +1132,8 @@ PRIVATE
inline_bots/inline_results_widget.h
intro/intro_code.cpp
intro/intro_code.h
intro/intro_email.cpp
intro/intro_email.h
intro/intro_password_check.cpp
intro/intro_password_check.h
intro/intro_phone.cpp
@@ -1274,6 +1289,8 @@ PRIVATE
menu/menu_antispam_validator.h
menu/menu_item_download_files.cpp
menu/menu_item_download_files.h
menu/menu_item_rate_transcribe_session.cpp
menu/menu_item_rate_transcribe_session.h
menu/menu_mute.cpp
menu/menu_mute.h
menu/menu_send.cpp
@@ -1307,6 +1324,8 @@ PRIVATE
mtproto/special_config_request.cpp
mtproto/special_config_request.h
mtproto/type_utils.h
overview/overview_checkbox.cpp
overview/overview_checkbox.h
overview/overview_layout.cpp
overview/overview_layout.h
overview/overview_layout_delegate.h
@@ -1427,10 +1446,6 @@ PRIVATE
platform/platform_window_title.h
profile/profile_back_button.cpp
profile/profile_back_button.h
profile/profile_block_group_members.cpp
profile/profile_block_group_members.h
profile/profile_block_peer_list.cpp
profile/profile_block_peer_list.h
profile/profile_block_widget.cpp
profile/profile_block_widget.h
profile/profile_cover_drop_area.cpp
@@ -1625,8 +1640,6 @@ PRIVATE
ui/text/format_song_document_name.h
ui/widgets/expandable_peer_list.cpp
ui/widgets/expandable_peer_list.h
ui/widgets/label_with_custom_emoji.cpp
ui/widgets/label_with_custom_emoji.h
ui/widgets/chat_filters_tabs_strip.cpp
ui/widgets/chat_filters_tabs_strip.h
ui/widgets/peer_bubble.cpp
@@ -1659,6 +1672,8 @@ PRIVATE
window/window_adaptive.h
window/window_chat_preview.cpp
window/window_chat_preview.h
window/window_chat_switch_process.cpp
window/window_chat_switch_process.h
window/window_connecting_widget.cpp
window/window_connecting_widget.h
window/window_controller.cpp

Binary file not shown.

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icon / Mini / mini_ton_bold</title>
<g id="Icon-/-Mini-/-mini_ton_bold" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M12.5980305,2.7875 C13.2201476,2.7875 13.7244732,3.29182558 13.7244732,3.9139427 C13.7244732,4.11165014 13.6724374,4.30587532 13.5735947,4.47710133 L9.21676388,12.0244744 C8.80179975,12.7433201 7.88266529,12.9896647 7.16381961,12.5747006 C6.92829269,12.4387393 6.73407151,12.2414175 6.60185728,12.0037668 L2.40584723,4.46158062 C2.10339516,3.91793325 2.29892259,3.23203413 2.84256996,2.92958206 C3.01005587,2.83640316 3.19854713,2.7875 3.39020787,2.7875 L12.5980305,2.7875 Z M7.24956057,4.2875 L4.025,4.2875 L7.24956057,10.0835 L7.24956057,4.2875 Z M11.95,4.2875 L8.74956057,4.2875 L8.74956057,9.8255 L11.95,4.2875 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Mini / mini_gift_lock</title>
<g id="Mini-/-mini_gift_lock" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M34.6666667,26.3333333 C40.1895142,26.3333333 44.6666667,30.8104858 44.6666667,36.3333333 C44.6666667,41.8561808 40.1895142,46.3333333 34.6666667,46.3333333 C29.1438192,46.3333333 24.6666667,41.8561808 24.6666667,36.3333333 C24.6666667,30.8104858 29.1438192,26.3333333 34.6666667,26.3333333 Z M22,3.06666667 C27.8542183,3.06666667 32.6,7.81244832 32.6,13.6666667 L32.5999548,19.4672746 C34.7232323,19.9235567 36.4292218,21.5034006 37.0650949,23.5539779 C36.287246,23.4090943 35.4858038,23.3333333 34.6666667,23.3333333 C27.4869649,23.3333333 21.6666667,29.1536316 21.6666667,36.3333333 C21.6666667,38.3649522 22.1326999,40.2877232 22.9636948,42.0005746 L12.6666667,42 C9.35295817,42 6.66666667,39.3137085 6.66666667,36 L6.66666667,25.3333333 C6.66666667,22.454169 8.69461762,20.048658 11.4000452,19.4672746 L11.4,13.6666667 C11.4,7.81244832 16.1457817,3.06666667 22,3.06666667 Z M34.6666667,29.3061633 C33.7052821,29.3061633 32.9259259,30.0855195 32.9259259,31.0469041 L32.9259259,36.9308449 C32.9259259,37.7182476 33.2996271,38.4589206 33.9329602,38.9267797 L37.3333333,41.3922119 C38.1066056,41.9634476 39.1965447,41.7996646 39.7677804,41.0263923 L39.8381314,40.9238035 C40.323622,40.1593232 40.1416125,39.1383445 39.4019608,38.5919452 L36.4074074,36.4264726 L36.4074074,31.0469041 C36.4074074,30.0855195 35.6280512,29.3061633 34.6666667,29.3061633 Z M22,7.6 C18.6494725,7.6 15.9333333,10.3161392 15.9333333,13.6666667 L15.9326667,19.3326667 L28.0666667,19.3326667 L28.0666667,13.6666667 C28.0666667,10.3161392 25.3505275,7.6 22,7.6 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -377,6 +377,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_intro_fragment_about" = "Get the code for {phone_number} in the Anonymous Numbers section on Fragment.";
"lng_intro_fragment_button" = "Open Fragment";
"lng_intro_email_setup_title" = "Choose a login email";
"lng_intro_email_confirm_subtitle" = "Please check your email {email} (don't forget the spam folder) and enter the code we just sent you.";
"lng_phone_title" = "Your Phone Number";
"lng_phone_desc" = "Please confirm your country code\nand enter your phone number.";
"lng_phone_to_qr" = "Quick log in using QR code";
@@ -512,6 +515,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_notify_global" = "Global settings";
"lng_settings_notify_title" = "Notifications for chats";
"lng_settings_desktop_notify" = "Desktop notifications";
"lng_settings_master_volume_notifications" = "Volume";
"lng_settings_native_title" = "System integration";
"lng_settings_use_windows" = "Use Windows notifications";
"lng_settings_skip_in_focus" = "Respect system Focus mode";
@@ -552,12 +556,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_notification_title_private_chats" = "Notifications for private chats";
"lng_notification_about_private_chats#one" = "Please note that **{count} chat** is listed as an exception and won't be affected by this change.";
"lng_notification_about_private_chats#other" = "Please note that **{count} chats** are listed as exceptions and won't be affected by this change.";
"lng_notification_volume_private_chats" = "Notifications volume for private chats";
"lng_notification_title_groups" = "Notifications for groups";
"lng_notification_about_groups#one" = "Please note that **{count} group** is listed as an exception and won't be affected by this change.";
"lng_notification_about_groups#other" = "Please note that **{count} groups** are listed as exceptions and won't be affected by this change.";
"lng_notification_volume_groups" = "Notifications volume for groups";
"lng_notification_title_channels" = "Notifications for channels";
"lng_notification_about_channels#one" = "Please note that **{count} channel** is listed as an exception and won't be affected by this change.";
"lng_notification_about_channels#other" = "Please note that **{count} channels** are listed as exceptions and won't be affected by this change.";
"lng_notification_volume_channel" = "Notifications volume for channels";
"lng_notification_exceptions_view" = "View exceptions";
"lng_notification_enable" = "Enable notifications";
"lng_notification_sound" = "Sound";
@@ -687,6 +694,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_shortcuts_media_fullscreen" = "Toggle video fullscreen";
"lng_shortcuts_show_chat_menu" = "Show chat menu";
"lng_shortcuts_show_chat_preview" = "Show chat preview";
"lng_shortcuts_record_voice_message" = "Record Voice Message";
"lng_shortcuts_record_round_message" = "Record Round Message";
"lng_settings_chat_reactions_title" = "Quick Reaction";
"lng_settings_chat_reactions_subtitle" = "Choose your favorite reaction";
@@ -1238,6 +1247,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_quick_dialog_action_toast_archive_success" = "The chat has been archived.";
"lng_quick_dialog_action_toast_unarchive_success" = "The chat has been unarchived.";
"lng_archive_hint_title" = "This is your Archive";
"lng_archive_hint_about" = "Archived chats will remain in the Archive when you receive a new message. {link}";
"lng_archive_hint_about_unmuted" = "When you receive a new message, muted chats will remain in the Archive, while unmuted chats will be moved to Chats. {link}";
"lng_archive_hint_about_link" = "Tap to change {emoji}";
"lng_archive_hint_section_1" = "Archived Chats";
"lng_archive_hint_section_1_info" = "Move any chat into your Archive and back by swiping on it.";
"lng_archive_hint_section_2" = "Hiding Archive";
"lng_archive_hint_section_2_info" = "Hide the Archive from your Main screen by swiping on it.";
"lng_archive_hint_section_3" = "Stories";
"lng_archive_hint_section_3_info" = "Archive Stories from your contacts separately from chats with them.";
"lng_archive_hint_button" = "Got it";
"lng_settings_generic_subscribe" = "Subscribe to {link} to use this setting.";
"lng_settings_generic_subscribe_link" = "Telegram Premium";
@@ -1638,6 +1659,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_media_type_audios" = "Voice messages";
"lng_media_type_links" = "Shared links";
"lng_media_type_rounds" = "Video messages";
"lng_media_saved_music_your" = "Your playlist";
"lng_media_saved_music_title" = "Playlist";
"lng_profile_common_groups_section" = "Groups in common";
"lng_info_edit_contact" = "Edit contact";
"lng_info_delete_contact" = "Delete contact";
@@ -1927,6 +1950,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_manage_monoforum_price" = "Price for each message";
"lng_manage_monoforum_about" = "Allow users to send messages to your channel, with the option to charge a fee for each message.";
"lng_manage_monoforum_price_about" = "Your channel will receive {percent} of the selected fee ({amount}) for each incoming message.";
"lng_manage_monoforum_link_subtitle" = "Link to direct messages";
"lng_manage_history_visibility_title" = "Chat history for new members";
"lng_manage_history_visibility_shown" = "Visible";
@@ -2055,6 +2079,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_sure_delete_contact" = "Are you sure you want to delete {contact} from your contact list?";
"lng_sure_delete_history" = "Are you sure you want to delete all message history with {contact}?\n\nThis action cannot be undone.";
"lng_sure_delete_group_history" = "Are you sure you want to delete all messages in \"{group}\"?\n\nThis action cannot be undone.";
"lng_sure_delete_channel_history" = "Are you sure you want to delete all messages in \"{channel}\"?\n\n**This action cannot be undone.**";
"lng_sure_delete_and_exit" = "Are you sure you want to delete all message history and leave «{group}»?\n\nThis action cannot be undone.";
"lng_sure_leave_channel" = "Are you sure you want to leave\nthis channel?";
"lng_sure_delete_channel" = "Are you sure you want to delete this channel? All subscribers will be removed and all messages will be lost.";
@@ -2167,6 +2192,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_you_proximity_reached" = "You are now within {distance} from {user}";
"lng_action_you_theme_changed" = "You changed the chat theme to {emoji}";
"lng_action_theme_changed" = "{from} changed the chat theme to {emoji}";
"lng_action_you_gift_theme_changed" = "You set {name} as a new theme for this chat.";
"lng_action_gift_theme_changed" = "{from} set {name} as a new theme for this chat.";
"lng_action_you_theme_disabled" = "You disabled the chat theme";
"lng_action_theme_disabled" = "{from} disabled the chat theme";
"lng_action_proximity_distance_m#one" = "{count} meter";
@@ -2183,6 +2210,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"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_sent_upgrade_other" = "{from} sent an upgrade worth {cost} for the gift you received from {user}.";
"lng_action_gift_sent_upgrade_self_other" = "You sent an upgrade worth {cost} for the gift {name} received from {user}.";
"lng_action_gift_sent_upgrade" = "{from} sent an upgrade worth {cost} for your gift.";
"lng_action_gift_sent_upgrade_self" = "You sent an upgrade worth {cost} for this gift.";
"lng_action_gift_sent_upgrade_self_channel" = "You sent an upgrade worth {cost} for your gift to {name}.";
"lng_action_gift_upgraded_helped" = "{user} unpacked the gift that you helped to upgrade.";
"lng_action_gift_upgraded_helped_self" = "You unpacked the gift that {user} helped to upgrade.";
"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";
@@ -2339,6 +2373,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"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_upgradable" = "Upgradeable";
"lng_peer_gifts_filter_limited" = "Limited";
"lng_peer_gifts_filter_unique" = "Unique";
"lng_peer_gifts_filter_saved" = "Displayed";
@@ -2902,6 +2937,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_credits_box_history_entry_ads" = "Ads Platform";
"lng_credits_box_history_entry_premium_bot" = "Stars Top-Up";
"lng_credits_box_history_entry_currency_in" = "TON Top-Up";
"lng_credits_box_history_entry_posts_search" = "Posts Search";
"lng_credits_box_history_entry_api" = "Paid Broadcast";
"lng_credits_box_history_entry_floodskip_about#one" = "{count} Message";
"lng_credits_box_history_entry_floodskip_about#other" = "{count} Messages";
@@ -3610,8 +3646,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"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_in_blockchain_link_arrow" = "View {arrow}";
"lng_gift_visible_hide_arrow" = "Hide {arrow}";
"lng_gift_visible_show_arrow" = "Show {arrow}";
"lng_gift_show_on_page" = "Display on my Page";
"lng_gift_show_on_channel" = "Display in channel's Gifts";
"lng_gift_availability" = "Availability";
@@ -3630,7 +3667,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"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";
"lng_gift_unique_upgrade" = "Upgrade";
"lng_gift_unique_upgrade_next" = "Upgrade Next Gift";
"lng_gift_unique_gift_upgrade" = "Gift an Upgrade";
"lng_gift_unique_number" = "Collectible #{index}";
"lng_gift_unique_number_by" = "Collectible #{index} by {name}";
"lng_gift_unique_model" = "Model";
@@ -3640,6 +3679,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"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_value" = "Value";
"lng_gift_unique_value_learn_more" = "learn more";
"lng_gift_unique_info" = "Gifted to {recipient} on {date}.";
"lng_gift_unique_info_sender" = "Gifted by {from} to {recipient} on {date}.";
"lng_gift_unique_info_sender_comment" = "Gifted by {from} to {recipient} on {date} with the comment \"{text}\".";
@@ -3648,6 +3689,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_availability_left#one" = "{count} of {amount} left";
"lng_gift_availability_left#other" = "{count} of {amount} left";
"lng_gift_availability_none" = "None of {amount} left";
"lng_gift_value_about_average" = "This is the average sale price of {gift} gifts on Telegram and Fragment over the past month.";
"lng_gift_value_about_last" = "This is the price at which {gift} was last sold on {platform}.";
"lng_gift_value_initial_sale" = "Initial Sale";
"lng_gift_value_initial_price" = "Initial Price";
"lng_gift_value_initial_price_value" = "{stars} ({amount})";
"lng_gift_value_last_sale" = "Last Sale";
"lng_gift_value_last_price" = "Last Price";
"lng_gift_value_minimum_price" = "Minimum Price";
"lng_gift_value_minimum_price_tooltip" = "{amount} is the floor price for {gift} gifts listed on Telegram and Fragment.";
"lng_gift_vlaue_average_price" = "Average Price";
"lng_gift_value_average_price_tooltip" = "{amount} is the average sale price of {gift} gifts on Telegram and Fragment over the past month.";
"lng_gift_value_availability#one" = "{count} {emoji} for sale on {platform} {arrow}";
"lng_gift_value_availability#other" = "{count} {emoji} for sale on {platform} {arrow}";
"lng_gift_value_telegram" = "Telegram";
"lng_gift_value_fragment" = "Fragment";
"lng_gift_convert_to_stars#one" = "Convert to {count} Star";
"lng_gift_convert_to_stars#other" = "Convert to {count} Stars";
"lng_gift_convert_sure_title" = "Convert Gift to Stars";
@@ -3683,10 +3739,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"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_unique_about_user" = "{name} will get a unique number, model, backdrop and symbol for your gift.";
"lng_gift_upgrade_unique_about_channel" = "Admins of {name} will get a unique number, model, backdrop and symbol for your gift.";
"lng_gift_upgrade_transferable_title" = "Transferable";
"lng_gift_upgrade_transferable_about" = "Send your upgraded gift to any of your friends on Telegram.";
"lng_gift_upgrade_transferable_about_user" = "{name} will be able to send the gift to anyone on Telegram.";
"lng_gift_upgrade_transferable_about_channel" = "Admins of {name} will be able to send the gift to anyone on Telegram.";
"lng_gift_upgrade_tradable_title" = "Tradable";
"lng_gift_upgrade_tradable_about" = "Sell or auction your gift on third-party NFT marketplaces.";
"lng_gift_upgrade_tradable_about_user" = "{name} will be able to sell the gift on Telegram and NFT marketplaces.";
"lng_gift_upgrade_tradable_about_channel" = "Admins of {name} will be able to sell the gift on Telegram and NFT marketplaces.";
"lng_gift_upgrade_button" = "Upgrade for {price}";
"lng_gift_upgrade_free" = "Upgrade for Free";
"lng_gift_upgrade_confirm" = "Confirm";
@@ -3696,6 +3758,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_upgrade_add_comment" = "Add sender's name and comment";
"lng_gift_upgraded_title" = "Gift Upgraded";
"lng_gift_upgraded_about" = "Your gift {name} now has unique attributes and can be transferred to others";
"lng_gift_upgrade_gifted_title" = "Upgrade Gifted";
"lng_gift_upgrade_gifted_about" = "Now {name} can turn your gift into a unique collectible.";
"lng_gift_upgrade_gifted_about_channel" = "Now the admins of {name} can turn your gift into a unique collectible.";
"lng_gift_transferred_title" = "Gift Transferred";
"lng_gift_transferred_about" = "{name} was successfully transferred to {recipient}.";
"lng_gift_transfer_title" = "Transfer {name}";
@@ -3722,6 +3787,8 @@ 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_set_theme" = "Set as Theme in...";
"lng_gift_transfer_choose" = "Choose Chat";
"lng_gift_transfer_wear" = "Wear";
"lng_gift_transfer_take_off" = "Take Off";
"lng_gift_transfer_sell" = "Sell";
@@ -3781,7 +3848,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_resale_symbol" = "Symbol";
"lng_gift_resale_symbols#one" = "{count} Symbol";
"lng_gift_resale_symbols#other" = "{count} Symbols";
"lng_gift_resale_switch_to" = "Switch to {currency}";
"lng_gift_resale_switch_to_ton" = "Switch to Ton";
"lng_gift_resale_switch_to_stars" = "Switch to Stars";
"lng_gift_resale_early" = "You will be able to resell this gift in {duration}.";
"lng_gift_transfer_early" = "You will be able to transfer this gift in {duration}.";
"lng_gift_resale_transfer_early_title" = "Try Later";
@@ -3804,6 +3872,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_collection_delete_sure" = "Are you sure you want to delete this collection?";
"lng_gift_collection_delete_button" = "Delete";
"lng_gift_collection_add_to" = "Add to Collection";
"lng_gift_locked_title" = "Gift Locked";
"lng_accounts_limit_title" = "Limit Reached";
"lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected account.";
@@ -4122,6 +4191,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_dialogs_suggestions_credits_sub_low_title#other" = "{emoji} {count} Stars needed for {channels}";
"lng_dialogs_suggestions_credits_sub_low_about" = "Insufficient funds to cover your subscription.";
"lng_unconfirmed_auth_title" = "Someone just got access to your messages!";
"lng_unconfirmed_auth_confirm" = "Yes, its me";
"lng_unconfirmed_auth_deny" = "No, its not me!";
"lng_unconfirmed_auth_single" = "We detected a new login to your account from {from}, {country}. Is it you?";
"lng_unconfirmed_auth_multiple#one" = "We detected new {count} login to your account. Is it you?";
"lng_unconfirmed_auth_multiple#other" = "We detected new {count} logins to your account. Is it you?";
"lng_unconfirmed_auth_multiple_from#one" = "We detected new {count} login to your account from {country}. Is it you?";
"lng_unconfirmed_auth_multiple_from#other" = "We detected new {count} logins to your account from {country}. Is it you?";
"lng_unconfirmed_auth_denied_title#one" = "New Login Prevented";
"lng_unconfirmed_auth_denied_title#other" = "New Logins Prevented";
"lng_unconfirmed_auth_denied_single" = "We have terminated the login attempt from {country}.";
"lng_unconfirmed_auth_denied_multiple" = "We have terminated the login attempts from: {country}";
"lng_unconfirmed_auth_denied_warning" = "Never send your login code to anyone or you can lose your Telegram account!";
"lng_unconfirmed_auth_confirmed" = "New Login Allowed";
"lng_unconfirmed_auth_confirmed_message" = "You can check the list of your active logins in {link}.";
"lng_about_random" = "Send a {emoji} emoji to any chat to try your luck.";
"lng_about_random_send" = "Send";
@@ -4289,6 +4374,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_archive_to_list" = "Move to chat list";
"lng_context_archive_to_menu_info" = "Archive moved to the main menu!\nRight click the archive button to return the Archive to your chat list.";
"lng_context_archive_settings" = "Archive settings";
"lng_context_archive_how_does_it_work" = "How does it work?";
"lng_context_mute" = "Mute notifications";
"lng_context_unmute" = "Unmute";
@@ -4300,6 +4386,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_remove_from_group" = "Remove from group";
"lng_context_add_to_group" = "Add to group";
"lng_context_rate_transcription" = "Rate transcription";
"lng_toast_sent_rate_transcription" = "Thank you for your feedback!";
"lng_context_copy_link" = "Copy Link";
"lng_context_copy_message_link" = "Copy Message Link";
"lng_context_copy_post_link" = "Copy Post Link";
@@ -4319,6 +4408,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_pack_info" = "View Sticker Set";
"lng_context_pack_add" = "Add Stickers";
"lng_context_save_file" = "Save As...";
"lng_context_save_music_to" = "Save to...";
"lng_context_save_music_profile" = "... Profile";
"lng_context_save_music_saved" = "... Saved Messages";
"lng_context_save_music_folder" = "... Downloads";
"lng_context_save_music_about" = "Choose where you want this audio to be saved.";
"lng_context_copy_text" = "Copy Text";
"lng_context_open_gif" = "Open GIF";
"lng_context_save_gif" = "Add to GIFs";
@@ -4733,6 +4827,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_selected_delete_sure_this" = "Do you want to delete this message?";
"lng_selected_delete_sure#one" = "Do you want to delete {count} message?";
"lng_selected_delete_sure#other" = "Do you want to delete {count} messages?";
"lng_selected_remove_saved_music" = "Do you want to remove this file from your profile?";
"lng_saved_music_added" = "Audio added to your Profile.";
"lng_saved_music_removed" = "Audio removed from your Profile.";
"lng_delete_photo_sure" = "Do you want to delete this photo?";
"lng_delete_for_everyone_hint#one" = "This will delete it for everyone in this chat.";
"lng_delete_for_everyone_hint#other" = "This will delete them for everyone in this chat.";
@@ -6258,6 +6355,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_chat_theme_change_wallpaper" = "Change Wallpaper";
"lng_chat_theme_title" = "Select theme";
"lng_chat_theme_cant_voice" = "Sorry, you can't change the chat theme while you have an unsent voice message.";
"lng_chat_theme_gift_replace" = "This gift is already your theme in the chat with {name}. Remove it there and use it here instead?";
"lng_photo_editor_menu_delete" = "Delete";
"lng_photo_editor_menu_flip" = "Flip";
@@ -6295,7 +6393,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_sponsored_hide_ads" = "Hide";
"lng_sponsored_title" = "What are sponsored messages?";
"lng_sponsored_info_description1" = "Unlike other apps, Telegram never uses your private data to target ads. Sponsored messages on Telegram are based solely on the topic of the public channels in which they are shown. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored messages.\n\nUnlike other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties cant spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers a free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:";
"lng_sponsored_info_description1_linked" = "Unlike other apps, Telegram never uses your private data to target ads. {link}\n\nUnlike other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties cant spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:";
"lng_sponsored_info_description1_link" = "Learn more in the Privacy Policy";
"lng_sponsored_info_description1_url" = "https://telegram.org/privacy#5-6-no-ads-based-on-user-data";
"lng_sponsored_info_description2" = "Sponsored Messages are currently in test mode. Once they are fully launched and allow Telegram to cover its basic costs, we will start sharing ad revenue with the owners of public channels in which sponsored messages are displayed.\n\nOnline ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech company should operate together.";
"lng_sponsored_info_menu" = "Advertiser info";
"lng_sponsored_info_submenu" = "Advertiser: {text}";
@@ -6332,6 +6432,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_ringtones_box_title" = "Notification Sound";
"lng_ringtones_box_cloud_subtitle" = "Choose your tone";
"lng_ringtones_box_volume" = "Volume";
"lng_ringtones_box_upload_choose" = "Choose a tone";
"lng_ringtones_box_upload_button" = "Upload Sound";
"lng_ringtones_box_about" = "Right click on any short voice note or MP3 file in chat and select \"Save for Notifications\". It will appear here.";

View File

@@ -51,8 +51,8 @@ var LocationPicker = {
},
init: function (params) {
mapboxgl.accessToken = params.token;
if (params.protocol) {
mapboxgl.config.API_URL = params.protocol + '://domain/api.mapbox.com';
if (location.hostname != 'desktop-app-resource') {
mapboxgl.config.API_URL = location.protocol + '//' + location.host + '/api.mapbox.com';
}
var options = { container: 'map', config: {

View File

@@ -39,6 +39,7 @@
<file alias="topics_list.tgs">../../animations/edit_peers/topics_list.tgs</file>
<file alias="direct_messages.tgs">../../animations/edit_peers/direct_messages.tgs</file>
<file alias="no_chats.tgs">../../animations/no_chats.tgs</file>
<file alias="transcribe_loading.tgs">../../animations/transcribe_loading.tgs</file>
<file alias="dice_idle.tgs">../../animations/dice/dice_idle.tgs</file>
<file alias="dart_idle.tgs">../../animations/dice/dart_idle.tgs</file>

View File

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

View File

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

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 6,0,2,0
PRODUCTVERSION 6,0,2,0
FILEVERSION 6,1,3,0
PRODUCTVERSION 6,1,3,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "6.0.2.0"
VALUE "FileVersion", "6.1.3.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "6.0.2.0"
VALUE "ProductVersion", "6.1.3.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -9,10 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "base/unixtime.h"
#include "core/changelogs.h"
#include "core/application.h"
#include "core/changelogs.h"
#include "core/core_settings.h"
#include "lang/lang_keys.h"
#include "main/main_app_config.h"
#include "main/main_session_settings.h"
#include "main/main_session.h"
namespace Api {
namespace {
@@ -83,7 +86,19 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) {
} // namespace
Authorizations::Authorizations(not_null<ApiWrap*> api)
: _api(&api->instance()) {
: _api(&api->instance())
, _autoconfirmPeriod([=] {
constexpr auto kFallbackCount = 604800;
return api->session().appConfig().get<int>(
u"authorization_autoconfirm_period"_q,
kFallbackCount);
})
, _saveUnreviewed([=] {
api->session().settings().setUnreviewed(_unreviewed);
api->session().saveSettingsDelayed();
}) {
_unreviewed = api->session().settings().unreviewed();
removeExpiredUnreviewed();
Core::App().settings().deviceModelChanges(
) | rpl::start_with_next([=](const QString &model) {
auto changed = false;
@@ -119,6 +134,7 @@ void Authorizations::reload() {
) | ranges::views::transform([](const MTPAuthorization &auth) {
return ParseEntry(auth.data());
}) | ranges::to<List>;
removeExpiredUnreviewed();
refreshCallsDisabledHereFromCloud();
_listChanges.fire({});
}).fail([=] {
@@ -217,11 +233,7 @@ void Authorizations::toggleCallsDisabled(uint64 hash, bool disabled) {
MTP_bool(disabled)
)).done([=] {
_toggleCallsDisabledRequests.remove(hash);
}).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()));
}).fail([=] {
_toggleCallsDisabledRequests.remove(hash);
}).send();
_toggleCallsDisabledRequests.emplace(hash, id);
@@ -265,4 +277,129 @@ crl::time Authorizations::lastReceivedTime() {
return _lastReceived;
}
const std::vector<Data::UnreviewedAuth> &Authorizations::unreviewed() {
removeExpiredUnreviewed();
return _unreviewed;
}
void Authorizations::removeExpiredUnreviewed() {
const auto now = base::unixtime::now();
const auto period = _autoconfirmPeriod();
const auto oldSize = _unreviewed.size();
_unreviewed.erase(
std::remove_if(_unreviewed.begin(), _unreviewed.end(),
[=](const auto &auth) {
return (now - auth.date) >= period;
}),
_unreviewed.end());
if (_unreviewed.size() != oldSize) {
_saveUnreviewed();
}
}
void Authorizations::review(const std::vector<uint64> &hashes, bool confirm) {
for (const auto hash : hashes) {
if (const auto sent = _reviewRequests.take(hash)) {
_api.request(*sent).cancel();
}
}
const auto checkComplete = [=] {
if (_reviewRequests.empty()) {
_saveUnreviewed();
_unreviewedChanges.fire({});
}
};
for (const auto hash : hashes) {
const auto removeFromUnreviewed = [=] {
_unreviewed.erase(
std::remove_if(_unreviewed.begin(), _unreviewed.end(),
[hash](const auto &auth) { return auth.hash == hash; }),
_unreviewed.end());
_reviewRequests.remove(hash);
checkComplete();
};
if (confirm) {
using Flag = MTPaccount_ChangeAuthorizationSettings::Flag;
const auto id = _api.request(MTPaccount_ChangeAuthorizationSettings(
MTP_flags(Flag::f_confirmed),
MTP_long(hash),
MTPBool(), // encrypted_requests_disabled
MTPBool() // call_requests_disabled
)).done([=] {
removeFromUnreviewed();
}).fail([=] {
removeFromUnreviewed();
}).send();
_reviewRequests.emplace(hash, id);
} else {
const auto id = _api.request(MTPaccount_ResetAuthorization(
MTP_long(hash)
)).done([=](const MTPBool &result) {
if (mtpIsTrue(result)) {
_list.erase(
ranges::remove(_list, hash, &Entry::hash),
end(_list));
_listChanges.fire({});
}
removeFromUnreviewed();
}).fail([=] {
removeFromUnreviewed();
}).send();
_reviewRequests.emplace(hash, id);
}
}
}
rpl::producer<> Authorizations::unreviewedChanges() const {
return _unreviewedChanges.events();
}
void Authorizations::apply(const MTPUpdate &update) {
removeExpiredUnreviewed();
update.match([&](const MTPDupdateNewAuthorization &data) {
auto unreviewed = Data::UnreviewedAuth{
.hash = data.vhash().v,
.unconfirmed = data.is_unconfirmed(),
.date = data.vdate().value_or_empty(),
.device = qs(data.vdevice().value_or_empty()),
.location = qs(data.vlocation().value_or_empty())
};
if (!unreviewed.unconfirmed) {
const auto hash = unreviewed.hash;
const auto was = _unreviewed.size();
_unreviewed.erase(
std::remove_if(
_unreviewed.begin(),
_unreviewed.end(),
[hash](const auto &auth) { return auth.hash == hash; }),
_unreviewed.end());
if (was != _unreviewed.size()) {
_saveUnreviewed();
_unreviewedChanges.fire({});
}
return;
}
for (auto &auth : _unreviewed) {
if (auth.hash == unreviewed.hash) {
auth = std::move(unreviewed);
_saveUnreviewed();
_unreviewedChanges.fire({});
return;
}
}
_unreviewed.push_back(std::move(unreviewed));
_saveUnreviewed();
_unreviewedChanges.fire({});
}, [](auto&&) {
Unexpected("Update in Authorizations::apply.");
});
}
} // namespace Api

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_authorization.h"
#include "mtproto/sender.h"
class ApiWrap;
@@ -35,6 +36,8 @@ public:
Fn<void(const MTP::Error &error)> &&fail,
std::optional<uint64> hash = std::nullopt);
void apply(const MTPUpdate &update);
[[nodiscard]] crl::time lastReceivedTime();
[[nodiscard]] List list() const;
@@ -42,6 +45,11 @@ public:
[[nodiscard]] int total() const;
[[nodiscard]] rpl::producer<int> totalValue() const;
[[nodiscard]] const std::vector<Data::UnreviewedAuth> &unreviewed();
[[nodiscard]] rpl::producer<> unreviewedChanges() const;
void review(const std::vector<uint64> &hashes, bool confirm);
void updateTTL(int days);
[[nodiscard]] rpl::producer<int> ttlDays() const;
@@ -57,6 +65,7 @@ public:
private:
void refreshCallsDisabledHereFromCloud();
void removeExpiredUnreviewed();
MTP::Sender _api;
mtpRequestId _requestId = 0;
@@ -64,10 +73,16 @@ private:
List _list;
rpl::event_stream<> _listChanges;
Fn<int()> _autoconfirmPeriod;
std::vector<Data::UnreviewedAuth> _unreviewed;
rpl::event_stream<> _unreviewedChanges;
Fn<void()> _saveUnreviewed;
mtpRequestId _ttlRequestId = 0;
rpl::variable<int> _ttlDays = 0;
base::flat_map<uint64, mtpRequestId> _toggleCallsDisabledRequests;
base::flat_map<uint64, mtpRequestId> _reviewRequests;
rpl::variable<bool> _callsDisabledHere;
crl::time _lastReceived = 0;

View File

@@ -142,13 +142,12 @@ void ConfirmSubscriptionBox(
const auto content = box->verticalLayout();
Ui::AddSkip(content, st::confirmInvitePhotoTop);
const auto userpicWrap = content->add(
object_ptr<Ui::CenterWrap<>>(
content,
object_ptr<Ui::RpWidget>(content)));
const auto userpic = userpicWrap->entity();
const auto userpic = content->add(
object_ptr<Ui::RpWidget>(content),
style::al_top);
const auto photoSize = st::confirmInvitePhotoSize;
userpic->resize(Size(photoSize));
userpic->setNaturalWidth(photoSize);
const auto creditsIconSize = photoSize / 3;
const auto creditsIconCallback =
Ui::PaintOutlinedColoredCreditsIconCallback(
@@ -188,8 +187,8 @@ void ConfirmSubscriptionBox(
}
auto p = QPainter(userpic);
p.drawImage(0, 0, state->frame);
}, userpicWrap->lifetime());
userpicWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
}, userpic->lifetime());
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
if (photo) {
state->photoMedia = photo->createMediaView();
state->photoMedia->wanted(Data::PhotoSize::Small, Data::FileOrigin());
@@ -197,7 +196,7 @@ void ConfirmSubscriptionBox(
session->downloaderTaskFinished(
) | rpl::start_with_next([=] {
userpic->update();
}, userpicWrap->entity()->lifetime());
}, userpic->lifetime());
}
} else {
state->photoEmpty = std::make_unique<Ui::EmptyUserpic>(
@@ -215,43 +214,40 @@ void ConfirmSubscriptionBox(
2.);
box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
object_ptr<Ui::FlatLabel>(
box,
object_ptr<Ui::FlatLabel>(
box,
tr::lng_channel_invite_subscription_title(),
st::inviteLinkSubscribeBoxTitle)));
tr::lng_channel_invite_subscription_title(),
st::inviteLinkSubscribeBoxTitle),
style::al_top);
box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
object_ptr<Ui::FlatLabel>(
box,
object_ptr<Ui::FlatLabel>(
box,
tr::lng_channel_invite_subscription_about(
lt_channel,
rpl::single(Ui::Text::Bold(name)),
lt_price,
tr::lng_credits_summary_options_credits(
lt_count,
rpl::single(amount) | tr::to_count(),
Ui::Text::Bold),
Ui::Text::WithEntities),
st::inviteLinkSubscribeBoxAbout)));
tr::lng_channel_invite_subscription_about(
lt_channel,
rpl::single(Ui::Text::Bold(name)),
lt_price,
tr::lng_credits_summary_options_credits(
lt_count,
rpl::single(amount) | tr::to_count(),
Ui::Text::Bold),
Ui::Text::WithEntities),
st::inviteLinkSubscribeBoxAbout),
style::al_top);
Ui::AddSkip(content);
box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
object_ptr<Ui::FlatLabel>(
box,
object_ptr<Ui::FlatLabel>(
box,
tr::lng_channel_invite_subscription_terms(
lt_link,
rpl::combine(
tr::lng_paid_react_agree_link(),
tr::lng_group_invite_subscription_about_url()
) | rpl::map([](const QString &text, const QString &url) {
return Ui::Text::Link(text, url);
}),
Ui::Text::RichLangValue),
st::inviteLinkSubscribeBoxTerms)));
tr::lng_channel_invite_subscription_terms(
lt_link,
rpl::combine(
tr::lng_paid_react_agree_link(),
tr::lng_group_invite_subscription_about_url()
) | rpl::map([](const QString &text, const QString &url) {
return Ui::Text::Link(text, url);
}),
Ui::Text::RichLangValue),
st::inviteLinkSubscribeBoxTerms),
style::al_top);
{
const auto balance = Settings::AddBalanceWidget(

View File

@@ -153,6 +153,7 @@ Data::CreditsHistoryEntry CreditsHistoryEntryFromTL(
.floodSkip = int(tl.data().vfloodskip_number().value_or(0)),
.converted = stargift && incoming,
.stargift = stargift.has_value(),
.postsSearch = tl.data().is_posts_search(),
.giftUpgraded = tl.data().is_stargift_upgrade(),
.giftResale = tl.data().is_stargift_resale(),
.reaction = tl.data().is_reaction(),

View File

@@ -45,15 +45,17 @@ namespace {
auto options = PremiumSubscriptionOptionsFromTL(tlOptions);
for (auto i = 0; i < options.size(); i++) {
const auto &tlOption = tlOptions[i].data();
const auto currency = qs(tlOption.vcurrency());
const auto perUserText = Ui::FillAmountAndCurrency(
tlOption.vamount().v / float64(tlOption.vusers().v),
qs(tlOption.vcurrency()),
currency,
false);
options[i].costPerMonth = perUserText
+ ' '
+ QChar(0x00D7)
+ ' '
+ QString::number(tlOption.vusers().v);
options[i].currency = currency;
}
return options;
}
@@ -613,24 +615,32 @@ std::vector<GiftOptionData> PremiumGiftCodeOptions::optionsForPeer() const {
return result;
}
Data::PremiumSubscriptionOptions PremiumGiftCodeOptions::options(int amount) {
const auto it = _subscriptionOptions.find(amount);
Data::PremiumSubscriptionOptions PremiumGiftCodeOptions::optionsForGiveaway(
int usersCount) {
const auto skipForStars = [&](Data::PremiumSubscriptionOptions options) {
const auto proj = &Data::PremiumSubscriptionOption::currency;
options.erase(
ranges::remove(options, Ui::kCreditsCurrency, proj),
end(options));
return options;
};
const auto it = _subscriptionOptions.find(usersCount);
if (it != end(_subscriptionOptions)) {
return it->second;
return skipForStars(it->second);
} else {
auto tlOptions = QVector<MTPPremiumGiftCodeOption>();
for (auto i = 0; i < _optionsForOnePerson.months.size(); i++) {
tlOptions.push_back(MTP_premiumGiftCodeOption(
MTP_flags(MTPDpremiumGiftCodeOption::Flags(0)),
MTP_int(amount),
MTP_int(usersCount),
MTP_int(_optionsForOnePerson.months[i]),
MTPstring(),
MTPint(),
MTP_string(_optionsForOnePerson.currencies[i]),
MTP_long(_optionsForOnePerson.totalCosts[i] * amount)));
MTP_long(_optionsForOnePerson.totalCosts[i] * usersCount)));
}
_subscriptionOptions[amount] = GiftCodesFromTL(tlOptions);
return _subscriptionOptions[amount];
_subscriptionOptions[usersCount] = GiftCodesFromTL(tlOptions);
return skipForStars(_subscriptionOptions[usersCount]);
}
}
@@ -853,6 +863,7 @@ std::optional<Data::StarGift> FromTL(
.perUserRemains = data.vper_user_remains().value_or_empty(),
.firstSaleDate = data.vfirst_sale_date().value_or_empty(),
.lastSaleDate = data.vlast_sale_date().value_or_empty(),
.lockedUntilDate = data.vlocked_until_date().value_or_empty(),
.requirePremium = data.is_require_premium(),
.upgradable = data.vupgrade_stars().has_value(),
.birthday = data.is_birthday(),
@@ -880,13 +891,20 @@ std::optional<Data::StarGift> FromTL(
const auto releasedById = data.vreleased_by()
? peerFromMTP(*data.vreleased_by())
: PeerId();
const auto themeUserId = data.vtheme_peer()
? peerFromMTP(*data.vtheme_peer())
: PeerId();
const auto releasedBy = releasedById
? session->data().peer(releasedById).get()
: nullptr;
const auto themeUser = themeUserId
? session->data().peer(themeUserId).get()
: nullptr;
auto result = Data::StarGift{
.id = uint64(data.vid().v),
.id = data.vid().v,
.unique = std::make_shared<Data::UniqueGift>(Data::UniqueGift{
.id = data.vid().v,
.initialGiftId = data.vgift_id().v,
.slug = qs(data.vslug()),
.title = qs(data.vtitle()),
.ownerAddress = qs(data.vowner_address().value_or_empty()),
@@ -895,12 +913,23 @@ std::optional<Data::StarGift> FromTL(
? peerFromMTP(*data.vowner_id())
: PeerId()),
.releasedBy = releasedBy,
.themeUser = themeUser,
.nanoTonForResale = FindTonForResale(data.vresell_amount()),
.starsForResale = FindStarsForResale(data.vresell_amount()),
.number = data.vnum().v,
.onlyAcceptTon = data.is_resale_ton_only(),
.canBeTheme = data.is_theme_available(),
.model = *model,
.pattern = *pattern,
.value = (data.vvalue_amount()
? std::make_shared<Data::UniqueGiftValue>(
Data::UniqueGiftValue{
.currency = qs(
data.vvalue_currency().value_or_empty()),
.valuePrice = int64(
data.vvalue_amount().value_or_empty()),
})
: nullptr),
}),
.document = model->document,
.releasedBy = releasedBy,
@@ -950,20 +979,20 @@ std::optional<Data::SavedStarGift> FromTL(
| ranges::to_vector)
: std::vector<int>()),
.message = (data.vmessage()
? TextWithEntities{
.text = qs(data.vmessage()->data().vtext()),
.entities = Api::EntitiesFromMTP(
session,
data.vmessage()->data().ventities().v),
}
? Api::ParseTextWithEntities(
session,
*data.vmessage())
: TextWithEntities()),
.starsConverted = int64(data.vconvert_stars().value_or_empty()),
.starsUpgradedBySender = int64(
data.vupgrade_stars().value_or_empty()),
.giftPrepayUpgradeHash = qs(
data.vprepaid_upgrade_hash().value_or_empty()),
.fromId = (data.vfrom_id()
? peerFromMTP(*data.vfrom_id())
: PeerId()),
.date = data.vdate().v,
.upgradeSeparate = data.is_upgrade_separate(),
.upgradable = data.is_can_upgrade(),
.anonymous = data.is_name_hidden(),
.pinned = data.is_pinned_to_top() && hasUnique,

View File

@@ -180,7 +180,8 @@ public:
[[nodiscard]] rpl::producer<rpl::no_value, QString> request();
[[nodiscard]] std::vector<GiftOptionData> optionsForPeer() const;
[[nodiscard]] Data::PremiumSubscriptionOptions options(int amount);
[[nodiscard]] Data::PremiumSubscriptionOptions optionsForGiveaway(
int usersCount);
[[nodiscard]] const std::vector<int> &availablePresets() const;
[[nodiscard]] int monthsFromPreset(int monthsIndex);
[[nodiscard]] Payments::InvoicePremiumGiftCode invoice(

View File

@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item_helpers.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "main/main_session_settings.h"
namespace Api {
@@ -25,6 +26,32 @@ Transcribes::Transcribes(not_null<ApiWrap*> api)
, _api(&api->instance()) {
}
bool Transcribes::isRated(not_null<HistoryItem*> item) const {
const auto fullId = item->fullId();
for (const auto &[transcribeId, id] : _ids) {
if (id == fullId) {
return _session->settings().isTranscriptionRated(transcribeId);
}
}
return false;
}
void Transcribes::rate(not_null<HistoryItem*> item, bool isGood) {
const auto fullId = item->fullId();
for (const auto &[transcribeId, id] : _ids) {
if (id == fullId) {
_api.request(MTPmessages_RateTranscribedAudio(
item->history()->peer->input,
MTP_int(item->id),
MTP_long(transcribeId),
MTP_bool(isGood))).send();
_session->settings().markTranscriptionAsRated(transcribeId);
_session->saveSettings();
return;
}
}
}
bool Transcribes::freeFor(not_null<HistoryItem*> item) const {
if (const auto channel = item->history()->peer->asMegagroup()) {
const auto owner = &channel->owner();

View File

@@ -37,6 +37,8 @@ public:
void apply(const MTPDupdateTranscribedAudio &update);
[[nodiscard]] bool freeFor(not_null<HistoryItem*> item) const;
[[nodiscard]] bool isRated(not_null<HistoryItem*> item) const;
void rate(not_null<HistoryItem*> item, bool isGood);
[[nodiscard]] bool trialsSupport();
[[nodiscard]] TimeId trialsRefreshAt();

View File

@@ -2213,6 +2213,10 @@ void Updates::feedUpdate(const MTPUpdate &update) {
}
} break;
case mtpc_updateNewAuthorization: {
session().api().authorizations().apply(update);
} break;
case mtpc_updateServiceNotification: {
const auto &d = update.c_updateServiceNotification();
const auto text = TextWithEntities {

View File

@@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_forum_topic.h"
#include "data/data_forum.h"
#include "data/data_saved_messages.h"
#include "data/data_saved_music.h"
#include "data/data_saved_sublist.h"
#include "data/data_search_controller.h"
#include "data/data_session.h"
@@ -2458,7 +2459,17 @@ void ApiWrap::refreshFileReference(
v::match(origin.data, [&](Data::FileOriginMessage data) {
if (const auto item = _session->data().message(data)) {
const auto media = item->media();
const auto storyId = media ? media->storyId() : FullStoryId();
const auto mediaStory = media ? media->storyId() : FullStoryId();
const auto storyId = mediaStory
? mediaStory
: FullStoryId{
(IsStoryMsgId(item->id)
? item->history()->peer->id
: PeerId()),
(IsStoryMsgId(item->id)
? StoryIdFromMsgId(item->id)
: StoryId())
};
if (storyId) {
request(MTPstories_GetStoriesByID(
_session->data().peer(storyId.peer)->input,
@@ -2469,6 +2480,17 @@ void ApiWrap::refreshFileReference(
request(MTPmessages_GetScheduledMessages(
item->history()->peer->input,
MTP_vector<MTPint>(1, MTP_int(realId))));
} else if (item->isSavedMusicItem()) {
const auto user = item->history()->peer->asUser();
const auto media = item->media();
const auto document = media ? media->document() : nullptr;
if (user && document) {
request(MTPusers_GetSavedMusicByID(
user->inputUser,
MTP_vector<MTPInputDocument>(1, document->mtpInput())));
} else {
fail();
}
} else if (item->isBusinessShortcut()) {
const auto &shortcuts = _session->data().shortcutMessages();
const auto realId = shortcuts.lookupId(item);
@@ -3340,8 +3362,8 @@ void ApiWrap::finishForwarding(const SendAction &action) {
return;
}
forwardMessages(std::move(toForward), action);
history->setForwardDraft(topicRootId, monoforumPeerId, {});
forwardMessages(std::move(toForward), action);
}
_session->data().sendHistoryChangeNotifications();
@@ -3362,6 +3384,22 @@ void ApiWrap::forwardMessages(
auto &histories = _session->data().histories();
for (auto i = begin(draft.items); i != end(draft.items);) {
const auto item = *i;
if (item->isSavedMusicItem()) {
SendExistingDocument(MessageToSend(action), item->media()->document());
i = draft.items.erase(i);
} else {
++i;
}
}
if (draft.items.empty()) {
if (successCallback) {
successCallback();
}
return;
}
struct SharedCallback {
int requestsLeft = 0;
FnMut<void()> callback;

View File

@@ -28,7 +28,7 @@ namespace Data {
struct UpdatedFileReferences;
class WallPaper;
struct ResolvedForwardDraft;
enum class DefaultNotify;
enum class DefaultNotify : uint8_t;
enum class StickersType : uchar;
class Forum;
class ForumTopic;

View File

@@ -13,12 +13,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/update_checker.h"
#include "lang/lang_keys.h"
#include "ui/boxes/confirm_box.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/text/text_utilities.h"
#include "ui/vertical_list.h"
#include "ui/widgets/buttons.h"
#include "ui/wrap/vertical_layout.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_channel_earn.h"
#include "styles/style_chat.h"
#include "styles/style_dialogs.h"
#include "styles/style_menu_icons.h"
#include "styles/style_premium.h"
#include "styles/style_settings.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
@@ -158,3 +166,151 @@ QString currentVersionText() {
#endif
return result;
}
void ArchiveHintBox(
not_null<Ui::GenericBox*> box,
bool unarchiveOnNewMessage,
Fn<void()> onUnarchive) {
box->setNoContentMargin(true);
const auto content = box->verticalLayout().get();
Ui::AddSkip(content);
Ui::AddSkip(content);
Ui::AddSkip(content);
{
const auto &icon = st::dialogsArchiveUserpic;
const auto rect = Rect(icon.size() * 2);
auto owned = object_ptr<Ui::RpWidget>(content);
owned->resize(rect.size());
owned->setNaturalWidth(rect.width());
const auto widget = box->addRow(std::move(owned), style::al_top);
widget->paintRequest(
) | rpl::start_with_next([=] {
auto p = Painter(widget);
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(st::activeButtonBg);
p.drawEllipse(rect);
icon.paintInCenter(p, rect);
}, widget->lifetime());
}
Ui::AddSkip(content);
Ui::AddSkip(content);
box->addRow(
object_ptr<Ui::FlatLabel>(
content,
tr::lng_archive_hint_title(),
st::boxTitle),
style::al_top);
Ui::AddSkip(content);
Ui::AddSkip(content);
{
const auto label = box->addRow(
object_ptr<Ui::FlatLabel>(
content,
(unarchiveOnNewMessage
? tr::lng_archive_hint_about_unmuted
: tr::lng_archive_hint_about)(
lt_link,
tr::lng_archive_hint_about_link(
lt_emoji,
rpl::single(
Ui::Text::IconEmoji(&st::textMoreIconEmoji)),
Ui::Text::RichLangValue
) | rpl::map([](TextWithEntities text) {
return Ui::Text::Link(std::move(text), 1);
}),
Ui::Text::RichLangValue),
st::channelEarnHistoryRecipientLabel));
label->resizeToWidth(box->width()
- rect::m::sum::h(st::boxRowPadding));
label->setLink(
1,
std::make_shared<GenericClickHandler>([=](ClickContext context) {
if (context.button == Qt::LeftButton) {
onUnarchive();
}
}));
}
Ui::AddSkip(content);
Ui::AddSkip(content);
Ui::AddSkip(content);
Ui::AddSkip(content);
{
const auto padding = QMargins(
st::settingsButton.padding.left(),
st::boxRowPadding.top(),
st::boxRowPadding.right(),
st::boxRowPadding.bottom());
const auto addEntry = [&](
rpl::producer<QString> title,
rpl::producer<QString> about,
const style::icon &icon) {
const auto top = content->add(
object_ptr<Ui::FlatLabel>(
content,
std::move(title),
st::channelEarnSemiboldLabel),
padding);
Ui::AddSkip(content, st::channelEarnHistoryThreeSkip);
content->add(
object_ptr<Ui::FlatLabel>(
content,
std::move(about),
st::channelEarnHistoryRecipientLabel),
padding);
const auto left = Ui::CreateChild<Ui::RpWidget>(
box->verticalLayout().get());
left->paintRequest(
) | rpl::start_with_next([=] {
auto p = Painter(left);
icon.paint(p, 0, 0, left->width());
}, left->lifetime());
left->resize(icon.size());
top->geometryValue(
) | rpl::start_with_next([=](const QRect &g) {
left->moveToLeft(
(g.left() - left->width()) / 2,
g.top() + st::channelEarnHistoryThreeSkip);
}, left->lifetime());
};
addEntry(
tr::lng_archive_hint_section_1(),
tr::lng_archive_hint_section_1_info(),
st::menuIconArchive);
Ui::AddSkip(content);
Ui::AddSkip(content);
addEntry(
tr::lng_archive_hint_section_2(),
tr::lng_archive_hint_section_2_info(),
st::menuIconStealth);
Ui::AddSkip(content);
Ui::AddSkip(content);
addEntry(
tr::lng_archive_hint_section_3(),
tr::lng_archive_hint_section_3_info(),
st::menuIconStoriesSavedSection);
Ui::AddSkip(content);
Ui::AddSkip(content);
}
Ui::AddSkip(content);
Ui::AddSkip(content);
Ui::AddSkip(content);
{
const auto &st = st::premiumPreviewDoubledLimitsBox;
box->setStyle(st);
auto button = object_ptr<Ui::RoundButton>(
box,
tr::lng_archive_hint_button(),
st::defaultActiveButton);
button->setTextTransform(
Ui::RoundButton::TextTransform::NoTransform);
button->resizeToWidth(box->width()
- st.buttonPadding.left()
- st.buttonPadding.left());
button->setClickedCallback([=] { box->closeBox(); });
box->addButton(std::move(button));
}
}

View File

@@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/layers/generic_box.h"
void AboutBox(not_null<Ui::GenericBox*> box);
void ArchiveHintBox(
not_null<Ui::GenericBox*> box,
bool unarchiveOnNewMessage,
Fn<void()> onUnarchive);
QString telegramFaqLink();
QString currentVersionText();

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/file_utilities.h"
#include "lang/lang_keys.h"
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "styles/style_boxes.h"
@@ -18,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
namespace {
constexpr auto kUrl = "https://promote.telegram.org"_cs;
constexpr auto kUrl = "https://ads.telegram.org"_cs;
} // namespace
@@ -54,8 +55,16 @@ void AboutSponsoredBox(not_null<Ui::GenericBox*> box) {
};
const auto &stLabel = st::aboutLabel;
const auto info1 = box->addRow(object_ptr<FlatLabel>(box, stLabel));
info1->setText(tr::lng_sponsored_info_description1(tr::now));
auto text1 = tr::lng_sponsored_info_description1_linked(
lt_link,
rpl::combine(
tr::lng_sponsored_info_description1_link(),
tr::lng_sponsored_info_description1_url()
) | rpl::map([](const QString &text, const QString &url) {
return Ui::Text::Link(text, url);
}),
Ui::Text::RichLangValue);
box->addRow(object_ptr<FlatLabel>(box, std::move(text1), stLabel));
box->addSkip(st::sponsoredUrlButtonSkip);
addUrl();

View File

@@ -451,12 +451,12 @@ auto BackgroundBox::Inner::resolveResetCustomPaper() const
return {};
}
const auto nonCustom = Window::Theme::Background()->paper();
const auto themeEmoji = _forPeer->themeEmoji();
if (forChannel() || themeEmoji.isEmpty()) {
const auto themeToken = _forPeer->themeToken();
if (forChannel() || themeToken.isEmpty()) {
return nonCustom;
}
const auto &themes = _forPeer->owner().cloudThemes();
const auto theme = themes.themeForEmoji(themeEmoji);
const auto theme = themes.themeForToken(themeToken);
if (!theme) {
return nonCustom;
}

View File

@@ -161,7 +161,7 @@ constexpr auto kMaxWallPaperSlugLength = 255;
return paper;
}
const auto &themes = session->data().cloudThemes();
if (const auto theme = themes.themeForEmoji(paper.emojiId())) {
if (const auto theme = themes.themeForToken(paper.emojiId())) {
using Type = Data::CloudThemeType;
const auto type = dark ? Type::Dark : Type::Light;
const auto i = theme->settings.find(type);

View File

@@ -758,7 +758,7 @@ backgroundCheckbox: Checkbox(defaultCheckbox) {
textFg: msgServiceFg;
textFgActive: msgServiceFg;
width: -50px;
width: -10px;
margin: margins(0px, 0px, 0px, 0px);
textPosition: point(0px, 6px);
@@ -1116,8 +1116,16 @@ moderateBoxExpandInnerSkip: 2px;
moderateBoxExpandFont: font(11px);
moderateBoxExpandToggleSize: 4px;
moderateBoxExpandToggleFourStrokes: 3px;
moderateBoxExpandIcon: icon{{ "info/edit/expand_arrow_small-flip_vertical", windowActiveTextFg }};
moderateBoxExpandIconDown: icon{{ "info/edit/expand_arrow_small", windowActiveTextFg }};
moderateBoxExpandIcon: IconEmoji{
icon: icon{{ "info/edit/expand_arrow_small-flip_vertical", windowActiveTextFg }};
padding: margins(-2px, -1px, 0px, 0px);
useIconColor: true;
}
moderateBoxExpandIconDown: IconEmoji{
icon: icon{{ "info/edit/expand_arrow_small", windowActiveTextFg }};
padding: margins(-2px, -1px, 0px, 0px);
useIconColor: true;
}
moderateBoxDividerLabel: FlatLabel(boxDividerLabel) {
palette: TextPalette(defaultTextPalette) {
selectLinkFg: windowActiveTextFg;

View File

@@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "ui/rect.h"
#include "ui/wrap/slide_wrap.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@@ -111,16 +112,12 @@ void DeleteMessagesBox::prepare() {
Ui::Text::RichLangValue);
deleteStyle = &st::attentionBoxButton;
} else if (_wipeHistoryJustClear) {
const auto isChannel = peer->isBroadcast();
const auto isPublicGroup = peer->isMegagroup()
&& peer->asChannel()->isPublic();
if (isChannel || isPublicGroup) {
canDelete = false;
}
details.text = isChannel
? tr::lng_no_clear_history_channel(tr::now)
: isPublicGroup
? tr::lng_no_clear_history_group(tr::now)
_revokeJustClearForChannel = true;
details.text = (peer->isChannel() && !peer->isMegagroup())
? tr::lng_sure_delete_channel_history(
tr::now,
lt_channel,
peer->name())
: peer->isSelf()
? tr::lng_sure_delete_saved_messages(tr::now)
: peer->isUser()
@@ -156,7 +153,8 @@ void DeleteMessagesBox::prepare() {
}
deleteStyle = &st::attentionBoxButton;
}
if (auto revoke = revokeText(peer)) {
if (_revokeJustClearForChannel) {
} else if (auto revoke = revokeText(peer)) {
_revoke.create(
this,
revoke->checkbox,
@@ -226,12 +224,14 @@ void DeleteMessagesBox::prepare() {
search->searchMessages({ .from = _moderateFrom });
}
} else {
details.text = (_ids.size() == 1)
details.text = hasSavedMusicMessages()
? tr::lng_selected_remove_saved_music(tr::now)
: (_ids.size() == 1)
? tr::lng_selected_delete_sure_this(tr::now)
: tr::lng_selected_delete_sure(tr::now, lt_count, _ids.size());
if (const auto peer = checkFromSinglePeer()) {
auto count = int(_ids.size());
if (hasScheduledMessages()) {
if (hasScheduledMessages() || hasSavedMusicMessages()) {
} else if (auto revoke = revokeText(peer)) {
const auto &settings = Core::App().settings();
const auto revokeByDefault
@@ -285,6 +285,7 @@ void DeleteMessagesBox::prepare() {
}
}
_text.create(this, rpl::single(std::move(details)), st::boxLabel);
_text->resizeToWidth(st::boxWidth - rect::m::sum::h(st::boxPadding));
if (_wipeHistoryJustClear && _wipeHistoryPeer) {
const auto validator = TTLMenu::TTLValidator(
@@ -314,28 +315,36 @@ void DeleteMessagesBox::prepare() {
addButton(tr::lng_about_done(), [=] { closeBox(); });
}
auto fullHeight = st::boxPadding.top()
+ _text->height()
+ st::boxPadding.bottom();
if (_moderateFrom) {
fullHeight += st::boxMediumSkip;
if (_banUser) {
fullHeight += _banUser->heightNoMargins() + st::boxLittleSkip;
const auto &padding = st::boxPadding;
rpl::combine(
widthValue(),
_text->naturalWidthValue()
) | rpl::start_with_next([=](int full, int) {
_text->resizeToNaturalWidth(full - padding.left() - padding.right());
auto fullHeight = st::boxPadding.top()
+ _text->height()
+ st::boxPadding.bottom();
if (_moderateFrom) {
fullHeight += st::boxMediumSkip;
if (_banUser) {
fullHeight += _banUser->heightNoMargins() + st::boxLittleSkip;
}
fullHeight += _reportSpam->heightNoMargins();
if (_deleteAll) {
fullHeight += st::boxLittleSkip + _deleteAll->heightNoMargins();
}
} else if (_revoke) {
fullHeight += st::boxMediumSkip + _revoke->heightNoMargins();
}
fullHeight += _reportSpam->heightNoMargins();
if (_deleteAll) {
fullHeight += st::boxLittleSkip + _deleteAll->heightNoMargins();
if (_autoDeleteSettings) {
fullHeight += st::boxMediumSkip
+ _autoDeleteSettings->height()
+ st::boxLittleSkip;
}
} else if (_revoke) {
fullHeight += st::boxMediumSkip + _revoke->heightNoMargins();
}
if (_autoDeleteSettings) {
fullHeight += st::boxMediumSkip
+ _autoDeleteSettings->height()
+ st::boxLittleSkip;
}
setDimensions(st::boxWidth, fullHeight);
_fullHeight = fullHeight;
setDimensions(st::boxWidth, fullHeight);
_fullHeight = fullHeight;
}, lifetime());
}
bool DeleteMessagesBox::hasScheduledMessages() const {
@@ -349,6 +358,17 @@ bool DeleteMessagesBox::hasScheduledMessages() const {
return false;
}
bool DeleteMessagesBox::hasSavedMusicMessages() const {
for (const auto &fullId : _ids) {
if (const auto item = _session->data().message(fullId)) {
if (item->isSavedMusicItem()) {
return true;
}
}
}
return false;
}
PeerData *DeleteMessagesBox::checkFromSinglePeer() const {
auto result = (PeerData*)nullptr;
for (const auto &fullId : _ids) {
@@ -554,12 +574,17 @@ void DeleteMessagesBox::deleteAndClear() {
!_revoke->checked());
Core::App().saveSettingsDelayed();
}
const auto revoke = _revoke ? _revoke->checked() : _revokeForBot;
const auto revoke = _revoke
? _revoke->checked()
: (_revokeForBot || _revokeJustClearForChannel);
const auto session = _session;
const auto invokeCallbackAndClose = [&] {
// deleteMessages can initiate closing of the current section,
// which will cause this box to be destroyed.
const auto weak = base::make_weak(this);
if (hasSavedMusicMessages()) {
uiShow()->showToast(tr::lng_saved_music_removed(tr::now));
}
if (const auto callback = _deleteConfirmedCallback) {
callback();
}

View File

@@ -58,6 +58,7 @@ private:
void deleteAndClear();
[[nodiscard]] PeerData *checkFromSinglePeer() const;
[[nodiscard]] bool hasScheduledMessages() const;
[[nodiscard]] bool hasSavedMusicMessages() const;
[[nodiscard]] std::optional<RevokeConfig> revokeText(
not_null<PeerData*> peer) const;
[[nodiscard]] PaidPostType paidPostType() const;
@@ -75,6 +76,7 @@ private:
bool _moderateDeleteAll = false;
bool _revokeForBot = false;
bool _revokeJustClearForChannel = false;
object_ptr<Ui::FlatLabel> _text = { nullptr };
object_ptr<Ui::Checkbox> _revoke = { nullptr };

View File

@@ -8,37 +8,42 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/edit_privacy_box.h"
#include "api/api_global_privacy.h"
#include "apiwrap.h"
#include "boxes/filters/edit_filter_chats_list.h"
#include "ui/effects/premium_graphics.h"
#include "ui/layers/generic_box.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/widgets/shadow.h"
#include "ui/text/format_values.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/painter.h"
#include "ui/vertical_list.h"
#include "boxes/peers/edit_peer_invite_link.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_peer_values.h"
#include "data/data_user.h"
#include "history/history.h"
#include "boxes/peer_list_controllers.h"
#include "lang/lang_keys.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "settings/settings_premium.h"
#include "settings/settings_privacy_controllers.h"
#include "settings/settings_privacy_security.h"
#include "calls/calls_instance.h"
#include "lang/lang_keys.h"
#include "apiwrap.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "data/data_user.h"
#include "data/data_chat.h"
#include "data/data_channel.h"
#include "data/data_peer_values.h"
#include "ui/boxes/peer_qr_box.h"
#include "ui/controls/invite_link_buttons.h"
#include "ui/controls/invite_link_label.h"
#include "ui/effects/premium_graphics.h"
#include "ui/layers/generic_box.h"
#include "ui/painter.h"
#include "ui/text/format_values.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/vertical_list.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/shadow.h"
#include "ui/wrap/slide_wrap.h"
#include "window/window_session_controller.h"
#include "styles/style_boxes.h"
#include "styles/style_settings.h"
#include "styles/style_info.h"
#include "styles/style_layers.h"
#include "styles/style_menu_icons.h"
#include "styles/style_settings.h"
#include "styles/style_window.h"
namespace {
@@ -1293,7 +1298,7 @@ void EditDirectMessagesPriceBox(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
box,
object_ptr<Ui::VerticalLayout>(box)),
{});
style::margins());
wrap->toggle(savedValue.has_value(), anim::type::instant);
wrap->toggleOn(toggle->toggledChanges());
@@ -1312,6 +1317,58 @@ void EditDirectMessagesPriceBox(
*result = stars;
}, box->lifetime());
if (const auto username = channel->username(); !username.isEmpty()) {
Ui::AddSkip(inner);
Ui::AddSubsectionTitle(
inner,
tr::lng_manage_monoforum_link_subtitle());
constexpr auto kDirectParam = "?direct"_cs;
const auto link = channel->session().createInternalLinkFull(username)
+ kDirectParam.utf8();
const auto copyLink = [=] {
TextUtilities::SetClipboardText(TextForMimeData::Simple(link));
box->uiShow()->showToast(tr::lng_group_invite_copied(tr::now));
};
const auto shareLink = [=] {
box->uiShow()->showBox(ShareInviteLinkBox(channel, link));
};
const auto createMenu = [=] {
auto result = base::make_unique_q<Ui::PopupMenu>(
inner,
st::popupMenuWithIcons);
result->addAction(
tr::lng_group_invite_context_qr(tr::now),
[=] {
box->uiShow()->showBox(Box([=](
not_null<Ui::GenericBox*> qrBox) {
Ui::FillPeerQrBox(qrBox, channel, link, nullptr);
}));
},
&st::menuIconQrCode);
return result;
};
auto linkText = Ui::Text::StripUrlProtocol(link);
const auto label = inner->lifetime().make_state<Ui::InviteLinkLabel>(
inner,
rpl::single(std::move(linkText)),
createMenu);
inner->add(
label->take(),
st::inviteLinkFieldPadding);
label->clicks() | rpl::start_with_next(copyLink, label->lifetime());
Ui::AddSkip(inner);
AddCopyShareLinkButtons(inner, copyLink, shareLink);
Ui::AddSkip(inner);
Ui::AddSkip(inner);
Ui::AddDivider(inner);
}
box->addButton(tr::lng_settings_save(), [=] {
const auto weak = base::make_weak(box);
callback(toggle->toggled() ? *result : std::optional<int>());

View File

@@ -143,6 +143,11 @@ private:
int id,
TextWithEntities text,
anim::type animated);
void insertTask(
int beforeIndex,
int id,
TextWithEntities text,
anim::type animated);
void initTaskField(not_null<Task*> task, TextWithEntities text);
void checkLastTask();
void validateState();
@@ -150,6 +155,9 @@ private:
void destroy(std::unique_ptr<Task> task);
void removeDestroyed(not_null<Task*> field);
int findField(not_null<Ui::InputField*> field) const;
void handlePaste(
not_null<Ui::InputField*> field,
const QStringList &list);
not_null<Ui::BoxContent*> _box;
not_null<Ui::VerticalLayout*> _container;
@@ -187,6 +195,27 @@ void InitField(
options);
}
[[nodiscard]] QStringList ParsePastedList(const QString &text) {
auto list = QStringView(text).split('\n');
for (auto i = list.begin(); i != list.end();) {
auto text = i->trimmed();
if (text.isEmpty() && (i + 1 != list.end())) {
i = list.erase(i);
} else {
*i++ = text;
}
}
if (list.size() < 2) {
return {};
}
auto result = QStringList();
result.reserve(list.size());
for (const auto &view : list) {
result.push_back(view.toString());
}
return result;
}
not_null<Ui::FlatLabel*> CreateWarningLabel(
not_null<QWidget*> parent,
not_null<Ui::InputField*> field,
@@ -263,7 +292,7 @@ Tasks::Task::Task(
session->user()->isPremium()
? st::createPollOptionFieldPremium
: st::createPollOptionField,
Ui::InputField::Mode::NoNewlines,
Ui::InputField::Mode::MultiLine,
tr::lng_todo_create_list_add()))
, _limit(session->appConfig().todoListItemTextLimit()) {
InitField(outer, _field, session);
@@ -629,24 +658,35 @@ void Tasks::addTask(
int id,
TextWithEntities text,
anim::type animated) {
insertTask(_list.size(), id, std::move(text), animated);
}
void Tasks::insertTask(
int beforeIndex,
int id,
TextWithEntities text,
anim::type animated) {
if (full()) {
return;
}
Assert(beforeIndex >= 0 && beforeIndex <= _list.size());
if (_list.size() > 1) {
(*(_list.end() - 2))->removePlaceholder();
(*(_list.end() - 2))->toggleRemoveAlways(true);
}
const auto locked = id && _existingLocked;
_list.push_back(std::make_unique<Task>(
_box,
_container,
&_controller->session(),
id,
_position + _list.size() + _destroyed.size(),
locked));
const auto field = _list.back()->field();
const auto i = _list.insert(
begin(_list) + beforeIndex,
std::make_unique<Task>(
_box,
_container,
&_controller->session(),
id,
_position + beforeIndex + _destroyed.size(),
locked));
const auto field = i->get()->field();
if (!locked) {
initTaskField(_list.back().get(), std::move(text));
initTaskField(i->get(), std::move(text));
} else {
InitMessageFieldHandlers(
_controller,
@@ -659,7 +699,7 @@ void Tasks::addTask(
});
}
field->finishAnimating();
_list.back()->show(animated);
i->get()->show(animated);
fixShadows();
}
@@ -706,6 +746,14 @@ void Tasks::initTaskField(not_null<Task*> task, TextWithEntities text) {
}, field->lifetime());
field->changes(
) | rpl::start_with_next([=] {
auto list = ParsePastedList(field->getLastText());
if (!list.empty()) {
field->setText(list.front());
field->forceProcessContentsChanges();
list.pop_front();
handlePaste(field, list);
}
Ui::PostponeCall(crl::guard(field, [=] {
validateState();
}));
@@ -793,6 +841,27 @@ int Tasks::findField(not_null<Ui::InputField*> field) const {
return result;
}
void Tasks::handlePaste(
not_null<Ui::InputField*> field,
const QStringList &list) {
const auto index = findField(field);
for (auto i = 0, count = int(list.size()); i != count; ++i) {
insertTask(
index + 1 + i,
0, // id
TextWithEntities{ list[i] },
anim::type::instant);
}
const auto last = std::min(
int(index + list.size()),
int(_list.size()) - 1);
const auto add = _list[last]->field();
crl::on_main(add, [=] {
add->setCursorPosition(add->getLastText().size());
add->setFocus();
});
}
void Tasks::checkLastTask() {
removeEmptyTail();
addEmptyTask();

View File

@@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/filter_icons.h"
#include "ui/layers/generic_box.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/power_saving.h"
#include "ui/vertical_list.h"
#include "ui/widgets/buttons.h"
@@ -593,7 +594,7 @@ void EditFilterBox(
) | rpl::start_with_next([=](const QRect &r) {
const auto h = st::normalFont->height;
preview->setGeometry(
colors->x(),
rect::right(colors) - st::settingsFilterTagPreviewSkip,
r.y() + (r.height() - h) / 2 + st::lineWidth,
colors->width(),
h);

View File

@@ -538,25 +538,24 @@ void LinkController::addHeader(not_null<Ui::VerticalLayout*> container) {
const auto isStatic = _filterTitle.isStatic;
verticalLayout->add(
object_ptr<Ui::CenterWrap<>>(
object_ptr<Ui::FlatLabel>(
verticalLayout,
object_ptr<Ui::FlatLabel>(
verticalLayout,
(_data.url.isEmpty()
? tr::lng_filters_link_no_about(Ui::Text::WithEntities)
: tr::lng_filters_link_share_about(
lt_folder,
rpl::single(Ui::Text::Wrapped(
_filterTitle.text,
EntityType::Bold)),
Ui::Text::WithEntities)),
st::settingsFilterDividerLabel,
st::defaultPopupMenu,
Core::TextContext({
.session = &_window->session(),
.customEmojiLoopLimit = isStatic ? -1 : 0,
}))),
st::filterLinkDividerLabelPadding);
(_data.url.isEmpty()
? tr::lng_filters_link_no_about(Ui::Text::WithEntities)
: tr::lng_filters_link_share_about(
lt_folder,
rpl::single(Ui::Text::Wrapped(
_filterTitle.text,
EntityType::Bold)),
Ui::Text::WithEntities)),
st::settingsFilterDividerLabel,
st::defaultPopupMenu,
Core::TextContext({
.session = &_window->session(),
.customEmojiLoopLimit = isStatic ? -1 : 0,
})),
st::filterLinkDividerLabelPadding,
style::al_top)->setTryMakeSimilarLines(true);
verticalLayout->geometryValue(
) | rpl::start_with_next([=](const QRect &r) {

View File

@@ -24,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rect.h"
#include "ui/text/text_utilities.h"
#include "ui/vertical_list.h"
#include "ui/widgets/label_with_custom_emoji.h"
#include "window/window_session_controller.h"
#include "styles/style_boxes.h"
#include "styles/style_channel_earn.h"
@@ -53,9 +52,8 @@ void GiftCreditsBox(
Ui::AddSkip(content);
const auto &stUser = st::premiumGiftsUserpicButton;
const auto userpicWrap = content->add(
object_ptr<Ui::CenterWrap<>>(
content,
object_ptr<Ui::UserpicButton>(content, peer, stUser)));
object_ptr<Ui::UserpicButton>(content, peer, stUser),
style::al_top);
userpicWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
Ui::AddSkip(content);
Ui::AddSkip(content);
@@ -78,19 +76,17 @@ void GiftCreditsBox(
u"internal:stars_examples"_q);
});
content->add(
object_ptr<Ui::CenterWrap<>>(
object_ptr<Ui::FlatLabel>(
content,
Ui::CreateLabelWithCustomEmoji(
content,
tr::lng_credits_box_history_entry_gift_out_about(
lt_user,
rpl::single(TextWithEntities{ peer->shortName() }),
lt_link,
std::move(link),
Ui::Text::RichLangValue),
Core::TextContext({ .session = &peer->session() }),
st::creditsBoxAbout)),
st::boxRowPadding);
tr::lng_credits_box_history_entry_gift_out_about(
lt_user,
rpl::single(TextWithEntities{ peer->shortName() }),
lt_link,
std::move(link),
Ui::Text::RichLangValue),
st::creditsBoxAbout),
st::boxRowPadding,
style::al_top);
}
Ui::AddSkip(content);
Ui::AddSkip(box->verticalLayout());
@@ -101,6 +97,7 @@ void GiftCreditsBox(
peer,
CreditsAmount(),
[=] { gifted(); box->uiShow()->hideLayer(); },
box->showFinishes(),
tr::lng_credits_summary_options_subtitle(),
{});

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_premium.h"
#include "api/api_premium_option.h"
#include "apiwrap.h"
#include "base/event_filter.h"
#include "base/timer_rpl.h"
#include "base/unixtime.h"
#include "base/weak_ptr.h"
@@ -31,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h"
#include "data/stickers/data_custom_emoji.h"
#include "info/channel_statistics/boosts/giveaway/boost_badge.h" // InfiniteRadialAnimationWidget.
#include "info/channel_statistics/earn/earn_icons.h"
#include "info/profile/info_profile_badge.h"
#include "info/profile/info_profile_values.h"
#include "lang/lang_keys.h"
@@ -54,6 +56,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rect.h"
#include "ui/ui_utility.h"
#include "ui/vertical_list.h"
#include "ui/text/custom_emoji_helper.h"
#include "ui/text/format_values.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/widgets/checkbox.h"
@@ -75,9 +79,81 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
constexpr auto kRarityTooltipDuration = 3 * crl::time(1000);
constexpr auto kTooltipDuration = 3 * crl::time(1000);
constexpr auto kPriceTooltipDuration = 6 * crl::time(1000);
constexpr auto kHorizontalBar = QChar(0x2015);
struct InfoTooltipData {
not_null<Ui::RpWidget*> parent;
Ui::ImportantTooltip *raw = nullptr;
};
void ShowInfoTooltip(
std::shared_ptr<InfoTooltipData> data,
not_null<QWidget*> target,
rpl::producer<TextWithEntities> text,
int duration) {
if (data->raw) {
data->raw->toggleAnimated(false);
}
const auto parent = data->parent;
const auto tooltip = Ui::CreateChild<Ui::ImportantTooltip>(
parent,
Ui::MakeNiceTooltipLabel(
parent,
std::move(text),
st::boxWideWidth,
st::defaultImportantTooltipLabel),
st::defaultImportantTooltip);
tooltip->toggleFast(false);
base::install_event_filter(tooltip, qApp, [=](not_null<QEvent*> e) {
if (e->type() == QEvent::MouseButtonPress) {
tooltip->toggleAnimated(false);
}
return base::EventFilterResult::Continue;
});
const auto update = [=] {
const auto geometry = Ui::MapFrom(parent, target, target->rect());
const auto countPosition = [=](QSize size) {
const auto left = geometry.x()
+ (geometry.width() - size.width()) / 2;
const auto right = parent->width()
- st::normalFont->spacew;
return QPoint(
std::max(std::min(left, right - size.width()), 0),
geometry.y() - size.height() - st::normalFont->descent);
};
tooltip->pointAt(geometry, RectPart::Top, countPosition);
};
parent->widthValue(
) | rpl::start_with_next(update, tooltip->lifetime());
update();
tooltip->toggleAnimated(true);
data->raw = tooltip;
tooltip->shownValue() | rpl::filter(
!rpl::mappers::_1
) | rpl::start_with_next([=] {
crl::on_main(tooltip, [=] {
if (tooltip->isHidden()) {
if (data->raw == tooltip) {
data->raw = nullptr;
}
delete tooltip;
}
});
}, tooltip->lifetime());
base::timer_once(
duration
) | rpl::start_with_next([=] {
tooltip->toggleAnimated(false);
}, tooltip->lifetime());
}
[[nodiscard]] QString CreateMessageLink(
not_null<Main::Session*> session,
PeerId peerId,
@@ -110,6 +186,33 @@ constexpr auto kHorizontalBar = QChar(0x2015);
};
}
[[nodiscard]] TextWithEntities FormatValuePrice(
int64 price,
QString currency,
bool approximately = false) {
auto result = TextWithEntities();
if (approximately) {
result.append('~');
}
return result.append(Ui::FillAmountAndCurrency(price, currency));
}
[[nodiscard]] TextWithEntities FormatValueDate(TimeId date) {
const auto parsed = base::unixtime::parse(date).date();
const auto day = parsed.day();
const auto month = parsed.month();
const auto year = parsed.year();
return { tr::lng_month_day_year(
tr::now,
lt_month,
Lang::MonthDay(month)(tr::now),
lt_day,
QString::number(day),
lt_year,
QString::number(year))
};
}
[[nodiscard]] object_ptr<Ui::RpWidget> MakeLinkCopyIcon(
not_null<QWidget*> parent) {
auto result = object_ptr<Ui::RpWidget>(parent);
@@ -156,6 +259,61 @@ constexpr auto kHorizontalBar = QChar(0x2015);
: st::giveawayGiftCodeValueMultiline));
}
[[nodiscard]] object_ptr<Ui::RpWidget> MakeValueWithSmallButton(
not_null<Ui::TableLayout*> table,
not_null<Ui::RpWidget*> value,
rpl::producer<QString> buttonText,
Fn<void(not_null<Ui::RpWidget*> button)> handler = nullptr,
int topSkip = 0) {
class MarginedWidget final : public Ui::RpWidget {
public:
using RpWidget::RpWidget;
QMargins getMargins() const override {
return { 0, 0, 0, st::giveawayGiftCodePeerMargin.bottom() };
}
};
auto result = object_ptr<MarginedWidget>(table);
const auto raw = result.data();
value->setParent(raw);
value->show();
const auto button = Ui::CreateChild<Ui::RoundButton>(
raw,
std::move(buttonText),
table->st().smallButton);
button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
if (handler) {
button->setClickedCallback([button, handler = std::move(handler)] {
handler(button);
});
} else {
button->setAttribute(Qt::WA_TransparentForMouseEvents);
}
rpl::combine(
raw->widthValue(),
button->widthValue(),
value->naturalWidthValue()
) | rpl::start_with_next([=](int width, int buttonWidth, int) {
const auto buttonSkip = st::normalFont->spacew + buttonWidth;
value->resizeToNaturalWidth(width - buttonSkip);
value->moveToLeft(0, 0, width);
button->moveToLeft(
rect::right(value) + st::normalFont->spacew,
(topSkip
+ (table->st().defaultValue.style.font->ascent
- table->st().smallButton.style.font->ascent)),
width);
}, value->lifetime());
value->heightValue() | rpl::start_with_next([=](int height) {
const auto bottom = st::giveawayGiftCodePeerMargin.bottom();
raw->resize(raw->width(), height + bottom);
}, raw->lifetime());
return result;
}
[[nodiscard]] object_ptr<Ui::RpWidget> MakePeerTableValue(
not_null<Ui::TableLayout*> table,
std::shared_ptr<ChatHelpers::Show> show,
@@ -174,38 +332,18 @@ constexpr auto kHorizontalBar = QChar(0x2015);
raw,
(button && handler) ? peer->shortName() : peer->name(),
table->st().defaultValue);
const auto send = (button && handler)
? Ui::CreateChild<Ui::RoundButton>(
raw,
std::move(button),
table->st().smallButton)
: nullptr;
if (send) {
send->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
send->setClickedCallback(std::move(handler));
}
rpl::combine(
raw->widthValue(),
send ? send->widthValue() : rpl::single(0)
) | rpl::start_with_next([=](int width, int sendWidth) {
raw->widthValue() | rpl::start_with_next([=](int width) {
const auto position = st::giveawayGiftCodeNamePosition;
const auto sendSkip = sendWidth
? (st::normalFont->spacew + sendWidth)
: 0;
label->resizeToNaturalWidth(width - position.x() - sendSkip);
label->resizeToNaturalWidth(width - position.x());
label->moveToLeft(position.x(), position.y(), width);
const auto top = (raw->height() - userpic->height()) / 2;
userpic->moveToLeft(0, top, width);
if (send) {
send->moveToLeft(
position.x() + label->width() + st::normalFont->spacew,
(position.y()
+ table->st().defaultValue.style.font->ascent
- table->st().smallButton.style.font->ascent),
width);
}
}, label->lifetime());
label->naturalWidthValue() | rpl::start_with_next([=](int width) {
raw->setNaturalWidth(st::giveawayGiftCodeNamePosition.x() + width);
}, label->lifetime());
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
label->setAttribute(Qt::WA_TransparentForMouseEvents);
label->setTextColorOverride(table->st().defaultValue.palette.linkFg->c);
@@ -214,7 +352,15 @@ constexpr auto kHorizontalBar = QChar(0x2015);
show->showBox(PrepareShortInfoBox(peer, show));
});
return result;
if (!button || !handler) {
return result;
}
return MakeValueWithSmallButton(
table,
result.release(),
std::move(button),
[=](not_null<Ui::RpWidget*> button) { handler(); },
st::giveawayGiftCodeNamePosition.y());
}
[[nodiscard]] object_ptr<Ui::RpWidget> MakePeerWithStatusValue(
@@ -222,24 +368,21 @@ constexpr auto kHorizontalBar = QChar(0x2015);
std::shared_ptr<ChatHelpers::Show> show,
PeerId id,
Fn<void(not_null<Ui::RpWidget*>, EmojiStatusId)> pushStatusId) {
auto result = object_ptr<Ui::AbstractButton>(table);
auto result = object_ptr<Ui::RpWidget>(table);
const auto raw = result.data();
const auto &st = st::giveawayGiftCodeUserpic;
raw->resize(raw->width(), st.photoSize);
const auto peerLabel = MakePeerTableValue(table, show, id).release();
peerLabel->setParent(raw);
peerLabel->show();
const auto peer = show->session().data().peer(id);
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(raw, peer, st);
const auto label = Ui::CreateChild<Ui::FlatLabel>(
raw,
peer->name(),
table->st().defaultValue);
raw->resize(raw->width(), peerLabel->height());
using namespace Info::Profile;
struct State {
rpl::variable<Badge::Content> content;
};
const auto state = label->lifetime().make_state<State>();
const auto peer = show->session().data().peer(id);
const auto state = peerLabel->lifetime().make_state<State>();
state->content = EmojiStatusIdValue(
peer
) | rpl::map([=](EmojiStatusId emojiStatusId) {
@@ -252,7 +395,7 @@ constexpr auto kHorizontalBar = QChar(0x2015);
.emojiStatusId = emojiStatusId,
};
});
const auto badge = label->lifetime().make_state<Badge>(
const auto badge = peerLabel->lifetime().make_state<Badge>(
raw,
st::infoPeerBadge,
&peer->session(),
@@ -270,32 +413,19 @@ constexpr auto kHorizontalBar = QChar(0x2015);
raw->widthValue(),
rpl::single(rpl::empty) | rpl::then(badge->updated())
) | rpl::start_with_next([=](int width, const auto &) {
const auto position = st::giveawayGiftCodeNamePosition;
const auto badgeWidget = badge->widget();
const auto badgeSkip = badgeWidget
? (st::normalFont->spacew + badgeWidget->width())
: 0;
label->resizeToNaturalWidth(width - position.x() - badgeSkip);
label->moveToLeft(position.x(), position.y(), width);
const auto top = (raw->height() - userpic->height()) / 2;
userpic->moveToLeft(0, top, width);
peerLabel->resizeToNaturalWidth(width - badgeSkip);
peerLabel->moveToLeft(0, 0, width);
if (badgeWidget) {
badgeWidget->moveToLeft(
position.x() + label->width() + st::normalFont->spacew,
(position.y()
+ table->st().defaultValue.style.font->ascent
- table->st().smallButton.style.font->ascent),
peerLabel->width() + st::normalFont->spacew,
st::giftBoxByStarsStarTop,
width);
}
}, label->lifetime());
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
label->setAttribute(Qt::WA_TransparentForMouseEvents);
label->setTextColorOverride(table->st().defaultValue.palette.linkFg->c);
raw->setClickedCallback([=] {
show->showBox(PrepareShortInfoBox(peer, show));
});
}, raw->lifetime());
return result;
}
@@ -340,7 +470,7 @@ void AddTableRow(
not_null<Ui::TableLayout*> table,
rpl::producer<QString> label,
object_ptr<Ui::RpWidget> value,
style::margins valueMargins) {
style::margins valueMargins = st::giveawayGiftCodeValueMargin) {
table->addRow(
(label
? object_ptr<Ui::FlatLabel>(
@@ -353,55 +483,108 @@ void AddTableRow(
valueMargins);
}
[[nodiscard]] object_ptr<Ui::RpWidget> MakePriceWithChangePercentValue(
not_null<Ui::TableLayout*> table,
const std::shared_ptr<Data::UniqueGiftValue> &value) {
auto label = object_ptr<Ui::FlatLabel>(
table,
rpl::single(FormatValuePrice(value->lastSalePrice, value->currency)),
table->st().defaultValue);
label->setAttribute(Qt::WA_TransparentForMouseEvents);
const auto initial = value->initialSalePrice;
if (!initial) {
return label;
}
const auto diff = (100 * (value->lastSalePrice - initial))
/ float64(initial);
const auto use = (std::abs(diff) >= 10.)
? base::SafeRound(diff)
: (int(base::SafeRound(diff * 100)) / 100.);
const auto prefix = (use > 0) ? u"+"_q : QString();
const auto percent = Lang::FormatExactCountDecimal(use) + '%';
auto text = rpl::single(prefix + percent);
return MakeValueWithSmallButton(table, label, std::move(text));
}
[[nodiscard]] object_ptr<Ui::RpWidget> MakePriceValueWithTooltip(
not_null<Ui::TableLayout*> table,
std::shared_ptr<InfoTooltipData> data,
TextWithEntities price,
TextWithEntities tooltip) {
const auto label = Ui::CreateChild<Ui::FlatLabel>(
table,
rpl::single(price),
table->st().defaultValue);
label->setAttribute(Qt::WA_TransparentForMouseEvents);
const auto handler = [=](not_null<Ui::RpWidget*> button) {
ShowInfoTooltip(
data,
button,
rpl::single(tooltip),
kPriceTooltipDuration);
};
auto text = rpl::single(u"?"_q);
return MakeValueWithSmallButton(table, label, std::move(text), handler);
}
[[nodiscard]] object_ptr<Ui::RpWidget> MakeMinimumPriceValue(
not_null<Ui::TableLayout*> table,
std::shared_ptr<InfoTooltipData> tooltip,
const std::shared_ptr<Data::UniqueGift> &unique) {
const auto &value = unique->value;
const auto text = FormatValuePrice(value->minimumPrice, value->currency);
return MakePriceValueWithTooltip(
table,
std::move(tooltip),
text,
tr::lng_gift_value_minimum_price_tooltip(
tr::now,
lt_amount,
Ui::Text::Bold(text.text),
lt_gift,
Ui::Text::Bold(unique->title),
Ui::Text::WithEntities));
}
[[nodiscard]] object_ptr<Ui::RpWidget> MakeAveragePriceValue(
not_null<Ui::TableLayout*> table,
std::shared_ptr<InfoTooltipData> tooltip,
const std::shared_ptr<Data::UniqueGift> &unique) {
const auto &value = unique->value;
const auto text = FormatValuePrice(value->averagePrice, value->currency);
return MakePriceValueWithTooltip(
table,
std::move(tooltip),
text,
tr::lng_gift_value_average_price_tooltip(
tr::now,
lt_amount,
Ui::Text::Bold(text.text),
lt_gift,
Ui::Text::Bold(unique->title),
Ui::Text::WithEntities));
}
[[nodiscard]] object_ptr<Ui::RpWidget> MakeAttributeValue(
not_null<Ui::TableLayout*> table,
const Data::UniqueGiftAttribute &attribute,
Fn<void(not_null<Ui::RpWidget*>, int)> showTooltip) {
auto result = object_ptr<Ui::RpWidget>(table);
const auto raw = result.data();
const auto label = Ui::CreateChild<Ui::FlatLabel>(
raw,
table,
attribute.name,
table->st().defaultValue);
const auto permille = attribute.rarityPermille;
const auto text = QString::number(permille / 10.) + '%';
const auto rarity = Ui::CreateChild<Ui::RoundButton>(
raw,
rpl::single(text),
table->st().smallButton);
rarity->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
rpl::combine(
raw->widthValue(),
rarity->widthValue()
) | rpl::start_with_next([=](int width, int convertWidth) {
const auto convertSkip = convertWidth
? (st::normalFont->spacew + convertWidth)
: 0;
label->resizeToNaturalWidth(width - convertSkip);
label->moveToLeft(0, 0, width);
rarity->moveToLeft(
label->width() + st::normalFont->spacew,
(table->st().defaultValue.style.font->ascent
- table->st().smallButton.style.font->ascent),
width);
}, label->lifetime());
label->heightValue() | rpl::start_with_next([=](int height) {
raw->resize(
raw->width(),
height + st::giveawayGiftCodeValueMargin.bottom());
}, raw->lifetime());
label->setAttribute(Qt::WA_TransparentForMouseEvents);
rarity->setClickedCallback([=] {
showTooltip(rarity, permille);
});
const auto permille = attribute.rarityPermille;
auto text = rpl::single(QString::number(permille / 10.) + '%');
return result;
const auto handler = [=](not_null<Ui::RpWidget*> button) {
showTooltip(button, permille);
};
return MakeValueWithSmallButton(table, label, std::move(text), handler);
}
[[nodiscard]] object_ptr<Ui::RpWidget> MakeStarGiftStarsValue(
@@ -409,113 +592,88 @@ void AddTableRow(
std::shared_ptr<ChatHelpers::Show> show,
const Data::CreditsHistoryEntry &entry,
Fn<void()> convertToStars) {
auto result = object_ptr<Ui::RpWidget>(table);
const auto raw = result.data();
const auto star = Ui::CreateSingleStarWidget(
raw,
table->st().defaultValue.style.font->height);
const auto label = Ui::CreateChild<Ui::FlatLabel>(
raw,
Lang::FormatCreditsAmountDecimal(entry.credits),
auto helper = Ui::Text::CustomEmojiHelper();
const auto price = helper.paletteDependent(Ui::Earn::IconCreditsEmoji(
)).append(' ').append(Lang::FormatCreditsAmountDecimal(entry.credits));
auto label = object_ptr<Ui::FlatLabel>(
table,
rpl::single(price),
table->st().defaultValue,
st::defaultPopupMenu);
const auto convert = convertToStars
? Ui::CreateChild<Ui::RoundButton>(
raw,
tr::lng_gift_sell_small(
lt_count_decimal,
rpl::single(entry.starsConverted * 1.)),
table->st().smallButton)
: nullptr;
if (convert) {
using namespace Ui;
convert->setTextTransform(RoundButton::TextTransform::NoTransform);
convert->setClickedCallback(std::move(convertToStars));
}
rpl::combine(
raw->widthValue(),
convert ? convert->widthValue() : rpl::single(0)
) | rpl::start_with_next([=](int width, int convertWidth) {
const auto convertSkip = convertWidth
? (st::normalFont->spacew + convertWidth)
: 0;
const auto labelLeft = rect::right(star) + st::normalFont->spacew;
label->resizeToNaturalWidth(width - convertSkip - labelLeft);
star->moveToLeft(0, 0, width);
label->moveToLeft(labelLeft, 0, width);
if (convert) {
convert->moveToLeft(
rect::right(label) + st::normalFont->spacew,
(table->st().defaultValue.style.font->ascent
- table->st().smallButton.style.font->ascent),
width);
}
}, label->lifetime());
label->heightValue() | rpl::start_with_next([=](int height) {
raw->resize(
raw->width(),
height + st::giveawayGiftCodeValueMargin.bottom());
}, raw->lifetime());
st::defaultPopupMenu,
helper.context());
label->setAttribute(Qt::WA_TransparentForMouseEvents);
return result;
if (!convertToStars) {
return label;
}
const auto handler = [=](not_null<Ui::RpWidget*> button) {
convertToStars();
};
auto text = tr::lng_gift_sell_small(
lt_count_decimal,
rpl::single(entry.starsConverted * 1.));
return MakeValueWithSmallButton(
table,
label.release(),
std::move(text),
handler);
}
[[nodiscard]] object_ptr<Ui::RpWidget> MakeNonUniqueStatusTableValue(
[[nodiscard]] object_ptr<Ui::RpWidget> MakeUniqueGiftValueValue(
not_null<Ui::TableLayout*> table,
Fn<void()> startUpgrade) {
auto result = object_ptr<Ui::RpWidget>(table);
const auto raw = result.data();
std::shared_ptr<ChatHelpers::Show> show,
const Data::CreditsHistoryEntry &entry,
Settings::CreditsEntryBoxStyleOverrides st) {
const auto unique = entry.uniqueGift;
const auto value = unique ? unique->value : nullptr;
const auto loading = std::make_shared<bool>(false);
const auto label = Ui::CreateChild<Ui::FlatLabel>(
raw,
tr::lng_gift_unique_status_non(),
table,
rpl::single(
FormatValuePrice(value->valuePrice, value->currency, true)),
table->st().defaultValue,
st::defaultPopupMenu);
const auto upgrade = startUpgrade
? Ui::CreateChild<Ui::RoundButton>(
raw,
tr::lng_gift_unique_status_upgrade(),
table->st().smallButton)
: (Ui::RoundButton*)(nullptr);
if (upgrade) {
using namespace Ui;
upgrade->setTextTransform(RoundButton::TextTransform::NoTransform);
upgrade->setClickedCallback(startUpgrade);
}
rpl::combine(
raw->widthValue(),
upgrade ? upgrade->widthValue() : rpl::single(0)
) | rpl::start_with_next([=](int width, int toggleWidth) {
const auto toggleSkip = toggleWidth
? (st::normalFont->spacew + toggleWidth)
: 0;
label->resizeToNaturalWidth(width - toggleSkip);
label->moveToLeft(0, 0, width);
if (upgrade) {
upgrade->moveToLeft(
label->width() + st::normalFont->spacew,
(table->st().defaultValue.style.font->ascent
- table->st().smallButton.style.font->ascent),
width);
}
}, label->lifetime());
label->heightValue() | rpl::start_with_next([=](int height) {
raw->resize(
raw->width(),
height + st::giveawayGiftCodeValueMargin.bottom());
}, raw->lifetime());
label->setAttribute(Qt::WA_TransparentForMouseEvents);
return result;
const auto handler = [=](not_null<Ui::RpWidget*> button) {
if (value->initialPriceStars) {
show->show(Box(Settings::UniqueGiftValueBox, show, entry, st));
} else if (*loading) {
return;
}
*loading = true;
show->session().api().request(MTPpayments_GetUniqueStarGiftValueInfo(
MTP_string(unique->slug)
)).done([=](const MTPpayments_UniqueStarGiftValueInfo &result) {
*loading = false;
const auto &data = result.data();
value->currency = qs(data.vcurrency());
value->valuePrice = data.vvalue().v;
value->initialSaleDate = data.vinitial_sale_date().v;
value->initialPriceStars = CreditsAmount(
data.vinitial_sale_stars().v);
value->initialSalePrice = data.vinitial_sale_price().v;
value->lastSaleDate = data.vlast_sale_date().value_or_empty();
value->lastSalePrice = data.vlast_sale_price().value_or_empty();
value->lastSaleFragment = data.is_last_sale_on_fragment();
value->minimumPrice = data.vfloor_price().value_or_empty();
value->averagePrice = data.vaverage_price().value_or_empty();
value->forSaleOnTelegram = data.vlisted_count().value_or_empty();
value->forSaleOnFragment = int(
data.vfragment_listed_count().value_or_empty());
value->fragmentUrl = qs(
data.vfragment_listed_url().value_or_empty());
show->show(Box(Settings::UniqueGiftValueBox, show, entry, st));
}).send();
};
return MakeValueWithSmallButton(
table,
label,
tr::lng_gift_unique_value_learn_more(),
handler);
}
not_null<Ui::FlatLabel*> AddTableRow(
@@ -530,11 +688,7 @@ not_null<Ui::FlatLabel*> AddTableRow(
st::defaultPopupMenu,
context);
const auto result = widget.data();
AddTableRow(
table,
std::move(label),
std::move(widget),
st::giveawayGiftCodeValueMargin);
AddTableRow(table, std::move(label), std::move(widget));
return result;
}
@@ -766,7 +920,8 @@ void GiftCodeBox(
std::move(shareLink),
Ui::Text::WithEntities)),
st::giveawayGiftCodeFooter),
st::giveawayGiftCodeFooterMargin);
st::giveawayGiftCodeFooterMargin,
style::al_top);
footer->setClickHandlerFilter([=](const auto &...) {
ShareWithFriend(controller, slug);
return false;
@@ -783,7 +938,7 @@ void GiftCodeBox(
close->moveToRight(0, 0);
}, box->lifetime());
const auto button = box->addButton(rpl::conditional(
box->addButton(rpl::conditional(
state->used.value(),
tr::lng_box_ok(),
tr::lng_gift_link_use()
@@ -812,15 +967,6 @@ void GiftCodeBox(
controller->session().api().premium().applyGiftCode(slug, done);
}
});
const auto buttonPadding = st::giveawayGiftCodeBox.buttonPadding;
const auto buttonWidth = st::boxWideWidth
- buttonPadding.left()
- buttonPadding.right();
button->widthValue() | rpl::filter([=] {
return (button->widthNoMargins() != buttonWidth);
}) | rpl::start_with_next([=] {
button->resizeToWidth(buttonWidth);
}, button->lifetime());
}
void GiftCodePendingBox(
@@ -908,7 +1054,8 @@ void GiftCodePendingBox(
box,
tr::lng_gift_link_pending_footer(),
st::giveawayGiftCodeFooter),
st::giveawayGiftCodeFooterMargin);
st::giveawayGiftCodeFooterMargin,
style::al_top);
const auto close = Ui::CreateChild<Ui::IconButton>(
box.get(),
@@ -920,16 +1067,7 @@ void GiftCodePendingBox(
close->moveToRight(0, 0);
}, box->lifetime());
const auto button = box->addButton(tr::lng_close(), closeCallback);
const auto buttonPadding = st::giveawayGiftCodeBox.buttonPadding;
const auto buttonWidth = st::boxWideWidth
- buttonPadding.left()
- buttonPadding.right();
button->widthValue() | rpl::filter([=] {
return (button->widthNoMargins() != buttonWidth);
}) | rpl::start_with_next([=] {
button->resizeToWidth(buttonWidth);
}, button->lifetime());
box->addButton(tr::lng_close(), closeCallback);
}
void ResolveGiftCode(
@@ -1013,12 +1151,11 @@ void GiveawayInfoBox(
label->setTextColorOverride(st::windowActiveTextFg->c);
}
const auto result = box->addRow(
object_ptr<Ui::PaddingWrap<Ui::CenterWrap<Ui::FlatLabel>>>(
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
box.get(),
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
box.get(),
std::move(label)),
QMargins(0, skip, 0, skip)));
std::move(label),
QMargins(0, skip, 0, skip)),
style::al_justify);
result->paintRequest() | rpl::start_with_next([=] {
auto p = QPainter(result);
p.setPen(Qt::NoPen);
@@ -1211,7 +1348,8 @@ void GiveawayInfoBox(
: tr::lng_prizes_cancelled()),
st::giveawayRefundedLabel),
st::giveawayRefundedPadding),
{ padding.left(), 0, padding.right(), padding.bottom() });
{ padding.left(), 0, padding.right(), padding.bottom() },
style::al_top);
const auto bg = wrap->lifetime().make_state<Ui::RoundRect>(
st::boxRadius,
st::attentionBoxButton.textBgOver);
@@ -1281,64 +1419,13 @@ void AddStarGiftTable(
const auto giftToChannel = entry.giftChannelSavedId
&& peerIsChannel(PeerId(entry.bareEntryOwnerId));
const auto raw = std::make_shared<Ui::ImportantTooltip*>(nullptr);
const auto tooltip = std::make_shared<InfoTooltipData>(InfoTooltipData{
.parent = container,
});
const auto showTooltip = [=](
not_null<Ui::RpWidget*> widget,
rpl::producer<TextWithEntities> text) {
if (*raw) {
(*raw)->toggleAnimated(false);
}
const auto tooltip = Ui::CreateChild<Ui::ImportantTooltip>(
container,
Ui::MakeNiceTooltipLabel(
container,
std::move(text),
st::boxWideWidth,
st::defaultImportantTooltipLabel),
st::defaultImportantTooltip);
tooltip->toggleFast(false);
const auto update = [=] {
const auto geometry = Ui::MapFrom(
container,
widget,
widget->rect());
const auto countPosition = [=](QSize size) {
const auto left = geometry.x()
+ (geometry.width() - size.width()) / 2;
const auto right = container->width()
- st::normalFont->spacew;
return QPoint(
std::max(std::min(left, right - size.width()), 0),
geometry.y() - size.height() - st::normalFont->descent);
};
tooltip->pointAt(geometry, RectPart::Top, countPosition);
};
container->widthValue(
) | rpl::start_with_next(update, tooltip->lifetime());
update();
tooltip->toggleAnimated(true);
*raw = tooltip;
tooltip->shownValue() | rpl::filter(
!rpl::mappers::_1
) | rpl::start_with_next([=] {
crl::on_main(tooltip, [=] {
if (tooltip->isHidden()) {
if (*raw == tooltip) {
*raw = nullptr;
}
delete tooltip;
}
});
}, tooltip->lifetime());
base::timer_once(
kRarityTooltipDuration
) | rpl::start_with_next([=] {
tooltip->toggleAnimated(false);
}, tooltip->lifetime());
ShowInfoTooltip(tooltip, widget, std::move(text), kTooltipDuration);
};
if (unique && entry.bareGiftResaleRecipientId) {
@@ -1395,8 +1482,7 @@ void AddStarGiftTable(
AddTableRow(
table,
tr::lng_gift_unique_owner(),
std::move(label),
st::giveawayGiftCodeValueMargin);
std::move(label));
}
} else if (giftToChannel) {
AddTableRow(
@@ -1457,8 +1543,6 @@ void AddStarGiftTable(
tr::lng_gift_link_label_date(),
rpl::single(Ui::Text::WithEntities(langDateTime(entry.date))));
}
const auto marginWithButton = st::giveawayGiftCodeValueMargin
- QMargins(0, 0, 0, st::giveawayGiftCodeValueMargin.bottom());
if (unique) {
const auto showRarity = [=](
not_null<Ui::RpWidget*> widget,
@@ -1472,18 +1556,15 @@ void AddStarGiftTable(
AddTableRow(
table,
tr::lng_gift_unique_model(),
MakeAttributeValue(table, unique->model, showRarity),
marginWithButton);
MakeAttributeValue(table, unique->model, showRarity));
AddTableRow(
table,
tr::lng_gift_unique_backdrop(),
MakeAttributeValue(table, unique->backdrop, showRarity),
marginWithButton);
MakeAttributeValue(table, unique->backdrop, showRarity));
AddTableRow(
table,
tr::lng_gift_unique_symbol(),
MakeAttributeValue(table, unique->pattern, showRarity),
marginWithButton);
MakeAttributeValue(table, unique->pattern, showRarity));
} else {
AddTableRow(
table,
@@ -1492,8 +1573,7 @@ void AddStarGiftTable(
table,
show,
entry,
std::move(convertToStars)),
marginWithButton);
std::move(convertToStars)));
}
if (entry.limitedCount > 0 && !entry.giftRefunded) {
auto amount = rpl::single(TextWithEntities{
@@ -1525,10 +1605,15 @@ void AddStarGiftTable(
AddTableRow(
table,
tr::lng_gift_unique_status(),
MakeNonUniqueStatusTableValue(table, std::move(startUpgrade)),
marginWithButton);
tr::lng_gift_unique_status_non(Ui::Text::WithEntities));
}
if (unique) {
if (unique->value) {
AddTableRow(
table,
tr::lng_gift_unique_value(),
MakeUniqueGiftValueValue(table, show, entry, st));
}
const auto &original = unique->originalDetails;
if (original.recipientId) {
const auto owner = &show->session().data();
@@ -1674,7 +1759,8 @@ void AddCreditsHistoryEntryTable(
show,
peerId);
}
if (actorId || (!entry.starrefCommission && peerId)) {
if (!entry.postsSearch
&& (actorId || (!entry.starrefCommission && peerId))) {
auto text = entry.starrefCommission
? tr::lng_credits_box_history_entry_referred()
: entry.in
@@ -1712,8 +1798,7 @@ void AddCreditsHistoryEntryTable(
(entry.reaction
? tr::lng_credits_box_history_entry_message
: tr::lng_credits_box_history_entry_media)(),
std::move(label),
st::giveawayGiftCodeValueMargin);
std::move(label));
}
}
using Type = Data::CreditsHistoryEntry::PeerType;
@@ -1820,8 +1905,7 @@ void AddCreditsHistoryEntryTable(
AddTableRow(
table,
tr::lng_credits_box_history_entry_id(),
std::move(label),
st::giveawayGiftCodeValueMargin);
std::move(label));
}
if (entry.floodSkip) {
AddTableRow(
@@ -2009,7 +2093,71 @@ void AddChannelEarnTable(
AddTableRow(
table,
tr::lng_credits_box_history_entry_id(),
std::move(label),
st::giveawayGiftCodeValueMargin);
std::move(label));
}
}
void AddUniqueGiftValueTable(
std::shared_ptr<ChatHelpers::Show> show,
not_null<Ui::VerticalLayout*> container,
Settings::CreditsEntryBoxStyleOverrides st,
const Data::CreditsHistoryEntry &entry) {
const auto value = entry.uniqueGift ? entry.uniqueGift->value : nullptr;
auto table = container->add(
object_ptr<Ui::TableLayout>(
container,
st.table ? *st.table : st::giveawayGiftCodeTable),
st::giveawayGiftCodeTableMargin);
if (value->initialSaleDate) {
AddTableRow(
table,
tr::lng_gift_value_initial_sale(),
rpl::single(FormatValueDate(value->initialSaleDate)));
}
auto helper = Ui::Text::CustomEmojiHelper();
auto starIcon = helper.paletteDependent(
Ui::Earn::IconCreditsEmoji());
AddTableRow(
table,
tr::lng_gift_value_initial_price(),
tr::lng_gift_value_initial_price_value(
lt_stars,
rpl::single(starIcon.append(' ').append(
Lang::FormatCreditsAmountDecimal(value->initialPriceStars)
)),
lt_amount,
rpl::single(FormatValuePrice(
value->initialSalePrice,
value->currency,
true)),
Ui::Text::WithEntities),
helper.context());
if (value->lastSaleDate) {
AddTableRow(
table,
tr::lng_gift_value_last_sale(),
rpl::single(FormatValueDate(value->lastSaleDate)));
}
if (value->lastSalePrice) {
AddTableRow(
table,
tr::lng_gift_value_last_price(),
MakePriceWithChangePercentValue(table, value));
}
const auto tooltip = std::make_shared<InfoTooltipData>(InfoTooltipData{
.parent = container,
});
if (value->minimumPrice) {
AddTableRow(
table,
tr::lng_gift_value_minimum_price(),
MakeMinimumPriceValue(table, tooltip, entry.uniqueGift));
}
if (value->averagePrice) {
AddTableRow(
table,
tr::lng_gift_vlaue_average_price(),
MakeAveragePriceValue(table, tooltip, entry.uniqueGift));
}
}

View File

@@ -106,3 +106,9 @@ void AddChannelEarnTable(
std::shared_ptr<Ui::Show> show,
not_null<Ui::VerticalLayout*> container,
const Data::CreditsHistoryEntry &entry);
void AddUniqueGiftValueTable(
std::shared_ptr<ChatHelpers::Show> show,
not_null<Ui::VerticalLayout*> container,
Settings::CreditsEntryBoxStyleOverrides st,
const Data::CreditsHistoryEntry &entry);

View File

@@ -403,22 +403,10 @@ void CreateModerateMessagesBox(
const auto container = wrap->entity();
wrap->toggle(false, anim::type::instant);
const auto session = &participants.front()->session();
const auto emojiMargin = QMargins(
-st::moderateBoxExpandInnerSkip,
-st::moderateBoxExpandInnerSkip / 2,
0,
0);
const auto emojiUp = Ui::Text::SingleCustomEmoji(
session->data().customEmojiManager().registerInternalEmoji(
st::moderateBoxExpandIcon,
emojiMargin,
false));
const auto emojiDown = Ui::Text::SingleCustomEmoji(
session->data().customEmojiManager().registerInternalEmoji(
st::moderateBoxExpandIconDown,
emojiMargin,
false));
const auto emojiUp = Ui::Text::IconEmoji(
&st::moderateBoxExpandIcon);
const auto emojiDown = Ui::Text::IconEmoji(
&st::moderateBoxExpandIconDown);
auto label = object_ptr<Ui::FlatLabel>(
inner,
@@ -463,9 +451,7 @@ void CreateModerateMessagesBox(
Ui::Text::WithEntities);
}) | rpl::flatten_latest(
) | rpl::start_with_next([=](const TextWithEntities &text) {
raw->setMarkedText(
Ui::Text::Link(text, u"internal:"_q),
Core::TextContext({ .session = session }));
raw->setMarkedText(Ui::Text::Link(text, u"internal:"_q));
}, label->lifetime());
Ui::AddSkip(inner);

View File

@@ -1460,7 +1460,8 @@ void PeerListContent::setSearchMode(PeerListSearchMode mode) {
_loadingAnimation = Ui::CreateLoadingPeerListItemWidget(
this,
_st.item,
2);
2,
_controller->computeListSt().bg->c);
}
}
} else {

View File

@@ -233,7 +233,8 @@ void FillUpgradeToPremiumCover(
container,
rpl::single(text),
st::inviteForbiddenInfo),
st::inviteForbiddenInfoPadding);
st::inviteForbiddenInfoPadding,
style::al_top);
}
void SimpleForbiddenBox(
@@ -511,7 +512,8 @@ void InviteForbiddenController::setComplexCover() {
if (_can) {
container->add(
MakeShowOrLabel(container, tr::lng_invite_upgrade_or()),
st::inviteForbiddenOrLabelPadding);
st::inviteForbiddenOrLabelPadding,
style::al_justify);
}
container->add(
object_ptr<Ui::FlatLabel>(
@@ -520,7 +522,8 @@ void InviteForbiddenController::setComplexCover() {
? tr::lng_invite_upgrade_via_title()
: tr::lng_via_link_cant()),
st::inviteForbiddenTitle),
st::inviteForbiddenTitlePadding);
st::inviteForbiddenTitlePadding,
style::al_top);
const auto about = _can
? (_peer->isBroadcast()
@@ -544,7 +547,8 @@ void InviteForbiddenController::setComplexCover() {
container,
rpl::single(about),
st::inviteForbiddenInfo),
st::inviteForbiddenInfoPadding);
st::inviteForbiddenInfoPadding,
style::al_top);
}
delegate()->peerListSetAboveWidget(std::move(cover));
}

View File

@@ -77,7 +77,9 @@ DefaultIconEmoji::DefaultIconEmoji(
std::move(value) | rpl::start_with_next([=](DefaultIcon value) {
_icon = value;
_image = QImage();
repaint();
if (repaint) {
repaint();
}
}, _lifetime);
}

View File

@@ -15,7 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/background_box.h"
#include "boxes/stickers_box.h"
#include "chat_helpers/compose/compose_show.h"
#include "core/ui_integration.h" // TextContext
#include "data/stickers/data_custom_emoji.h"
#include "data/stickers/data_stickers.h"
#include "data/data_changes.h"
@@ -82,7 +81,6 @@ public:
bool selected);
[[nodiscard]] uint8 index() const;
int naturalWidth() const override;
void setSelected(bool selected);
@@ -164,7 +162,6 @@ private:
void updateText();
const uint32 _level;
const TextWithEntities _icon;
const Ui::Text::MarkedContext _context;
Ui::Text::String _text;
bool _minimal = false;
@@ -183,6 +180,15 @@ ColorSample::ColorSample(
colorIndex
) | rpl::start_with_next([=](uint8 index) {
_index = index;
setNaturalWidth([&] {
if (_name.isEmpty() || _style->colorPatternIndex(_index)) {
return st::settingsColorSampleSize;
}
const auto padding = st::settingsColorSamplePadding;
return std::max(
padding.left() + _name.maxWidth() + padding.right(),
padding.top() + st::semiboldFont->height + padding.bottom());
}());
update();
}, lifetime());
}
@@ -197,6 +203,7 @@ ColorSample::ColorSample(
, _index(colorIndex)
, _selected(selected)
, _simple(true) {
setNaturalWidth(st::settingsColorSampleSize);
}
void ColorSample::setSelected(bool selected) {
@@ -278,16 +285,6 @@ uint8 ColorSample::index() const {
return _index;
}
int ColorSample::naturalWidth() const {
if (_name.isEmpty() || _style->colorPatternIndex(_index)) {
return st::settingsColorSampleSize;
}
const auto padding = st::settingsColorSamplePadding;
return std::max(
padding.left() + _name.maxWidth() + padding.right(),
padding.top() + st::semiboldFont->height + padding.bottom());
}
PreviewWrap::PreviewWrap(
not_null<Ui::GenericBox*> box,
std::shared_ptr<Ui::ChatStyle> style,
@@ -460,22 +457,12 @@ LevelBadge::LevelBadge(
uint32 level,
not_null<Main::Session*> session)
: Ui::RpWidget(parent)
, _level(level)
, _icon(Ui::Text::SingleCustomEmoji(
session->data().customEmojiManager().registerInternalEmoji(
st::settingsLevelBadgeLock,
QMargins(0, st::settingsLevelBadgeLockSkip, 0, 0),
false)))
, _context(Core::TextContext({
.session = session,
.repaint = [this] { update(); },
})) {
, _level(level) {
updateText();
}
void LevelBadge::updateText() {
auto text = _icon;
text.append(' ');
auto text = Ui::Text::IconEmoji(&st::settingsLevelBadgeLock).append(' ');
if (!_minimal) {
text.append(tr::lng_boost_level(
tr::now,
@@ -490,7 +477,7 @@ void LevelBadge::updateText() {
st,
text,
kMarkupTextOptions,
_context);
Ui::Text::MarkedContext{ .repaint = [=] { update(); } });
const auto &padding = st::settingsColorSamplePadding;
QWidget::resize(
_text.maxWidth() + rect::m::sum::h(padding),
@@ -1213,7 +1200,7 @@ void EditPeerColorBox(
peer,
state->index.value(),
state->emojiId.value()
), {});
), style::margins());
auto indices = peer->session().api().peerColors().suggestedValue();
const auto margin = st::settingsColorRadioMargin;

View File

@@ -1257,7 +1257,7 @@ void Controller::fillAutoTranslateButton() {
_navigation->uiShow(),
_peer,
[=](int level) {
if (const auto strong = weak.get()) {
if (weak.get()) {
state->isLocked = (level < requiredLevel);
}
return (level < requiredLevel)

View File

@@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "history/history_item_helpers.h" // GetErrorForSending.
#include "history/view/history_view_group_call_bar.h" // GenerateUserpics...
#include "info/channel_statistics/earn/earn_icons.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "qr/qr_generate.h"
@@ -42,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/controls/userpic_button.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/text/custom_emoji_helper.h"
#include "ui/text/format_values.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
@@ -247,6 +249,9 @@ private:
const Role _role = Role::Joined;
rpl::variable<LinkData> _data;
Ui::Text::CustomEmojiHelper _emojiHelper;
TextWithEntities _creditsEmoji;
base::unique_qptr<Ui::PopupMenu> _menu;
rpl::event_stream<Processed> _processed;
@@ -408,6 +413,8 @@ Controller::Controller(
const auto current = _data.current();
_link = current.link;
_revoked = current.revoked;
_creditsEmoji = _emojiHelper.paletteDependent(
Ui::Earn::IconCreditsEmoji());
}
rpl::producer<LinkData> Controller::dataValue() const {
@@ -725,7 +732,7 @@ void Controller::setupAboveJoinedWidget() {
? tr::lng_group_invite_subscription_info_title(
tr::now,
lt_emoji,
session().data().customEmojiManager().creditsEmoji(),
_creditsEmoji,
lt_price,
{ QString::number(current.subscription.credits) },
lt_multiplier,
@@ -736,15 +743,12 @@ void Controller::setupAboveJoinedWidget() {
: tr::lng_group_invite_subscription_info_title_none(
tr::now,
lt_emoji,
session().data().customEmojiManager().creditsEmoji(),
_creditsEmoji,
lt_price,
{ QString::number(current.subscription.credits) },
Ui::Text::WithEntities),
kMarkupTextOptions,
Core::TextContext({
.session = &session(),
.repaint = [=] { widget->update(); },
}));
_emojiHelper.context([=] { widget->update(); }));
auto &lifetime = widget->lifetime();
const auto rateValue = lifetime.make_state<rpl::variable<float64>>(
session().credits().rateValue(_peer));
@@ -965,43 +969,41 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
const auto photoSize = st::boostReplaceUserpic.photoSize;
const auto session = &row->peer()->session();
content->add(object_ptr<Ui::CenterWrap<>>(
content,
Settings::SubscriptionUserpic(content, channel, photoSize)));
content->add(
Settings::SubscriptionUserpic(content, channel, photoSize),
style::al_top);
Ui::AddSkip(content);
Ui::AddSkip(content);
box->addRow(object_ptr<Ui::CenterWrap<>>(
box,
box->addRow(
object_ptr<Ui::FlatLabel>(
box,
tr::lng_credits_box_subscription_title(),
st::creditsBoxAboutTitle)));
st::creditsBoxAboutTitle),
style::al_top);
Ui::AddSkip(content);
const auto subtitle1 = box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
object_ptr<Ui::FlatLabel>(
box,
object_ptr<Ui::FlatLabel>(
box,
st::creditsTopupPrice)))->entity();
st::creditsTopupPrice),
style::al_top);
subtitle1->setMarkedText(
tr::lng_credits_subscription_subtitle(
tr::now,
lt_emoji,
session->data().customEmojiManager().creditsEmoji(),
_creditsEmoji,
lt_cost,
{ QString::number(data.subscription.credits) },
Ui::Text::WithEntities),
Core::TextContext({ .session = session }));
_emojiHelper.context());
const auto subtitle2 = box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
object_ptr<Ui::FlatLabel>(
box,
object_ptr<Ui::FlatLabel>(
box,
st::creditsTopupPrice)))->entity();
st::creditsTopupPrice),
style::al_top);
session->credits().rateValue(
channel
) | rpl::start_with_next([=, currency = u"USD"_q](float64 rate) {
@@ -1023,8 +1025,7 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
Ui::AddSkip(content);
Ui::AddSkip(content);
box->addRow(object_ptr<Ui::CenterWrap<>>(
box,
box->addRow(
object_ptr<Ui::FlatLabel>(
box,
tr::lng_credits_box_out_about(
@@ -1033,18 +1034,12 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
) | Ui::Text::ToLink(
tr::lng_credits_box_out_about_link(tr::now)),
Ui::Text::WithEntities),
st::creditsBoxAboutDivider)));
st::creditsBoxAboutDivider),
style::al_top);
const auto button = box->addButton(tr::lng_box_ok(), [=] {
box->addButton(tr::lng_box_ok(), [=] {
box->closeBox();
});
const auto buttonWidth = st::boxWidth
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
button->widthValue() | rpl::filter([=] {
return (button->widthNoMargins() != buttonWidth);
}) | rpl::start_with_next([=] {
button->resizeToWidth(buttonWidth);
}, button->lifetime());
}));
}

View File

@@ -886,32 +886,18 @@ rpl::producer<int> AddSlowmodeSlider(
return secondsCount->value();
}
void AddBoostsUnrestrictLabels(
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session) {
void AddBoostsUnrestrictLabels(not_null<Ui::VerticalLayout*> container) {
const auto labels = container->add(
object_ptr<Ui::FixedHeightWidget>(container, st::normalFont->height),
st::slowmodeLabelsMargin);
const auto manager = &session->data().customEmojiManager();
const auto one = Ui::Text::SingleCustomEmoji(
manager->registerInternalEmoji(
st::boostMessageIcon,
st::boostMessageIconPadding));
const auto many = Ui::Text::SingleCustomEmoji(
manager->registerInternalEmoji(
st::boostsMessageIcon,
st::boostsMessageIconPadding));
const auto context = Core::TextContext({
.session = session,
.customEmojiLoopLimit = 1,
});
const auto one = Ui::Text::IconEmoji(&st::boostMessageIcon);
const auto many = Ui::Text::IconEmoji(&st::boostsMessageIcon);
for (auto i = 0; i != kBoostsUnrestrictValues; ++i) {
const auto label = Ui::CreateChild<Ui::FlatLabel>(
labels,
st::boostsUnrestrictLabel);
label->setMarkedText(
TextWithEntities(i ? many : one).append(QString::number(i + 1)),
context);
TextWithEntities(i ? many : one).append(QString::number(i + 1)));
rpl::combine(
labels->widthValue(),
label->widthValue()
@@ -977,7 +963,7 @@ rpl::producer<int> AddBoostsUnrestrictSlider(
const auto inner = outer->entity();
AddBoostsUnrestrictLabels(inner, &peer->session());
AddBoostsUnrestrictLabels(inner);
const auto slider = inner->add(
object_ptr<Ui::MediaSlider>(inner, st::localStorageLimitSlider),

View File

@@ -221,7 +221,8 @@ void Controller::prepare() {
Ui::Text::RichLangValue),
Ui::Text::RichLangValue),
st::boostReassignText),
st::boxRowPadding);
st::boxRowPadding,
style::al_top);
delegate()->peerListSetAboveWidget(std::move(above));
const auto now = base::unixtime::now();

View File

@@ -585,14 +585,14 @@ void ChannelsLimitBox(
const auto content = box->addRow(
object_ptr<PeerListContent>(box, controller),
{});
style::margins());
delegate->setContent(content);
controller->setDelegate(delegate);
const auto count = 100;
const auto placeholder = box->addRow(
object_ptr<PeerListDummy>(box, count, st::defaultPeerList),
{});
style::margins());
using namespace rpl::mappers;
controller->countValue(
@@ -676,14 +676,14 @@ void PublicLinksLimitBox(
const auto content = box->addRow(
object_ptr<PeerListContent>(box, controller),
{});
style::margins());
delegate->setContent(content);
controller->setDelegate(delegate);
const auto count = defaultLimit;
const auto placeholder = box->addRow(
object_ptr<PeerListDummy>(box, count, st::defaultPeerList),
{});
style::margins());
using namespace rpl::mappers;
controller->countValue(

View File

@@ -902,7 +902,7 @@ void PreviewBox(
const auto outer = box->addRow(
ChatBackPreview(box, size.height(), back),
{});
style::margins());
struct Hiding {
not_null<Ui::RpWidget*> widget;
@@ -1079,26 +1079,21 @@ void PreviewBox(
auto text = state->selected.value(
) | rpl::map(SectionAbout) | rpl::flatten_latest();
const auto padding = st::premiumPreviewAboutPadding;
const auto available = size.width() - padding.left() - padding.right();
auto titleLabel = object_ptr<Ui::FlatLabel>(
box,
std::move(title),
st::premiumPreviewAboutTitle);
titleLabel->resizeToWidth(available);
box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
object_ptr<Ui::FlatLabel>(
box,
std::move(titleLabel)),
st::premiumPreviewAboutTitlePadding);
auto textLabel = object_ptr<Ui::FlatLabel>(
box,
std::move(text),
st::premiumPreviewAbout);
textLabel->resizeToWidth(available);
std::move(title),
st::premiumPreviewAboutTitle),
st::premiumPreviewAboutTitlePadding,
style::al_top);
box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(box, std::move(textLabel)),
padding);
object_ptr<Ui::FlatLabel>(
box,
std::move(text),
st::premiumPreviewAbout),
st::premiumPreviewAboutPadding,
style::al_top
)->setTryMakeSimilarLines(true);
box->addRow(
CreateSwitch(box->verticalLayout(), &state->selected, state->order),
st::premiumDotsMargin);

View File

@@ -7,8 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/ringtones_box.h"
#include "data/notify/data_peer_notify_volume.h"
#include "data/notify/data_peer_notify_settings.h"
#include "api/api_ringtones.h"
#include "apiwrap.h"
#include "ui/widgets/continuous_sliders.h"
#include "base/call_delayed.h"
#include "base/event_filter.h"
#include "base/timer_rpl.h"
@@ -21,11 +24,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document_media.h"
#include "data/data_document_resolver.h"
#include "data/data_thread.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "data/notify/data_notify_settings.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "media/audio/media_audio.h"
#include "ui/wrap/slide_wrap.h"
#include "platform/platform_notifications_manager.h"
#include "settings/settings_common.h"
#include "ui/boxes/confirm_box.h"
@@ -111,7 +117,8 @@ void RingtonesBox(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session,
Data::NotifySound selected,
Fn<void(Data::NotifySound)> save) {
Fn<void(Data::NotifySound)> save,
Data::VolumeController volumeController) {
box->setTitle(tr::lng_ringtones_box_title());
const auto container = box->verticalLayout();
@@ -128,12 +135,17 @@ void RingtonesBox(
QPointer<Ui::Radiobutton> defaultButton;
QPointer<Ui::Radiobutton> chosenButton;
std::vector<QPointer<Ui::Radiobutton>> buttons;
ushort presavedVolume = 0;
};
const auto state = container->lifetime().make_state<State>(State{
.group = std::make_shared<Ui::RadiobuttonGroup>(),
.chosen = selected,
});
const auto volumeOverride = [volume = volumeController.volume] {
return volume ? (0.01 * volume()) : -1;
};
const auto addToGroup = [=](
not_null<Ui::VerticalLayout*> verticalLayout,
int value,
@@ -156,7 +168,10 @@ void RingtonesBox(
if (value == kDefaultValue) {
state->defaultButton = button;
button->setClickedCallback([=] {
Core::App().notifications().playSound(session, 0);
Core::App().notifications().playSound(
session,
0,
volumeOverride());
});
}
if (value < 0) {
@@ -170,7 +185,8 @@ void RingtonesBox(
if (media->loaded()) {
Core::App().notifications().playSound(
session,
media->owner()->id);
media->owner()->id,
volumeOverride());
}
});
base::install_event_filter(button, [=](not_null<QEvent*> e) {
@@ -320,6 +336,31 @@ void RingtonesBox(
}));
});
if (volumeController.volume && volumeController.saveVolume) {
auto saveAndTestVolume = [=](ushort currentVolume) {
state->presavedVolume = currentVolume;
const auto value = state->group->current();
if (value != kNoSoundValue) {
Core::App().notifications().playSound(
session,
(value == kDefaultValue)
? 0
: state->medias[value]->owner()->id,
0.01 * currentVolume);
}
};
Ui::AddRingtonesVolumeSlider(
container,
state->group->value() | rpl::map([=](int value) {
return value != kNoSoundValue;
}),
tr::lng_ringtones_box_volume(),
Data::VolumeController{
base::take(volumeController.volume),
std::move(saveAndTestVolume),
});
}
box->addSkip(st::ringtonesBoxSkip);
Ui::AddDividerText(container, tr::lng_ringtones_box_about());
@@ -333,6 +374,9 @@ void RingtonesBox(
: (value == kNoSoundValue)
? Data::NotifySound{ .none = true }
: Data::NotifySound{ .id = state->medias[value]->owner()->id };
if (state->presavedVolume) {
volumeController.saveVolume(state->presavedVolume);
}
save(sound);
box->closeBox();
});
@@ -345,5 +389,5 @@ void ThreadRingtonesBox(
const auto now = thread->owner().notifySettings().sound(thread);
RingtonesBox(box, &thread->session(), now, [=](Data::NotifySound sound) {
thread->owner().notifySettings().update(thread, {}, {}, sound);
});
}, Data::ThreadRingtonesVolumeController(thread));
}

View File

@@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data {
struct NotifySound;
class Thread;
enum class DefaultNotify : uint8_t;
struct VolumeController;
} // namespace Data
namespace Main {
@@ -28,7 +30,8 @@ void RingtonesBox(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session,
Data::NotifySound selected,
Fn<void(Data::NotifySound)> save);
Fn<void(Data::NotifySound)> save,
Data::VolumeController volumeController);
void ThreadRingtonesBox(
not_null<Ui::GenericBox*> box,

View File

@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/passcode_box.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/rect.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "ui/text/text_utilities.h"
@@ -148,6 +149,8 @@ void SelfDestructionBox::showContent() {
: tr::lng_self_destruct_sessions_description(tr::now)),
st::boxLabel);
_description->moveToLeft(st::boxPadding.left(), y);
_description->resizeToWidth(st::boxWidth
- rect::m::sum::h(st::boxPadding));
y += _description->height() + st::boxMediumSkip;
for (const auto value : _ttlValues) {
@@ -200,6 +203,8 @@ void SelfDestructionBox::prepare() {
? tr::lng_self_destruct_description(tr::now)
: tr::lng_self_destruct_sessions_description(tr::now)),
st::boxLabel);
fake->resizeToWidth(st::boxWidth
- rect::m::sum::h(st::boxPadding));
const auto boxHeight = st::boxOptionListPadding.top()
+ fake->height() + st::boxMediumSkip
+ (_ttlValues.size()

View File

@@ -348,9 +348,9 @@ void SendCreditsBox(
}, ministarsContainer->lifetime());
}
const auto thumb = box->addRow(object_ptr<Ui::CenterWrap<>>(
content,
SendCreditsThumbnail(content, session, form.get(), photoSize)));
const auto thumb = box->addRow(
SendCreditsThumbnail(content, session, form.get(), photoSize),
style::al_top);
thumb->setAttribute(Qt::WA_TransparentForMouseEvents);
if (form->invoice.subscriptionPeriod) {
const auto badge = SendCreditsBadge(content, form->invoice.amount);
@@ -364,31 +364,30 @@ void SendCreditsBox(
}
Ui::AddSkip(content);
box->addRow(object_ptr<Ui::CenterWrap<>>(
box,
box->addRow(
object_ptr<Ui::FlatLabel>(
box,
form->invoice.subscriptionPeriod
? rpl::single(form->title)
: tr::lng_credits_box_out_title(),
st::settingsPremiumUserTitle)));
st::settingsPremiumUserTitle),
style::al_top);
if (form->invoice.subscriptionPeriod && form->botId && form->photo) {
Ui::AddSkip(content);
Ui::AddSkip(content);
const auto bot = session->data().user(form->botId);
box->addRow(
object_ptr<Ui::CenterWrap<>>(
box,
Ui::CreatePeerBubble(box, bot)));
Ui::CreatePeerBubble(box, bot),
style::al_top);
Ui::AddSkip(content);
}
Ui::AddSkip(content);
box->addRow(object_ptr<Ui::CenterWrap<>>(
box,
box->addRow(
object_ptr<Ui::FlatLabel>(
box,
SendCreditsConfirmText(session, form.get()),
st::creditsBoxAbout)));
st::creditsBoxAbout),
style::al_top);
Ui::AddSkip(content);
Ui::AddSkip(content);
@@ -455,7 +454,7 @@ void SendCreditsBox(
lt_count,
rpl::single(form->invoice.amount) | tr::to_count(),
lt_emoji,
rpl::single(CreditsEmojiSmall(session)),
rpl::single(CreditsEmojiSmall()),
Ui::Text::RichLangValue),
state->confirmButtonBusy.value()
) | rpl::map([](TextWithEntities &&text, bool busy) {
@@ -465,14 +464,6 @@ void SendCreditsBox(
st::creditsBoxButtonLabel,
&box->getDelegate()->style().button.textFg);
const auto buttonWidth = st::boxWidth
- rect::m::sum::h(stBox.buttonPadding);
button->widthValue() | rpl::filter([=] {
return (button->widthNoMargins() != buttonWidth);
}) | rpl::start_with_next([=] {
button->resizeToWidth(buttonWidth);
}, button->lifetime());
{
const auto close = Ui::CreateChild<Ui::IconButton>(
content,
@@ -502,16 +493,13 @@ void SendCreditsBox(
}
}
TextWithEntities CreditsEmoji(not_null<Main::Session*> session) {
return Ui::Text::SingleCustomEmoji(
session->data().customEmojiManager().registerInternalEmoji(
st::settingsPremiumIconStar,
QMargins{ 0, -st::moderateBoxExpandInnerSkip, 0, 0 },
true),
TextWithEntities CreditsEmoji() {
return Ui::Text::IconEmoji(
&st::starIconEmojiLarge,
QString(QChar(0x2B50)));
}
TextWithEntities CreditsEmojiSmall(not_null<Main::Session*> session) {
TextWithEntities CreditsEmojiSmall() {
return Ui::Text::IconEmoji(
&st::starIconEmoji,
QString(QChar(0x2B50)));

View File

@@ -32,11 +32,9 @@ void SendCreditsBox(
std::shared_ptr<Payments::CreditsFormData> data,
Fn<void()> sent);
[[nodiscard]] TextWithEntities CreditsEmoji(
not_null<Main::Session*> session);
[[nodiscard]] TextWithEntities CreditsEmoji();
[[nodiscard]] TextWithEntities CreditsEmojiSmall(
not_null<Main::Session*> session);
[[nodiscard]] TextWithEntities CreditsEmojiSmall();
not_null<FlatLabel*> SetButtonMarkedLabel(
not_null<RpWidget*> button,

View File

@@ -843,9 +843,8 @@ void SendFilesBox::refreshPriceTag() {
QPainter(raw).drawImage(0, 0, _priceTagBg);
}, raw->lifetime());
const auto session = &_show->session();
auto price = _price.value() | rpl::map([=](uint64 amount) {
auto result = Ui::Text::Colorized(Ui::CreditsEmoji(session));
auto result = Ui::Text::Colorized(Ui::CreditsEmoji());
result.append(Lang::FormatCountDecimal(amount));
return result;
});
@@ -857,10 +856,10 @@ void SendFilesBox::refreshPriceTag() {
raw,
QString(),
st::paidTagLabel);
std::move(text) | rpl::start_with_next([=](TextWithEntities &&text) {
label->setMarkedText(text, Core::TextContext({
.session = session,
}));
std::move(
text
) | rpl::start_with_next([=](const TextWithEntities &text) {
label->setMarkedText(text);
}, label->lifetime());
label->show();
label->sizeValue() | rpl::start_with_next([=](QSize size) {

View File

@@ -1935,6 +1935,29 @@ void FastShareMessage(
}), Ui::LayerOption::CloseOther);
}
void FastShareMessageToSelf(
std::shared_ptr<Main::SessionShow> show,
not_null<HistoryItem*> item) {
const auto self = show->session().user();
const auto donePhraseArgs = ChatHelpers::ForwardedMessagePhraseArgs{
.toCount = 1,
.singleMessage = true,
.to1 = self,
.to2 = nullptr,
};
auto sendAction = Api::SendAction(self->owner().history(self));
sendAction.clearDraft = false;
show->session().api().forwardMessages(
Data::ResolvedForwardDraft{ .items = {item} },
std::move(sendAction),
[=] {
auto phrase = rpl::variable<TextWithEntities>(
ChatHelpers::ForwardedMessagePhrase(
donePhraseArgs)).current();
show->showToast(std::move(phrase));
});
}
void FastShareMessage(
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item,

View File

@@ -71,6 +71,9 @@ struct ShareBoxStyleOverrides {
};
[[nodiscard]] ShareBoxStyleOverrides DarkShareBoxStyle();
void FastShareMessageToSelf(
std::shared_ptr<Main::SessionShow> show,
not_null<HistoryItem*> item);
void FastShareMessage(
std::shared_ptr<Main::SessionShow> show,
not_null<HistoryItem*> item,

View File

@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_credits.h"
#include "api/api_global_privacy.h"
#include "api/api_premium.h"
#include "api/api_text_entities.h"
#include "base/event_filter.h"
#include "base/qt_signal_producer.h"
#include "base/random.h"
@@ -54,6 +55,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_item_helpers.h"
#include "info/channel_statistics/earn/earn_icons.h"
#include "info/peer_gifts/info_peer_gifts_common.h"
#include "info/profile/info_profile_icon.h"
#include "lang/lang_keys.h"
@@ -69,6 +71,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_credits_graphics.h"
#include "settings/settings_premium.h"
#include "ui/boxes/boost_box.h"
#include "ui/boxes/confirm_box.h"
#include "ui/chat/chat_style.h"
#include "ui/chat/chat_theme.h"
#include "ui/controls/emoji_button.h"
@@ -82,6 +85,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/new_badges.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/text/custom_emoji_helper.h"
#include "ui/text/format_values.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
@@ -235,37 +239,6 @@ struct SessionResalePrices {
crl::time lastReceived = 0;
};
[[nodiscard]] CreditsAmount StarsFromTon(
not_null<Main::Session*> session,
CreditsAmount ton) {
const auto appConfig = &session->appConfig();
const auto starsRate = appConfig->starsWithdrawRate() / 100.;
const auto tonRate = appConfig->currencyWithdrawRate();
if (!starsRate) {
return {};
}
const auto count = (ton.value() * tonRate) / starsRate;
return CreditsAmount(int(base::SafeRound(count)));
}
[[nodiscard]] CreditsAmount TonFromStars(
not_null<Main::Session*> session,
CreditsAmount stars) {
const auto appConfig = &session->appConfig();
const auto starsRate = appConfig->starsWithdrawRate() / 100.;
const auto tonRate = appConfig->currencyWithdrawRate();
if (!tonRate) {
return {};
}
const auto count = (stars.value() * starsRate) / tonRate;
const auto whole = int(std::floor(count));
const auto cents = int(base::SafeRound((count - whole) * 100));
return CreditsAmount(
whole,
cents * (Ui::kNanosInOne / 100),
CreditsType::Ton);
}
[[nodiscard]] not_null<SessionResalePrices*> ResalePrices(
not_null<Main::Session*> session) {
static auto result = base::flat_map<
@@ -1158,7 +1131,9 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
const auto user = session->user();
const auto requestId = session->api().request(
MTPpayments_GetSavedStarGifts(
MTP_flags(Flag::f_exclude_limited | Flag::f_exclude_unlimited),
MTP_flags(Flag::f_exclude_upgradable
| Flag::f_exclude_unupgradable
| Flag::f_exclude_unlimited),
user->input,
MTP_int(0), // collection_id
MTP_string(offset),
@@ -1194,8 +1169,9 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
}
[[nodiscard]] Text::String TabTextForPrice(
not_null<Main::Session*> session,
int price) {
int price,
TextWithEntities creditsIcon,
Ui::Text::MarkedContext context) {
const auto simple = [](const QString &text) {
return Text::String(st::semiboldTextStyle, text);
};
@@ -1210,13 +1186,12 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
} else if (price == kPriceTabResale) {
return simple(tr::lng_gift_stars_tabs_resale(tr::now));
}
auto &manager = session->data().customEmojiManager();
auto result = Text::String();
result.setMarkedText(
st::semiboldTextStyle,
manager.creditsEmoji().append(QString::number(price)),
creditsIcon.append(QString::number(price)),
kMarkupTextOptions,
Core::TextContext({ .session = session }));
context);
return result;
}
@@ -1254,7 +1229,7 @@ struct ResaleTabs {
object_ptr<RpWidget> widget;
};
[[nodiscard]] ResaleTabs MakeResaleTabs(
not_null<Window::SessionController*> window,
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> peer,
const ResaleGiftsDescriptor &info,
rpl::producer<ResaleFilter> filter) {
@@ -1336,7 +1311,7 @@ struct ResaleTabs {
action->setClickedCallback(std::move(callback));
menu->addAction(std::move(action));
};
auto context = Core::TextContext({ .session = &window->session() });
auto context = Core::TextContext({ .session = &show->session() });
context.customEmojiFactory = [original = context.customEmojiFactory](
QStringView data,
const Ui::Text::MarkedContext &context) {
@@ -1636,7 +1611,6 @@ struct GiftPriceTabs {
object_ptr<RpWidget> widget;
};
[[nodiscard]] GiftPriceTabs MakeGiftsPriceTabs(
not_null<Window::SessionController*> window,
not_null<PeerData*> peer,
rpl::producer<std::vector<GiftTypeStars>> gifts,
bool hasMyUnique) {
@@ -1745,7 +1719,6 @@ struct GiftPriceTabs {
state->priceTab = state->buttons[index].price;
};
const auto session = &peer->session();
state->prices.value(
) | rpl::start_with_next([=](const std::vector<int> &prices) {
auto x = st::giftBoxTabsMargin.left();
@@ -1759,12 +1732,18 @@ struct GiftPriceTabs {
currentPrice = kPriceTabAll;
}
state->active = -1;
auto helper = Ui::Text::CustomEmojiHelper();
const auto creditsIcon = helper.paletteDependent(
Ui::Earn::IconCreditsEmoji());
for (auto i = 0, count = int(prices.size()); i != count; ++i) {
const auto price = prices[i];
auto &button = state->buttons[i];
if (button.text.isEmpty() || button.price != price) {
button.price = price;
button.text = TabTextForPrice(session, price);
button.text = TabTextForPrice(
price,
creditsIcon,
helper.context());
}
button.active = (price == currentPrice);
if (button.active) {
@@ -2031,6 +2010,7 @@ void SendGift(
.message = details.text,
.recipient = peer,
.limitedCount = gift.info.limitedCount,
.perUserLimit = gift.info.perUserTotal,
.anonymous = details.anonymous,
.upgraded = details.upgraded,
}, done, processNonPanelPaymentFormFactory);
@@ -2078,6 +2058,23 @@ void ShowGiftUpgradedToast(
}
}
void ShowUpgradeGiftedToast(
base::weak_ptr<Window::SessionController> weak,
not_null<PeerData*> peer) {
if (const auto strong = weak.get()) {
strong->showToast({
.title = tr::lng_gift_upgrade_gifted_title(tr::now),
.text = { (peer->isBroadcast()
? tr::lng_gift_upgrade_gifted_about_channel
: tr::lng_gift_upgrade_gifted_about)(
tr::now,
lt_name,
peer->shortName()) },
.duration = kUpgradeDoneToastDuration,
});
}
}
void SendStarsFormRequest(
std::shared_ptr<Main::SessionShow> show,
Settings::SmallBalanceResult result,
@@ -2122,9 +2119,17 @@ void UpgradeGift(
auto formDone = [=](
Payments::CheckoutResult result,
const MTPUpdates *updates) {
if (result == Payments::CheckoutResult::Paid && updates) {
if (result == Payments::CheckoutResult::Paid) {
if (const auto strong = weak.get()) {
ShowGiftUpgradedToast(strong, session, *updates);
const auto owner = savedId.isUser()
? strong->session().user().get()
: savedId.chat();
if (owner) {
owner->owner().nextForUpgradeGiftInvalidate(owner);
}
}
if (updates) {
ShowGiftUpgradedToast(weak, session, *updates);
}
}
done(result);
@@ -2154,6 +2159,29 @@ void UpgradeGift(
std::move(formDone));
}
void GiftUpgrade(
not_null<Window::SessionController*> window,
not_null<PeerData*> peer,
QString giftPrepayUpgradeHash,
int stars,
Fn<void(Payments::CheckoutResult)> done) {
const auto weak = base::make_weak(window);
auto formDone = [=](
Payments::CheckoutResult result,
const MTPUpdates *updates) {
if (result == Payments::CheckoutResult::Paid) {
ShowUpgradeGiftedToast(weak, peer);
}
done(result);
};
RequestStarsFormAndSubmit(
window->uiShow(),
MTP_inputInvoiceStarGiftPrepaidUpgrade(
peer->input,
MTP_string(giftPrepayUpgradeHash)),
std::move(formDone));
}
void SoldOutBox(
not_null<GenericBox*> box,
not_null<Window::SessionController*> window,
@@ -2177,7 +2205,6 @@ void SoldOutBox(
void AddUpgradeButton(
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session,
int cost,
not_null<PeerData*> peer,
Fn<void(bool)> toggled,
@@ -2190,7 +2217,8 @@ void AddUpgradeButton(
button->toggleOn(rpl::single(false))->toggledValue(
) | rpl::start_with_next(toggled, button->lifetime());
auto star = session->data().customEmojiManager().creditsEmoji();
auto helper = Ui::Text::CustomEmojiHelper();
auto star = helper.paletteDependent(Ui::Earn::IconCreditsEmoji());
const auto label = Ui::CreateChild<Ui::FlatLabel>(
button,
tr::lng_gift_send_unique(
@@ -2201,7 +2229,7 @@ void AddUpgradeButton(
Text::WithEntities),
st::boxLabel,
st::defaultPopupMenu,
Core::TextContext({ .session = session }));
helper.context());
label->show();
label->setAttribute(Qt::WA_TransparentForMouseEvents);
button->widthValue() | rpl::start_with_next([=](int outer) {
@@ -2308,6 +2336,27 @@ void AddSoldLeftSlider(
}, slider->lifetime());
}
void CheckMaybeGiftLocked(
not_null<Window::SessionController*> window,
uint64 giftId,
Fn<void()> send) {
const auto session = &window->session();
session->api().request(MTPpayments_CheckCanSendGift(
MTP_long(giftId)
)).done(crl::guard(window, [=](
const MTPpayments_CheckCanSendGiftResult &result) {
result.match([&](const MTPDpayments_checkCanSendGiftResultOk &) {
send();
}, [&](const MTPDpayments_checkCanSendGiftResultFail &data) {
window->show(Ui::MakeInformBox({
.text = Api::ParseTextWithEntities(session, data.vreason()),
.title = tr::lng_gift_locked_title(),
}));
});
})).fail(crl::guard(window, [=] {
})).send();
}
void SendGiftBox(
not_null<GenericBox*> box,
not_null<Window::SessionController*> window,
@@ -2355,7 +2404,7 @@ void SendGiftBox(
});
auto cost = state->details.value(
) | rpl::map([session](const GiftDetails &details) {
) | rpl::map([](const GiftDetails &details) {
return v::match(details.descriptor, [&](const GiftTypePremium &data) {
const auto stars = (details.byStars && data.stars)
? data.stars
@@ -2363,7 +2412,7 @@ void SendGiftBox(
? data.cost
: 0;
if (stars) {
return CreditsEmojiSmall(session).append(
return CreditsEmojiSmall().append(
Lang::FormatCountDecimal(std::abs(stars)));
}
return TextWithEntities{
@@ -2372,7 +2421,7 @@ void SendGiftBox(
}, [&](const GiftTypeStars &data) {
const auto amount = std::abs(data.info.stars)
+ (details.upgraded ? data.info.starsToUpgrade : 0);
return CreditsEmojiSmall(session).append(
return CreditsEmojiSmall().append(
Lang::FormatCountDecimal(amount));
});
});
@@ -2448,8 +2497,7 @@ void SendGiftBox(
const auto showing = std::make_shared<bool>();
AddDivider(container);
AddSkip(container);
AddUpgradeButton(container, session, costToUpgrade, peer, [=](
bool on) {
AddUpgradeButton(container, costToUpgrade, peer, [=](bool on) {
auto now = state->details.current();
now.upgraded = on;
state->details = std::move(now);
@@ -2682,13 +2730,6 @@ void SendGiftBox(
};
}
[[nodiscard]] rpl::lifetime ShowStarGiftResale(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer,
uint64 giftId,
QString title,
Fn<void()> finishRequesting);
[[nodiscard]] object_ptr<RpWidget> MakeGiftsList(
not_null<Window::SessionController*> window,
not_null<PeerData*> peer,
@@ -2779,21 +2820,41 @@ void SendGiftBox(
raw,
&state->delegate);
}
const auto raw = button.get();
if (validated[index]) {
return;
}
button->show();
raw->show();
validated[index] = true;
const auto &descriptor = state->list[state->order[index]];
button->setDescriptor(descriptor, GiftButton::Mode::Full);
button->setClickedCallback([=] {
raw->setDescriptor(descriptor, GiftButton::Mode::Full);
raw->setClickedCallback([=] {
const auto star = std::get_if<GiftTypeStars>(&descriptor);
const auto send = crl::guard(raw, [=] {
window->show(Box(
SendGiftBox,
window,
peer,
state->api,
descriptor));
});
const auto unique = star ? star->info.unique : nullptr;
if (star
&& star->info.requirePremium
&& !peer->session().premium()) {
const auto premiumNeeded = star && star->info.requirePremium;
if (premiumNeeded && !peer->session().premium()) {
Settings::ShowPremiumGiftPremium(window, star->info);
return;
} else if (star
&& star->info.lockedUntilDate
&& star->info.lockedUntilDate > base::unixtime::now()) {
const auto ready = crl::guard(raw, [=] {
if (premiumNeeded && !peer->session().premium()) {
Settings::ShowPremiumGiftPremium(
window,
v::get<GiftTypeStars>(descriptor).info);
} else {
send();
}
});
CheckMaybeGiftLocked(window, star->info.id, ready);
} else if (unique && star->mine && !peer->isSelf()) {
if (ShowTransferGiftLater(window->uiShow(), unique)) {
return;
@@ -2837,7 +2898,7 @@ void SendGiftBox(
Api::InputSavedStarGiftId(savedId, unique),
peer->input),
formReady);
} else if (star && star->info.unique && star->resale) {
} else if (unique && star->resale) {
window->show(Box(
Settings::GlobalStarGiftBox,
window->uiShow(),
@@ -2861,16 +2922,21 @@ void SendGiftBox(
[=] { state->resaleRequestingId = 0; });
} else if (star && IsSoldOut(star->info)) {
window->show(Box(SoldOutBox, window, *star));
} else if (star
&& star->info.perUserTotal
&& !star->info.perUserRemains) {
window->showToast({
.text = tr::lng_gift_sent_finished(
tr::now,
lt_count,
star->info.perUserTotal,
Ui::Text::RichLangValue),
});
} else {
window->show(Box(
SendGiftBox,
window,
peer,
state->api,
descriptor));
send();
}
});
button->setGeometry(QRect(QPoint(x, y), single), extend);
raw->setGeometry(QRect(QPoint(x, y), single), extend);
};
y += rowFrom * singleh;
for (auto row = rowFrom; row != rowTill; ++row) {
@@ -2976,13 +3042,15 @@ void AddBlock(
content,
std::move(args.subtitle),
st::giftBoxSubtitle),
st::giftBoxSubtitleMargin);
st::giftBoxSubtitleMargin,
style::al_top);
const auto about = content->add(
object_ptr<FlatLabel>(
content,
std::move(args.about),
st::giftBoxAbout),
st::giftBoxAboutMargin);
st::giftBoxAboutMargin,
style::al_top);
about->setClickHandlerFilter(std::move(args.aboutFilter));
content->add(std::move(args.content));
}
@@ -3027,7 +3095,6 @@ void AddBlock(
state->gifts = GiftsStars(&window->session(), peer);
auto tabs = MakeGiftsPriceTabs(
window,
peer,
state->gifts.value(),
!state->my.list.empty() && !peer->isSelf());
@@ -3145,10 +3212,9 @@ void GiftBox(
&& uniqueDisallowed;
content->add(
object_ptr<CenterWrap<UserpicButton>>(
content,
object_ptr<UserpicButton>(content, peer, stUser))
)->entity()->setClickedCallback([=] { window->showPeerInfo(peer); });
object_ptr<UserpicButton>(content, peer, stUser),
style::al_top
)->setClickedCallback([=] { window->showPeerInfo(peer); });
AddSkip(content);
AddSkip(content);
@@ -3289,13 +3355,10 @@ void GiftResaleBox(
state->ton = !state->ton.current();
state->updated.fire({});
});
currency->setText(tr::lng_gift_resale_switch_to(
lt_currency,
rpl::conditional(
state->ton.value(),
rpl::single(Ui::Text::IconEmoji(&st::starIconEmoji)),
rpl::single(Ui::Text::IconEmoji(&st::tonIconEmoji))),
Ui::Text::WithEntities));
currency->setText(rpl::conditional(
state->ton.value(),
tr::lng_gift_resale_switch_to_stars(),
tr::lng_gift_resale_switch_to_ton()));
#endif
box->heightValue() | rpl::start_with_next([=](int height) {
@@ -3306,7 +3369,7 @@ void GiftResaleBox(
}, content->lifetime());
auto tabs = MakeResaleTabs(
window,
window->uiShow(),
peer,
state->data,
state->filter.value());
@@ -3392,30 +3455,6 @@ void GiftResaleBox(
}));
}
[[nodiscard]] rpl::lifetime ShowStarGiftResale(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer,
uint64 giftId,
QString title,
Fn<void()> finishRequesting) {
const auto weak = base::make_weak(controller);
const auto session = &controller->session();
return ResaleGiftsSlice(
session,
giftId
) | rpl::start_with_next([=](ResaleGiftsDescriptor &&info) {
if (const auto onstack = finishRequesting) {
onstack();
}
if (!info.giftId || !info.count) {
return;
}
info.title = title;
controller->show(
Box(GiftResaleBox, controller, peer, std::move(info)));
});
}
struct CustomList {
object_ptr<Ui::RpWidget> content = { nullptr };
Fn<bool(int, int, int)> overrideKey;
@@ -4377,13 +4416,15 @@ void ShowUniqueGiftWearBox(
lt_name,
rpl::single(UniqueGiftName(gift))),
st.title ? *st.title : st::uniqueGiftTitle),
st::settingsPremiumRowTitlePadding);
st::settingsPremiumRowTitlePadding,
style::al_top);
content->add(
object_ptr<Ui::FlatLabel>(
content,
tr::lng_gift_wear_about(),
st.subtitle ? *st.subtitle : st::uniqueGiftSubtitle),
st::settingsPremiumRowAboutPadding);
st::settingsPremiumRowAboutPadding,
style::al_top);
infoRow(
tr::lng_gift_wear_badge_title(),
(channel
@@ -4445,11 +4486,7 @@ void ShowUniqueGiftWearBox(
u"wear_collectibles"_q);
}
});
const auto lock = Ui::Text::SingleCustomEmoji(
session->data().customEmojiManager().registerInternalEmoji(
st::historySendDisabledIcon,
st::giftBoxLockMargins,
true));
const auto lock = Ui::Text::IconEmoji(&st::giftBoxLock);
auto label = rpl::combine(
tr::lng_gift_wear_start(),
Data::AmPremiumValue(&show->session())
@@ -4891,15 +4928,33 @@ void UpgradeBox(
infoRow(
tr::lng_gift_upgrade_unique_title(),
tr::lng_gift_upgrade_unique_about(),
(args.savedId
? tr::lng_gift_upgrade_unique_about()
: (args.peer->isBroadcast()
? tr::lng_gift_upgrade_unique_about_channel
: tr::lng_gift_upgrade_unique_about_user)(
lt_name,
rpl::single(args.peer->shortName()))),
&st::menuIconUnique);
infoRow(
tr::lng_gift_upgrade_transferable_title(),
tr::lng_gift_upgrade_transferable_about(),
(args.savedId
? tr::lng_gift_upgrade_transferable_about()
: (args.peer->isBroadcast()
? tr::lng_gift_upgrade_transferable_about_channel
: tr::lng_gift_upgrade_transferable_about_user)(
lt_name,
rpl::single(args.peer->shortName()))),
&st::menuIconReplace);
infoRow(
tr::lng_gift_upgrade_tradable_title(),
tr::lng_gift_upgrade_tradable_about(),
(args.savedId
? tr::lng_gift_upgrade_tradable_about()
: (args.peer->isBroadcast()
? tr::lng_gift_upgrade_tradable_about_channel
: tr::lng_gift_upgrade_tradable_about_user)(
lt_name,
rpl::single(args.peer->shortName()))),
&st::menuIconTradable);
struct State {
@@ -4907,36 +4962,39 @@ void UpgradeBox(
bool preserveDetails = false;
};
const auto state = std::make_shared<State>();
const auto preview = !args.savedId;
const auto gifting = !args.savedId
&& !args.giftPrepayUpgradeHash.isEmpty();
const auto preview = !args.savedId && !gifting;
if (!preview) {
if (!preview && !gifting) {
const auto skip = st::defaultVerticalListSkip;
container->add(
object_ptr<PlainShadow>(container),
st::boxRowPadding + QMargins(0, skip, 0, skip));
const auto checkbox = container->add(
object_ptr<CenterWrap<Checkbox>>(
object_ptr<Checkbox>(
container,
object_ptr<Checkbox>(
container,
(args.canAddComment
? tr::lng_gift_upgrade_add_comment(tr::now)
: args.canAddSender
? tr::lng_gift_upgrade_add_sender(tr::now)
: args.canAddMyComment
? tr::lng_gift_upgrade_add_my_comment(tr::now)
: tr::lng_gift_upgrade_add_my(tr::now)),
args.addDetailsDefault)),
st::defaultCheckbox.margin)->entity();
(args.canAddComment
? tr::lng_gift_upgrade_add_comment(tr::now)
: args.canAddSender
? tr::lng_gift_upgrade_add_sender(tr::now)
: args.canAddMyComment
? tr::lng_gift_upgrade_add_my_comment(tr::now)
: tr::lng_gift_upgrade_add_my(tr::now)),
args.addDetailsDefault),
st::defaultCheckbox.margin,
style::al_top);
checkbox->checkedChanges() | rpl::start_with_next([=](bool checked) {
state->preserveDetails = checked;
}, checkbox->lifetime());
}
box->setStyle(preview ? st::giftBox : st::upgradeGiftBox);
if (gifting) {
box->setWidth(st::boxWideWidth);
}
const auto cost = args.cost;
const auto session = &controller->session();
auto buttonText = preview ? tr::lng_box_ok() : rpl::single(QString());
const auto button = box->addButton(std::move(buttonText), [=] {
if (preview) {
@@ -4958,21 +5016,30 @@ void UpgradeBox(
}
}
};
UpgradeGift(controller, args.savedId, keepDetails, cost, done);
if (gifting) {
GiftUpgrade(
controller,
args.peer,
args.giftPrepayUpgradeHash,
cost,
done);
} else {
UpgradeGift(controller, args.savedId, keepDetails, cost, done);
}
});
if (!preview) {
auto star = session->data().customEmojiManager().creditsEmoji();
SetButtonMarkedLabel(
button,
(cost
? tr::lng_gift_upgrade_button(
lt_price,
rpl::single(star.append(
' ' + Lang::FormatCreditsAmountDecimal(
CreditsAmount{ cost }))),
rpl::single(Ui::Text::IconEmoji(
&st::starIconEmoji
).append(' ').append(Lang::FormatCreditsAmountDecimal(
CreditsAmount{ cost }))),
Ui::Text::WithEntities)
: tr::lng_gift_upgrade_confirm(Ui::Text::WithEntities)),
&controller->session(),
{},
st::creditsBoxButtonLabel,
&st::giftBox.button.textFg);
}
@@ -5336,4 +5403,61 @@ void ShowResaleGiftBoughtToast(
.duration = kUpgradeDoneToastDuration,
});
}
rpl::lifetime ShowStarGiftResale(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer,
uint64 giftId,
QString title,
Fn<void()> finishRequesting) {
const auto weak = base::make_weak(controller);
const auto session = &controller->session();
return ResaleGiftsSlice(
session,
giftId
) | rpl::start_with_next([=](ResaleGiftsDescriptor &&info) {
if (const auto onstack = finishRequesting) {
onstack();
}
if (!info.giftId || !info.count) {
return;
}
info.title = title;
if (const auto strong = weak.get()) {
strong->show(Box(GiftResaleBox, strong, peer, std::move(info)));
}
});
}
CreditsAmount StarsFromTon(
not_null<Main::Session*> session,
CreditsAmount ton) {
const auto appConfig = &session->appConfig();
const auto starsRate = appConfig->starsWithdrawRate() / 100.;
const auto tonRate = appConfig->currencyWithdrawRate();
if (!starsRate) {
return {};
}
const auto count = (ton.value() * tonRate) / starsRate;
return CreditsAmount(int(base::SafeRound(count)));
}
CreditsAmount TonFromStars(
not_null<Main::Session*> session,
CreditsAmount stars) {
const auto appConfig = &session->appConfig();
const auto starsRate = appConfig->starsWithdrawRate() / 100.;
const auto tonRate = appConfig->currencyWithdrawRate();
if (!tonRate) {
return {};
}
const auto count = (stars.value() * starsRate) / tonRate;
const auto whole = int(std::floor(count));
const auto cents = int(base::SafeRound((count - whole) * 100));
return CreditsAmount(
whole,
cents * (Ui::kNanosInOne / 100),
CreditsType::Ton);
}
} // namespace Ui

View File

@@ -110,6 +110,7 @@ struct StarGiftUpgradeArgs {
Fn<void(bool)> ready;
not_null<PeerData*> peer;
Data::SavedStarGiftId savedId;
QString giftPrepayUpgradeHash;
int cost = 0;
bool canAddSender = false;
bool canAddComment = false;
@@ -157,4 +158,18 @@ void ShowResaleGiftBoughtToast(
not_null<PeerData*> to,
const Data::UniqueGift &gift);
[[nodiscard]] rpl::lifetime ShowStarGiftResale(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer,
uint64 giftId,
QString title,
Fn<void()> finishRequesting);
[[nodiscard]] CreditsAmount StarsFromTon(
not_null<Main::Session*> session,
CreditsAmount ton);
[[nodiscard]] CreditsAmount TonFromStars(
not_null<Main::Session*> session,
CreditsAmount stars);
} // namespace Ui

View File

@@ -641,20 +641,11 @@ void ChangeSetNameBox(
const auto it = sets.find(input.id);
return (it == sets.end()) ? QString() : it->second->title;
}();
const auto wrap = box->addRow(object_ptr<Ui::FixedHeightWidget>(
const auto field = box->addRow(object_ptr<Ui::InputField>(
box,
st::editStickerSetNameField.heightMin));
auto owned = object_ptr<Ui::InputField>(
wrap,
st::editStickerSetNameField,
tr::lng_stickers_context_edit_name(),
wasName);
const auto field = owned.data();
wrap->widthValue() | rpl::start_with_next([=](int width) {
field->move(0, 0);
field->resize(width, field->height());
wrap->resize(width, field->height());
}, wrap->lifetime());
wasName));
field->selectAll();
constexpr auto kMaxSetNameLength = 50;
field->setMaxLength(kMaxSetNameLength);

View File

@@ -12,8 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_cloud_password.h"
#include "base/unixtime.h"
#include "boxes/passcode_box.h"
#include "data/data_cloud_themes.h"
#include "data/data_session.h"
#include "data/data_star_gift.h"
#include "data/data_thread.h"
#include "data/data_user.h"
#include "boxes/filters/edit_filter_chats_list.h" // CreatePe...tionSubtitle.
#include "boxes/peer_list_box.h"
@@ -673,6 +675,107 @@ void ShowTransferGiftBox(
Ui::LayerOption::KeepOther);
}
void SetThemeFromUniqueGift(
not_null<Window::SessionController*> window,
std::shared_ptr<Data::UniqueGift> unique) {
class Controller final : public ChooseRecipientBoxController {
public:
Controller(
not_null<Window::SessionController*> window,
std::shared_ptr<Data::UniqueGift> unique)
: ChooseRecipientBoxController({
.session = &window->session(),
.callback = [=](not_null<Data::Thread*> thread) {
const auto weak = base::make_weak(window);
const auto peer = thread->peer();
SendPeerThemeChangeRequest(window, peer, QString(), unique);
if (weak) window->showPeerHistory(peer);
if (weak) window->hideLayer(anim::type::normal);
},
.filter = [=](not_null<Data::Thread*> thread) {
return thread->peer()->isUser();
},
.moneyRestrictionError = WriteMoneyRestrictionError,
}) {
}
private:
void prepareViewHook() override {
ChooseRecipientBoxController::prepareViewHook();
delegate()->peerListSetTitle(tr::lng_gift_transfer_choose());
}
};
window->show(
Box<PeerListBox>(
std::make_unique<Controller>(window, std::move(unique)),
[](not_null<PeerListBox*> box) {
box->addButton(tr::lng_cancel(), [=] {
box->closeBox();
});
}));
}
void SendPeerThemeChangeRequest(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer,
const QString &token,
const std::shared_ptr<Data::UniqueGift> &unique,
bool locallySet) {
const auto api = &peer->session().api();
api->request(MTPmessages_SetChatWallPaper(
MTP_flags(0),
peer->input,
MTPInputWallPaper(),
MTPWallPaperSettings(),
MTPint()
)).afterDelay(10).done([=](const MTPUpdates &result) {
api->applyUpdates(result);
}).send();
api->request(MTPmessages_SetChatTheme(
peer->input,
(unique
? MTP_inputChatThemeUniqueGift(MTP_string(unique->slug))
: MTP_inputChatTheme(MTP_string(token)))
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result);
if (!locallySet) {
peer->updateFullForced();
}
}).send();
}
void SetPeerTheme(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer,
const QString &token,
const std::shared_ptr<Ui::ChatTheme> &theme) {
const auto giftTheme = token.startsWith(u"gift:"_q)
? peer->owner().cloudThemes().themeForToken(token)
: std::optional<Data::CloudTheme>();
peer->setThemeToken(token);
const auto dropWallPaper = (peer->wallPaper() != nullptr);
if (dropWallPaper) {
peer->setWallPaper({});
}
if (theme) {
// Remember while changes propagate through event loop.
controller->pushLastUsedChatTheme(theme);
}
SendPeerThemeChangeRequest(
controller,
peer,
token,
giftTheme ? giftTheme->unique : nullptr,
true);
}
void ShowBuyResaleGiftBox(
std::shared_ptr<ChatHelpers::Show> show,
std::shared_ptr<Data::UniqueGift> gift,

View File

@@ -20,6 +20,10 @@ struct UniqueGift;
class SavedStarGiftId;
} // namespace Data
namespace Ui {
class ChatTheme;
} // namespace Ui
void ShowTransferToBox(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer,
@@ -45,3 +49,18 @@ bool ShowResaleGiftLater(
bool ShowTransferGiftLater(
std::shared_ptr<ChatHelpers::Show> show,
std::shared_ptr<Data::UniqueGift> gift);
void SetThemeFromUniqueGift(
not_null<Window::SessionController*> window,
std::shared_ptr<Data::UniqueGift> unique);
void SendPeerThemeChangeRequest(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer,
const QString &token,
const std::shared_ptr<Data::UniqueGift> &unique,
bool locallySet = false);
void SetPeerTheme(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer,
const QString &token,
const std::shared_ptr<Ui::ChatTheme> &theme);

View File

@@ -208,7 +208,7 @@ void TranslateBox(
box,
CreateLoadingTextWidget(
box,
st::aboutLabel,
st::aboutLabel.style,
std::min(original->entity()->height() / lineHeight, kMaxLines),
state->to.value() | rpl::map([=](LanguageId id) {
return id.locale().textDirection() == Qt::RightToLeft;
@@ -248,12 +248,9 @@ void TranslateBox(
showText(
Ui::Text::Italic(tr::lng_translate_box_error(tr::now)));
} else {
showText(TextWithEntities{
.text = qs(list.front().data().vtext()),
.entities = Api::EntitiesFromMTP(
&peer->session(),
list.front().data().ventities().v),
});
showText(Api::ParseTextWithEntities(
&peer->session(),
list.front()));
}
}).fail([=](const MTP::Error &error) {
showText(

View File

@@ -324,7 +324,7 @@ void UsernamesBox(
const auto editor = box->addRow(
object_ptr<UsernameEditor>(box, peer),
{});
style::margins());
editor->setEnabled(!isBot);
box->setFocusCallback([=] { editor->setInnerFocus(); });
@@ -366,7 +366,7 @@ void UsernamesBox(
!isBot
? [=] { box->scrollToY(0); editor->setInnerFocus(); }
: Fn<void()>(nullptr)),
{});
style::margins());
const auto finish = [=] {
list->save(

View File

@@ -825,7 +825,7 @@ void ShowCallsBox(not_null<::Window::SessionController*> window) {
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
box,
object_ptr<Ui::VerticalLayout>(box)),
{});
style::margins());
groupCalls->hide(anim::type::instant);
groupCalls->toggleOn(state->groupCallsController.shownValue());
@@ -833,8 +833,7 @@ void ShowCallsBox(not_null<::Window::SessionController*> window) {
groupCalls->entity(),
tr::lng_call_box_groupcalls_subtitle());
state->groupCallsDelegate.setContent(groupCalls->entity()->add(
object_ptr<PeerListContent>(box, &state->groupCallsController),
{}));
object_ptr<PeerListContent>(box, &state->groupCallsController)));
state->groupCallsController.setDelegate(&state->groupCallsDelegate);
Ui::AddSkip(groupCalls->entity());
Ui::AddDivider(groupCalls->entity());
@@ -853,7 +852,7 @@ void ShowCallsBox(not_null<::Window::SessionController*> window) {
const auto content = box->addRow(
object_ptr<PeerListContent>(box, &state->callsController),
{});
style::margins());
state->callsDelegate.setContent(content);
state->callsController.setDelegate(&state->callsDelegate);

View File

@@ -3982,7 +3982,7 @@ void GroupCall::inviteUsers(
}
};
if (const auto call = _conferenceCall.get()) {
if (_conferenceCall.get()) {
for (const auto &request : requests) {
inviteToConference(request, [=] {
return &state->result;

View File

@@ -116,13 +116,12 @@ void ConferenceCallJoinConfirm(
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
object_ptr<Ui::FlatLabel>(
box,
object_ptr<Ui::FlatLabel>(
box,
tr::lng_confcall_join_title(),
st::boxTitle)),
st::boxRowPadding + st::confcallLinkTitlePadding);
tr::lng_confcall_join_title(),
st::boxTitle),
st::boxRowPadding + st::confcallLinkTitlePadding,
style::al_top);
const auto wrapName = [&](not_null<PeerData*> peer) {
return rpl::single(Ui::Text::Bold(peer->shortName()));
};
@@ -136,7 +135,8 @@ void ConferenceCallJoinConfirm(
Ui::Text::RichLangValue)
: tr::lng_confcall_join_text(Ui::Text::RichLangValue)),
st::confcallLinkCenteredText),
st::boxRowPadding
st::boxRowPadding,
style::al_top
)->setTryMakeSimilarLines(true);
const auto &participants = call->participants();
@@ -209,7 +209,8 @@ void ConferenceCallJoinConfirm(
box,
std::move(text),
st::confcallLinkCenteredText),
st::boxRowPadding
st::boxRowPadding,
style::al_top
)->setTryMakeSimilarLines(true);
}
const auto joinAndClose = [=] {
@@ -321,13 +322,12 @@ void ShowConferenceCallLinkBox(
Info::BotStarRef::CreateLinkHeaderIcon(box, &call->session()),
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
object_ptr<Ui::FlatLabel>(
box,
object_ptr<Ui::FlatLabel>(
box,
tr::lng_confcall_link_title(),
st.box ? st.box->title : st::boxTitle)),
st::boxRowPadding + st::confcallLinkTitlePadding);
tr::lng_confcall_link_title(),
st.box ? st.box->title : st::boxTitle),
st::boxRowPadding + st::confcallLinkTitlePadding,
style::al_top);
box->addRow(
object_ptr<Ui::FlatLabel>(
box,
@@ -335,7 +335,8 @@ void ShowConferenceCallLinkBox(
(st.centerLabel
? *st.centerLabel
: st::confcallLinkCenteredText)),
st::boxRowPadding
st::boxRowPadding,
style::al_top
)->setTryMakeSimilarLines(true);
Ui::AddSkip(box->verticalLayout(), st::defaultVerticalListSkip * 2);

View File

@@ -1042,19 +1042,19 @@ not_null<Ui::RpWidget*> CreateReActivateHeader(not_null<QWidget*> parent) {
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
result->add(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
object_ptr<Ui::FlatLabel>(
result,
object_ptr<Ui::FlatLabel>(
result,
tr::lng_confcall_inactive_title(),
st::boxTitle)),
st::boxRowPadding + st::confcallLinkTitlePadding);
tr::lng_confcall_inactive_title(),
st::boxTitle),
st::boxRowPadding + st::confcallLinkTitlePadding,
style::al_top);
result->add(
object_ptr<Ui::FlatLabel>(
result,
tr::lng_confcall_inactive_about(),
st::confcallLinkCenteredText),
st::boxRowPadding + st::confcallLinkTitlePadding
st::boxRowPadding + st::confcallLinkTitlePadding,
style::al_top
)->setTryMakeSimilarLines(true);
Ui::AddDivider(result);

View File

@@ -196,22 +196,13 @@ void RecordingInfo::prepareVideo() {
}
void RecordingInfo::setLabel(const QString &text) {
const auto label = _container->add(
_container->add(
object_ptr<Ui::FlatLabel>(
_container,
text,
st::groupCallRecordingSubLabel),
st::groupCallRecordingSubLabelMargins);
rpl::combine(
sizeValue(),
label->sizeValue()
) | rpl::start_with_next([=](QSize my, QSize labelSize) {
label->moveToLeft(
(my.width() - labelSize.width()) / 2,
label->y(),
my.width());
}, label->lifetime());
st::groupCallRecordingSubLabelMargins,
style::al_top);
}
RecordingType RecordingInfo::type() const {

View File

@@ -126,7 +126,7 @@ int Style::minButtonWidth(HistoryMessageMarkupButton::Type type) const {
BotKeyboard::BotKeyboard(
not_null<Window::SessionController*> controller,
QWidget *parent)
: TWidget(parent)
: RpWidget(parent)
, _controller(controller)
, _st(&st::botKbButton) {
setGeometry(0, 0, _st->margin, st::botKbScroll.deltat);

View File

@@ -21,7 +21,7 @@ class SessionController;
} // namespace Window
class BotKeyboard
: public TWidget
: public Ui::RpWidget
, public Ui::AbstractTooltipShower
, public ClickHandlerHost {
public:
@@ -44,7 +44,7 @@ public:
void step_selected(crl::time ms, bool timer);
void resizeToWidth(int newWidth, int maxOuterHeight) {
_maxOuterHeight = maxOuterHeight;
return TWidget::resizeToWidth(newWidth);
return RpWidget::resizeToWidth(newWidth);
}
[[nodiscard]] bool maximizeSize() const;

View File

@@ -1295,6 +1295,7 @@ historyRecordLockPosition: point(1px, 22px);
historyRecordCancelButtonWidth: 100px;
historyRecordCancelButtonFg: lightButtonFg;
historyRecordTooltipSkip: 8px;
historyRecordTooltip: ImportantTooltip(defaultImportantTooltip) {
padding: margins(4px, 4px, 4px, 4px);
radius: 11px;
@@ -1626,4 +1627,17 @@ frozenInfoBox: Box(defaultBox) {
shadowIgnoreTopSkip: true;
}
menuTranscribeItemPadding: margins(0px, 10px, 0px, 10px);
menuTranscribeDummyButton: IconButton(defaultIconButton) {
width: 40px;
height: 40px;
icon: icon {{ "chat/input_attach", historyComposeIconFgOver }};
iconOver: icon {{ "chat/input_attach", historyComposeIconFgOver }};
rippleAreaPosition: point(3px, 3px);
rippleAreaSize: 34px;
ripple: defaultRippleAnimationBgOver;
}
roundVideoFont: font(14px semibold);

View File

@@ -704,12 +704,12 @@ void SuggestionsWidget::enterEventHook(QEnterEvent *e) {
if (!inner().contains(mapToInner(QCursor::pos()))) {
clearMouseSelection();
}
return TWidget::enterEventHook(e);
return RpWidget::enterEventHook(e);
}
void SuggestionsWidget::leaveEventHook(QEvent *e) {
clearMouseSelection();
return TWidget::leaveEventHook(e);
return RpWidget::leaveEventHook(e);
}
SuggestionsController::SuggestionsController(

View File

@@ -1467,7 +1467,8 @@ void FrozenInfoBox(
content,
tr::lng_frozen_title(),
st.title ? *st.title : st::uniqueGiftTitle),
st::settingsPremiumRowTitlePadding);
st::settingsPremiumRowTitlePadding,
style::al_top);
Ui::AddSkip(content, st::defaultVerticalListSkip * 3);

View File

@@ -277,7 +277,7 @@ void TabbedPanel::leaveEventHook(QEvent *e) {
} else {
_hideTimer.callOnce(kHideTimeoutMs);
}
return TWidget::leaveEventHook(e);
return RpWidget::leaveEventHook(e);
}
void TabbedPanel::otherEnter() {

View File

@@ -648,6 +648,8 @@ bool Application::eventFilter(QObject *object, QEvent *e) {
if (base::Platform::GlobalShortcuts::IsToggleFullScreenKey(event)
&& toggleActiveWindowFullScreen()) {
return true;
} else if (Shortcuts::HandlePossibleChatSwitch(event)) {
return true;
}
} break;
case QEvent::MouseButtonPress:
@@ -656,8 +658,20 @@ bool Application::eventFilter(QObject *object, QEvent *e) {
updateNonIdle();
} break;
case QEvent::KeyRelease: {
const auto event = static_cast<QKeyEvent*>(e);
if (Shortcuts::HandlePossibleChatSwitch(event)) {
return true;
}
} break;
case QEvent::ShortcutOverride: {
// handle shortcuts ourselves
// Ctrl+Tab/Ctrl+Shift+Tab chat switch is a special shortcut case,
// because it not only does an action on the shortcut activation,
// but also keeps the UI visible until you release the Ctrl key.
Shortcuts::HandlePossibleChatSwitch(static_cast<QKeyEvent*>(e));
// Handle all the shortcut management manually.
return true;
} break;

View File

@@ -240,7 +240,8 @@ QByteArray Settings::serialize() const {
+ Serialize::stringSize(_customFontFamily)
+ sizeof(qint32) * 3
+ Serialize::bytearraySize(_tonsiteStorageToken)
+ sizeof(qint32) * 8;
+ sizeof(qint32) * 8
+ sizeof(ushort);
auto result = QByteArray();
result.reserve(size);
@@ -402,7 +403,8 @@ QByteArray Settings::serialize() const {
<< SerializeVideoQuality(_videoQuality)
<< qint32(_ivZoom.current())
<< qint32(_systemDarkModeEnabled.current() ? 1 : 0)
<< qint32(_quickDialogAction);
<< qint32(_quickDialogAction)
<< _notificationsVolume;
}
Ensures(result.size() == size);
@@ -532,6 +534,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
quint32 videoQuality = SerializeVideoQuality(_videoQuality);
quint32 chatFiltersHorizontal = _chatFiltersHorizontal.current() ? 1 : 0;
quint32 quickDialogAction = quint32(_quickDialogAction);
ushort notificationsVolume = _notificationsVolume;
stream >> themesAccentColors;
if (!stream.atEnd()) {
@@ -863,6 +866,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!stream.atEnd()) {
stream >> quickDialogAction;
}
if (!stream.atEnd()) {
stream >> notificationsVolume;
}
if (stream.status() != QDataStream::Ok) {
LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()"));
@@ -1085,6 +1091,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
_videoQuality = DeserializeVideoQuality(videoQuality);
_chatFiltersHorizontal = (chatFiltersHorizontal == 1);
_quickDialogAction = Dialogs::Ui::QuickDialogAction(quickDialogAction);
_notificationsVolume = notificationsVolume;
}
QString Settings::getSoundPath(const QString &key) const {
@@ -1477,6 +1484,7 @@ void Settings::resetOnLastLogout() {
_videoQuality = {};
_chatFiltersHorizontal = false;
_quickDialogAction = Dialogs::Ui::QuickDialogAction::Disabled;
_notificationsVolume = 100;
_recentEmojiPreload.clear();
_recentEmoji.clear();

View File

@@ -950,6 +950,13 @@ public:
[[nodiscard]] Dialogs::Ui::QuickDialogAction quickDialogAction() const;
void setQuickDialogAction(Dialogs::Ui::QuickDialogAction);
[[nodiscard]] ushort notificationsVolume() const {
return _notificationsVolume;
}
void setNotificationsVolume(ushort value) {
_notificationsVolume = value;
}
void resetOnLastLogout();
private:
@@ -1093,6 +1100,8 @@ private:
Dialogs::Ui::QuickDialogAction _quickDialogAction
= Dialogs::Ui::QuickDialogAction::Disabled;
ushort _notificationsVolume = 100;
QByteArray _photoEditorBrush;
};

View File

@@ -646,7 +646,9 @@ bool ResolveUsernameOrPhone(
const auto threadId = topicId ? topicId : threadParam.toInt();
const auto gameParam = params.value(u"game"_q);
const auto videot = params.value(u"t"_q);
if (params.contains(u"direct"_q)) {
resolveType = ResolveType::ChannelDirect;
}
if (!gameParam.isEmpty() && validDomain(gameParam)) {
startToken = gameParam;
resolveType = ResolveType::ShareGame;
@@ -1089,7 +1091,17 @@ bool ShowCollectibleUsername(
}
const auto username = match->captured(1);
const auto peerId = PeerId(match->captured(2).toULongLong());
controller->resolveCollectible(peerId, username);
const auto weak = base::make_weak(controller);
controller->resolveCollectible(peerId, username, [=](const QString &e) {
if (e == u"COLLECTIBLE_NOT_FOUND"_q) {
if (const auto strong = weak.get()) {
TextUtilities::SetClipboardText({
strong->session().createInternalLinkFull(username)
});
strong->showToast(tr::lng_username_copied(tr::now));
}
}
});
return true;
}
@@ -1308,8 +1320,9 @@ bool ResolveTestChatTheme(
qthelp::UrlParamNameTransform::ToLower);
if (const auto history = controller->activeChatCurrent().history()) {
controller->clearCachedChatThemes();
const auto theme = history->owner().cloudThemes().updateThemeFromLink(
history->peer->themeEmoji(),
const auto owner = &history->owner();
const auto theme = owner->cloudThemes().updateThemeFromLink(
history->peer->themeToken(),
params);
if (theme) {
if (!params["export"].isEmpty()) {

View File

@@ -7,14 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "core/shortcuts.h"
#include "base/platform/base_platform_info.h"
#include "base/event_filter.h"
#include "base/parse_helper.h"
#include "core/application.h"
#include "mainwindow.h"
#include "mainwidget.h"
#include "window/window_controller.h"
#include "core/application.h"
#include "media/player/media_player_instance.h"
#include "base/platform/base_platform_info.h"
#include "platform/platform_specific.h"
#include "base/parse_helper.h"
#include <QAction>
#include <QShortcut>
@@ -30,6 +31,19 @@ constexpr auto kCountLimit = 256; // How many shortcuts can be in json file.
rpl::event_stream<not_null<Request*>> RequestsStream;
bool Paused/* = false*/;
const auto kChatSwitchSpecialKeys = std::array{
Qt::Key_Left,
Qt::Key_Right,
Qt::Key_Up,
Qt::Key_Down,
Qt::Key_Q,
};
Qt::Key ChatSwitchModifier/* = Qt::Key()*/;
bool ChatSwitchStarted/* = false*/;
QObject *ChatSwitchFilter/* = nullptr*/;
rpl::event_stream<ChatSwitchRequest> ChatSwitchStream;
std::array<Qt::Key, kChatSwitchSpecialKeys.size()> ChatSwitchKeyPressHandled;
const auto AutoRepeatCommands = base::flat_set<Command>{
Command::MediaPrevious,
Command::MediaNext,
@@ -112,6 +126,8 @@ const auto CommandByName = base::flat_map<QString, Command>{
{ u"show_chat_menu"_q , Command::ShowChatMenu },
{ u"show_chat_preview"_q , Command::ShowChatPreview },
{ u"record_voice"_q , Command::RecordVoice },
// Shortcuts that have no default values.
{ u"message"_q , Command::JustSendMessage },
{ u"message_silently"_q , Command::SendSilentMessage },
@@ -119,6 +135,7 @@ const auto CommandByName = base::flat_map<QString, Command>{
{ u"media_viewer_video_fullscreen"_q , Command::MediaViewerFullscreen },
{ u"show_scheduled"_q , Command::ShowScheduled },
{ u"archive_chat"_q , Command::ArchiveChat },
{ u"record_round"_q , Command::RecordRound },
//
};
@@ -140,6 +157,7 @@ const base::flat_map<Command, QString> &CommandNames() {
Command::MediaViewerFullscreen,
Command::ShowScheduled,
Command::ArchiveChat,
Command::RecordRound,
};
class Manager {
@@ -152,6 +170,7 @@ public:
void toggleMedia(bool toggled);
void toggleSupport(bool toggled);
void listen(not_null<QWidget*> widget);
[[nodiscard]] bool handles(const QKeySequence &sequence) const;
[[nodiscard]] const QStringList &errors() const;
@@ -357,6 +376,10 @@ void Manager::listen(not_null<QWidget*> widget) {
}
}
bool Manager::handles(const QKeySequence &sequence) const {
return _shortcuts.contains(sequence);
}
void Manager::pruneListened() {
for (auto i = begin(_listened); i != end(_listened);) {
if (i->data()) {
@@ -466,10 +489,6 @@ void Manager::fillDefaults() {
set(u"ctrl+pgup"_q, Command::ChatPrevious);
set(u"alt+up"_q, Command::ChatPrevious);
set(u"%1+tab"_q.arg(ctrl), Command::ChatNext);
set(u"%1+shift+tab"_q.arg(ctrl), Command::ChatPrevious);
set(u"%1+backtab"_q.arg(ctrl), Command::ChatPrevious);
set(u"ctrl+alt+home"_q, Command::ChatFirst);
set(u"ctrl+alt+end"_q, Command::ChatLast);
@@ -509,6 +528,8 @@ void Manager::fillDefaults() {
set(u"ctrl+\\"_q, Command::ShowChatMenu);
set(u"ctrl+]"_q, Command::ShowChatPreview);
set(u"ctrl+r"_q, Command::RecordVoice);
_defaults = keysCurrents();
}
@@ -788,12 +809,169 @@ const QStringList &Errors() {
return Data.errors();
}
bool MarkChatSwitchKeyPressHandled(Qt::Key key, bool handled) {
if (!key) {
return false;
} else if (handled) {
if (ranges::contains(ChatSwitchKeyPressHandled, key)) {
return false;
}
const auto i = ranges::find(ChatSwitchKeyPressHandled, Qt::Key(0));
Assert(i != end(ChatSwitchKeyPressHandled));
*i = key;
return true;
}
const auto i = ranges::find(ChatSwitchKeyPressHandled, key);
if (i == end(ChatSwitchKeyPressHandled)) {
return false;
}
*i = Qt::Key(0);
return true;
}
bool CancelChatSwitch(Qt::Key result) {
ChatSwitchModifier = Qt::Key();
if (!ChatSwitchStarted) {
return false;
}
ChatSwitchStarted = false;
delete base::take(ChatSwitchFilter);
ChatSwitchStream.fire({ .action = result });
return true;
}
bool NavigateChatSwitch(Qt::Key result) {
if (!ChatSwitchStarted) {
return false;
}
ChatSwitchStream.fire({ .action = result });
return true;
}
bool CheckChatSwitchEvent(Qt::Key key) {
const auto plain = Qt::Key(int(key)
& ~(Qt::ControlModifier
| Qt::ShiftModifier
| Qt::AltModifier
| Qt::MetaModifier));
if (MarkChatSwitchKeyPressHandled(plain, false)) {
return true;
} else if (plain == Qt::Key_Escape) {
return CancelChatSwitch(Qt::Key_Escape);
} else if (plain == Qt::Key_Return || plain == Qt::Key_Enter) {
return CancelChatSwitch(Qt::Key_Enter);
} else if (ranges::contains(kChatSwitchSpecialKeys, plain)) {
return NavigateChatSwitch(Qt::Key(plain));
}
return false;
}
bool HandleEvent(
not_null<QObject*> object,
not_null<QShortcutEvent*> event) {
if (ChatSwitchStarted) {
const auto full = event->key();
for (auto i = 0; i != full.count(); ++i) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
const auto raw = full[i].key();
#else // Qt >= 6.0.0
const auto raw = full[i];
#endif // Qt < 6.0.0
if (CheckChatSwitchEvent(Qt::Key(raw))) {
return true;
}
}
return false;
}
return Launch(Data.lookup(object));
}
rpl::producer<ChatSwitchRequest> ChatSwitchRequests() {
return ChatSwitchStream.events();
}
bool HandlePossibleChatSwitch(not_null<QKeyEvent*> event) {
const auto type = event->type();
if (Paused) {
return false;
} else if (type == QEvent::ShortcutOverride) {
const auto key = Qt::Key(event->key());
const auto ctrl = Platform::IsMac()
? Qt::MetaModifier
: Qt::ControlModifier;
if ((event->modifiers() & ctrl)
&& ranges::contains(kChatSwitchSpecialKeys, key)) {
// For Ctrl+Q in case of active chat switch we have
// on Windows always ShortcutOverride + KeyPress,
// while on macOS we have just ShortcutOverride for
// the first press and KeyPress only for repeat events.
//
// This complex scheme allows to handle both cases with
// firing the switch event Key_Q exactly once initially.
if (CheckChatSwitchEvent(key)) {
MarkChatSwitchKeyPressHandled(key, true);
}
crl::on_main([=] {
MarkChatSwitchKeyPressHandled(key, false);
});
}
if (Data.handles(QKeySequence(ctrl | Qt::Key_Tab))
&& (Data.handles(ctrl | Qt::ShiftModifier | Qt::Key_Backtab)
|| Data.handles(ctrl | Qt::ShiftModifier | Qt::Key_Tab)
|| Data.handles(QKeySequence(ctrl | Qt::Key_Backtab)))) {
return false;
} else if (key == Qt::Key_Control || key == Qt::Key_Meta) {
ChatSwitchModifier = key;
} else if (key == Qt::Key_Tab || key == Qt::Key_Backtab) {
const auto modifiers = event->modifiers();
if (modifiers & ctrl) {
if (ChatSwitchModifier == Qt::Key()) {
ChatSwitchModifier = Platform::IsMac()
? Qt::Key_Meta
: Qt::Key_Control;
}
const auto action = (modifiers & Qt::ShiftModifier)
? Qt::Key_Backtab
: key;
if (Data.handles(modifiers | key)) {
return false;
} else if (action == Qt::Key_Tab
&& Data.handles(QKeySequence(ctrl | Qt::Key_Tab))) {
return false;
} else if (action == Qt::Key_Backtab
&& Data.handles(QKeySequence(ctrl | Qt::Key_Backtab))) {
return false;
} else if (action == Qt::Key_Backtab
&& Data.handles(ctrl | Qt::ShiftModifier | Qt::Key_Tab)) {
return false;
}
const auto started = !std::exchange(ChatSwitchStarted, true);
if (started) {
Assert(!ChatSwitchFilter);
ChatSwitchFilter = base::install_event_filter(qApp, [=](not_null<QEvent*> e) {
return (e->type() == QEvent::InputMethod)
? base::EventFilterResult::Cancel
: base::EventFilterResult::Continue;
});
}
ChatSwitchStream.fire({
.action = action,
.started = started,
});
return true;
}
}
} else if (type == QEvent::KeyPress) {
return CheckChatSwitchEvent(Qt::Key(event->key()));
} else if (type == QEvent::KeyRelease) {
const auto key = Qt::Key(event->key());
if (key == ChatSwitchModifier) {
CancelChatSwitch(Qt::Key_Enter);
}
}
return false;
}
void ToggleMediaShortcuts(bool toggled) {
Data.toggleMedia(toggled);
}

View File

@@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class QKeyEvent;
class QShortcutEvent;
namespace Shortcuts {
enum class Command {
@@ -66,6 +69,9 @@ enum class Command {
SendSilentMessage,
ScheduleMessage,
RecordVoice,
RecordRound,
ReadChat,
ArchiveChat,
@@ -119,7 +125,7 @@ private:
};
rpl::producer<not_null<Request*>> Requests();
[[nodiscard]] rpl::producer<not_null<Request*>> Requests();
void Start();
void Finish();
@@ -129,7 +135,15 @@ void Listen(not_null<QWidget*> widget);
bool Launch(Command command);
bool HandleEvent(not_null<QObject*> object, not_null<QShortcutEvent*> event);
const QStringList &Errors();
bool HandlePossibleChatSwitch(not_null<QKeyEvent*> event);
struct ChatSwitchRequest {
Qt::Key action = Qt::Key_Tab; // Key_Tab, Key_Backtab or Key_Escape.
bool started = false;
};
[[nodiscard]] rpl::producer<ChatSwitchRequest> ChatSwitchRequests();
[[nodiscard]] const QStringList &Errors();
// Media shortcuts are not enabled by default, because other
// applications also use them. They are enabled only when

View File

@@ -15,7 +15,6 @@ extern "C" {
#include <openssl/sha.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/engine.h>
#include <openssl/conf.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>

View File

@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
constexpr auto AppName = "Telegram Desktop"_cs;
constexpr auto AppFile = "Telegram"_cs;
constexpr auto AppVersion = 6000002;
constexpr auto AppVersionStr = "6.0.2";
constexpr auto AppVersion = 6001003;
constexpr auto AppVersionStr = "6.1.3";
constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/components/recent_peers.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "history/history.h"
#include "main/main_session.h"
#include "storage/serialize_common.h"
#include "storage/serialize_peer.h"
@@ -16,6 +19,7 @@ namespace Data {
namespace {
constexpr auto kLimit = 48;
constexpr auto kMaxRememberedOpenChats = 32;
} // namespace
@@ -133,4 +137,30 @@ void RecentPeers::applyLocal(QByteArray serialized) {
("Suggestions: RecentPeers read OK, count: %1").arg(_list.size()));
}
std::vector<not_null<Thread*>> RecentPeers::collectChatOpenHistory() const {
_session->local().readSearchSuggestions();
return _opens;
}
void RecentPeers::chatOpenPush(not_null<Thread*> thread) {
const auto i = ranges::find(_opens, thread);
if (i == end(_opens)) {
while (_opens.size() >= kMaxRememberedOpenChats) {
_opens.pop_back();
}
_opens.insert(begin(_opens), thread);
} else if (i != begin(_opens)) {
ranges::rotate(begin(_opens), i, i + 1);
}
}
void RecentPeers::chatOpenRemove(not_null<Thread*> thread) {
_opens.erase(ranges::remove(_opens, thread), end(_opens));
}
void RecentPeers::chatOpenKeepUserpics(
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> userpics) {
_chatOpenUserpicsCache = std::move(userpics);
}
} // namespace Data

View File

@@ -7,12 +7,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ui/userpic_view.h"
namespace Main {
class Session;
} // namespace Main
namespace Data {
class Thread;
class RecentPeers final {
public:
explicit RecentPeers(not_null<Main::Session*> session);
@@ -28,10 +32,22 @@ public:
[[nodiscard]] QByteArray serialize() const;
void applyLocal(QByteArray serialized);
[[nodiscard]] auto collectChatOpenHistory() const
-> std::vector<not_null<Thread*>>;
void chatOpenPush(not_null<Thread*> thread);
void chatOpenRemove(not_null<Thread*> thread);
void chatOpenKeepUserpics(
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> userpics);
private:
const not_null<Main::Session*> _session;
std::vector<not_null<PeerData*>> _list;
std::vector<not_null<Thread*>> _opens;
base::flat_map<
not_null<PeerData*>,
Ui::PeerUserpicView> _chatOpenUserpicsCache;
rpl::event_stream<> _updates;
};

View File

@@ -0,0 +1,20 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Data {
struct UnreviewedAuth {
uint64 hash = 0;
bool unconfirmed = false;
TimeId date = 0;
QString device;
QString location;
};
} // namespace Data

View File

@@ -66,7 +66,7 @@ struct PeerUpdate {
Notifications = (1ULL << 4),
Migration = (1ULL << 5),
UnavailableReason = (1ULL << 6),
ChatThemeEmoji = (1ULL << 7),
ChatThemeToken = (1ULL << 7),
ChatWallPaper = (1ULL << 8),
IsBlocked = (1ULL << 9),
MessagesTTL = (1ULL << 10),

View File

@@ -235,7 +235,8 @@ void ChannelData::setFlags(ChannelDataFlags which) {
| Flag::CallNotEmpty
| Flag::SimilarExpanded
| Flag::Signatures
| Flag::SignatureProfiles)) {
| Flag::SignatureProfiles
| Flag::ForumTabs)) {
if (const auto history = this->owner().historyLoaded(this)) {
if (diff & Flag::CallNotEmpty) {
history->updateChatListEntry();
@@ -262,6 +263,9 @@ void ChannelData::setFlags(ChannelDataFlags which) {
if (diff & (Flag::Signatures | Flag::SignatureProfiles)) {
session().changes().peerUpdated(this, UpdateFlag::Rights);
}
if (diff & Flag::ForumTabs) {
history->forumTabsChanged(which & Flag::ForumTabs);
}
}
}
if (const auto raw = takenForum.get()) {
@@ -1402,7 +1406,7 @@ void ApplyChannelUpdate(
update.vboosts_applied().value_or_empty(),
update.vboosts_unrestrict().value_or_empty());
}
channel->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
channel->setThemeToken(qs(update.vtheme_emoticon().value_or_empty()));
channel->setTranslationDisabled(update.is_translations_disabled());
const auto reactionsLimit = update.vreactions_limit().value_or_empty();

View File

@@ -486,7 +486,7 @@ void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) {
SetTopPinnedMessageId(chat, pinned->v);
}
chat->checkFolder(update.vfolder_id().value_or_empty());
chat->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
chat->setThemeToken(qs(update.vtheme_emoticon().value_or_empty()));
chat->setTranslationDisabled(update.is_translations_disabled());
const auto reactionsLimit = update.vreactions_limit().value_or_empty();
if (const auto allowed = update.vavailable_reactions()) {

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_cloud_themes.h"
#include "api/api_premium.h"
#include "window/themes/window_theme.h"
#include "window/themes/window_theme_preview.h"
#include "window/themes/window_theme_editor_box.h"
@@ -26,6 +27,7 @@ namespace {
constexpr auto kFirstReloadTimeout = 10 * crl::time(1000);
constexpr auto kReloadTimeout = 3600 * crl::time(1000);
constexpr auto kGiftThemesLimit = 24;
bool IsTestingColors/* = false*/;
@@ -37,44 +39,38 @@ CloudTheme CloudTheme::Parse(
bool parseSettings) {
const auto document = data.vdocument();
const auto paper = [&](const MTPThemeSettings &settings) {
return settings.match([&](const MTPDthemeSettings &data) {
return data.vwallpaper()
? WallPaper::Create(session, *data.vwallpaper())
: std::nullopt;
});
const auto &data = settings.data();
return data.vwallpaper()
? WallPaper::Create(session, *data.vwallpaper())
: std::nullopt;
};
const auto outgoingMessagesColors = [&](
const MTPThemeSettings &settings) {
auto result = std::vector<QColor>();
settings.match([&](const MTPDthemeSettings &data) {
if (const auto colors = data.vmessage_colors()) {
for (const auto &color : colors->v) {
result.push_back(Ui::ColorFromSerialized(color));
}
const auto &data = settings.data();
if (const auto colors = data.vmessage_colors()) {
for (const auto &color : colors->v) {
result.push_back(Ui::ColorFromSerialized(color));
}
});
}
return result;
};
const auto accentColor = [&](const MTPThemeSettings &settings) {
return settings.match([&](const MTPDthemeSettings &data) {
return Ui::ColorFromSerialized(data.vaccent_color());
});
const auto &data = settings.data();
return Ui::ColorFromSerialized(data.vaccent_color());
};
const auto outgoingAccentColor = [&](const MTPThemeSettings &settings) {
return settings.match([&](const MTPDthemeSettings &data) {
return Ui::MaybeColorFromSerialized(data.voutbox_accent_color());
});
const auto &data = settings.data();
return Ui::MaybeColorFromSerialized(data.voutbox_accent_color());
};
const auto basedOnDark = [&](const MTPThemeSettings &settings) {
return settings.match([&](const MTPDthemeSettings &data) {
return data.vbase_theme().match([](
const MTPDbaseThemeNight &) {
return true;
}, [](const MTPDbaseThemeTinted &) {
return true;
}, [](const auto &) {
return false;
});
const auto &data = settings.data();
return data.vbase_theme().match([](const MTPDbaseThemeNight &) {
return true;
}, [](const MTPDbaseThemeTinted &) {
return true;
}, [](const auto &) {
return false;
});
};
const auto settings = [&] {
@@ -111,6 +107,80 @@ CloudTheme CloudTheme::Parse(
};
}
CloudTheme CloudTheme::Parse(
not_null<Main::Session*> session,
const MTPDchatThemeUniqueGift &data,
bool parseSettings) {
const auto gift = Api::FromTL(session, data.vgift());
if (!gift || !gift->unique) {
return {};
}
const auto paper = [&](const MTPThemeSettings &settings) {
const auto &data = settings.data();
return data.vwallpaper()
? WallPaper::Create(session, *data.vwallpaper())
: std::nullopt;
};
const auto basedOnDark = [&](const MTPThemeSettings &settings) {
const auto &data = settings.data();
return data.vbase_theme().match([](const MTPDbaseThemeNight &) {
return true;
}, [](const MTPDbaseThemeTinted &) {
return true;
}, [](const auto &) {
return false;
});
};
const auto outgoingMessagesColors = [&](
const MTPThemeSettings &settings) {
auto result = std::vector<QColor>();
const auto &data = settings.data();
if (const auto colors = data.vmessage_colors()) {
for (const auto &color : colors->v) {
result.push_back(Ui::ColorFromSerialized(color));
}
//} else if (basedOnDark(settings)) {
// result.push_back(gift->unique->backdrop.edgeColor);
//} else {
// result.push_back(anim::color(
// gift->unique->backdrop.patternColor,
// QColor(255, 255, 255, 255),
// 0.75));
}
return result;
};
const auto accentColor = [&](const MTPThemeSettings &settings) {
const auto &data = settings.data();
return Ui::ColorFromSerialized(data.vaccent_color());
};
const auto outgoingAccentColor = [&](const MTPThemeSettings &settings) {
const auto &data = settings.data();
return Ui::MaybeColorFromSerialized(
data.voutbox_accent_color()
);// .value_or(gift->unique->backdrop.patternColor);
};
const auto settings = [&] {
auto result = base::flat_map<Type, Settings>();
for (const auto &fields : data.vtheme_settings().v) {
const auto type = basedOnDark(fields) ? Type::Dark : Type::Light;
result.emplace(type, Settings{
.paper = paper(fields),
.accentColor = accentColor(fields),
.outgoingAccentColor = outgoingAccentColor(fields),
.outgoingMessagesColors = outgoingMessagesColors(fields),
});
}
return result;
};
return {
.id = gift->unique->id,
.unique = gift->unique,
.settings = (parseSettings
? settings()
: base::flat_map<Type, Settings>()),
};
}
CloudTheme CloudTheme::Parse(
not_null<Main::Session*> session,
const MTPTheme &data,
@@ -382,9 +452,16 @@ rpl::producer<> CloudThemes::chatThemesUpdated() const {
return _chatThemesUpdates.events();
}
std::optional<CloudTheme> CloudThemes::themeForEmoji(
const QString &emoticon) const {
const auto emoji = Ui::Emoji::Find(emoticon);
std::optional<CloudTheme> CloudThemes::themeForToken(
const QString &token) const {
if (token.startsWith(u"gift:"_q)) {
const auto id = QStringView(token).mid(5).toULongLong();
const auto i = _giftThemes.find(id);
return (i != end(_giftThemes))
? i->second
: std::optional<CloudTheme>();
}
const auto emoji = Ui::Emoji::Find(token);
if (!emoji) {
return {};
}
@@ -394,18 +471,21 @@ std::optional<CloudTheme> CloudThemes::themeForEmoji(
return (i != end(_chatThemes)) ? std::make_optional(*i) : std::nullopt;
}
rpl::producer<std::optional<CloudTheme>> CloudThemes::themeForEmojiValue(
const QString &emoticon) {
rpl::producer<std::optional<CloudTheme>> CloudThemes::themeForTokenValue(
const QString &token) {
if (token.startsWith(u"gift:"_q)) {
return rpl::single(themeForToken(token));
}
const auto testing = TestingColors();
if (!Ui::Emoji::Find(emoticon)) {
if (!Ui::Emoji::Find(token)) {
return rpl::single<std::optional<CloudTheme>>(std::nullopt);
} else if (auto result = themeForEmoji(emoticon)) {
} else if (auto result = themeForToken(token)) {
if (testing) {
return rpl::single(
std::move(result)
) | rpl::then(chatThemesUpdated(
) | rpl::map([=] {
return themeForEmoji(emoticon);
return themeForToken(token);
}) | rpl::filter([](const std::optional<CloudTheme> &theme) {
return theme.has_value();
}));
@@ -418,12 +498,88 @@ rpl::producer<std::optional<CloudTheme>> CloudThemes::themeForEmojiValue(
std::nullopt
) | rpl::then(chatThemesUpdated(
) | rpl::map([=] {
return themeForEmoji(emoticon);
return themeForToken(token);
}) | rpl::filter([](const std::optional<CloudTheme> &theme) {
return theme.has_value();
}) | rpl::take(limit));
}
void CloudThemes::myGiftThemesLoadMore(bool reload) {
if (reload && !_myGiftThemesTokens.empty()) {
_session->api().request(base::take(_myGiftThemesRequestId)).cancel();
}
if (_myGiftThemesRequestId || (!reload && _myGiftThemesLoaded)) {
return;
}
_myGiftThemesRequestId = _session->api().request(
MTPaccount_GetUniqueGiftChatThemes(
MTP_int(reload ? 0 : _myGiftThemesTokens.size()),
MTP_int(kGiftThemesLimit),
MTP_long(_myGiftThemesHash))
).done([=](const MTPaccount_ChatThemes &result) {
_myGiftThemesRequestId = 0;
result.match([&](const MTPDaccount_chatThemes &data) {
if (reload || _myGiftThemesTokens.empty()) {
_myGiftThemesHash = data.vhash().v;
_myGiftThemesTokens.clear();
_myGiftThemesLoaded = false;
}
_session->data().processUsers(data.vusers());
_session->data().processChats(data.vchats());
const auto &list = data.vthemes().v;
const auto got = int(list.size());
_myGiftThemesTokens.reserve(_myGiftThemesTokens.size() + got);
for (const auto &theme : list) {
theme.match([](const MTPDchatTheme &) {
}, [&](const MTPDchatThemeUniqueGift &data) {
_myGiftThemesTokens.push_back(
processGiftThemeGetToken(data));
});
}
_myGiftThemesLoaded = (got < kGiftThemesLimit);
_myGiftThemesUpdates.fire({});
}, [&](const MTPDaccount_chatThemesNotModified &) {
if (!reload) {
_myGiftThemesLoaded = true;
_myGiftThemesUpdates.fire({});
}
});
}).fail([=] {
_myGiftThemesRequestId = 0;
_myGiftThemesLoaded = true;
}).send();
}
const std::vector<QString> &CloudThemes::myGiftThemesTokens() const {
return _myGiftThemesTokens;
}
bool CloudThemes::myGiftThemesReady() const {
return !_myGiftThemesTokens.empty() || _myGiftThemesLoaded;
}
rpl::producer<> CloudThemes::myGiftThemesUpdated() const {
return _myGiftThemesUpdates.events();
}
QString CloudThemes::processGiftThemeGetToken(
const MTPDchatThemeUniqueGift &data) {
auto parsed = CloudTheme::Parse(_session, data, true);
if (parsed.unique) {
const auto id = parsed.unique->id;
_giftThemes[id] = std::move(parsed);
return u"gift:%1"_q.arg(id);
}
return QString();
}
void CloudThemes::refreshChatThemesFor(const QString &token) {
if (token.startsWith(u"gift:"_q)) {
return;
}
refreshChatThemes();
}
bool CloudThemes::TestingColors() {
return IsTestingColors;
}

View File

@@ -22,6 +22,7 @@ class Controller;
namespace Data {
struct UniqueGift;
class DocumentMedia;
enum class CloudThemeType {
@@ -38,6 +39,7 @@ struct CloudTheme {
UserId createdBy = 0;
int usersCount = 0;
QString emoticon;
std::shared_ptr<UniqueGift> unique;
using Type = CloudThemeType;
struct Settings {
@@ -52,6 +54,10 @@ struct CloudTheme {
not_null<Main::Session*> session,
const MTPDtheme &data,
bool parseSettings = false);
static CloudTheme Parse(
not_null<Main::Session*> session,
const MTPDchatThemeUniqueGift &data,
bool parseSettings = false);
static CloudTheme Parse(
not_null<Main::Session*> session,
const MTPTheme &data,
@@ -73,9 +79,18 @@ public:
void refreshChatThemes();
[[nodiscard]] const std::vector<CloudTheme> &chatThemes() const;
[[nodiscard]] rpl::producer<> chatThemesUpdated() const;
[[nodiscard]] std::optional<CloudTheme> themeForEmoji(
const QString &emoticon) const;
[[nodiscard]] auto themeForEmojiValue(const QString &emoticon)
void myGiftThemesLoadMore(bool reload = false);
[[nodiscard]] const std::vector<QString> &myGiftThemesTokens() const;
[[nodiscard]] rpl::producer<> myGiftThemesUpdated() const;
[[nodiscard]] QString processGiftThemeGetToken(
const MTPDchatThemeUniqueGift &data);
[[nodiscard]] bool myGiftThemesReady() const;
void refreshChatThemesFor(const QString &token);
[[nodiscard]] std::optional<CloudTheme> themeForToken(
const QString &token) const;
[[nodiscard]] auto themeForTokenValue(const QString &token)
-> rpl::producer<std::optional<CloudTheme>>;
[[nodiscard]] static bool TestingColors();
@@ -140,6 +155,13 @@ private:
std::vector<CloudTheme> _chatThemes;
rpl::event_stream<> _chatThemesUpdates;
mtpRequestId _myGiftThemesRequestId = 0;
base::flat_map<uint64, CloudTheme> _giftThemes;
std::vector<QString> _myGiftThemesTokens;
rpl::event_stream<> _myGiftThemesUpdates;
uint64 _myGiftThemesHash = 0;
bool _myGiftThemesLoaded = false;
base::Timer _reloadCurrentTimer;
LoadingDocument _updatingFrom;
LoadingDocument _previewFrom;

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data {
struct UniqueGift;
struct UniqueGiftValue;
struct CreditTopupOption final {
uint64 credits = 0;
@@ -71,8 +72,11 @@ struct CreditsHistoryEntry final {
uint64 bareEntryOwnerId = 0;
uint64 giftChannelSavedId = 0;
uint64 stargiftId = 0;
QString giftPrepayUpgradeHash;
std::shared_ptr<UniqueGift> uniqueGift;
Fn<std::vector<CreditsHistoryEntry>()> pinnedSavedGifts;
uint64 nextToUpgradeStickerId = 0;
Fn<void()> nextToUpgradeShow;
CreditsAmount starrefAmount;
int starrefCommission = 0;
uint64 starrefRecipientId = 0;
@@ -98,9 +102,11 @@ struct CreditsHistoryEntry final {
bool converted : 1 = false;
bool anonymous : 1 = false;
bool stargift : 1 = false;
bool postsSearch : 1 = false;
bool giftTransferred : 1 = false;
bool giftRefunded : 1 = false;
bool giftUpgraded : 1 = false;
bool giftUpgradeSeparate : 1 = false;
bool giftResale : 1 = false;
bool giftResaleForceTon : 1 = false;
bool giftPinned : 1 = false;

View File

@@ -1712,6 +1712,10 @@ void DocumentData::forceIsStreamedAnimation() {
setMaybeSupportsStreaming(true);
}
bool DocumentData::isMusicForProfile() const {
return isSong();
}
bool DocumentData::isVoiceMessage() const {
return (type == VoiceDocument);
}
@@ -1740,6 +1744,7 @@ bool DocumentData::isTheme() const {
|| _filename.endsWith(u".tdesktop-palette"_q, Qt::CaseInsensitive)
|| (hasMimeType(u"application/x-tgtheme-tdesktop"_q)
&& (_filename.isEmpty()
|| !_filename.contains('.')
|| _nameType == Core::NameType::ThemeFile));
}

View File

@@ -182,6 +182,7 @@ public:
[[nodiscard]] const VideoData *video() const;
void forceIsStreamedAnimation();
[[nodiscard]] bool isMusicForProfile() const;
[[nodiscard]] bool isVoiceMessage() const;
[[nodiscard]] bool isVideoMessage() const;
[[nodiscard]] bool isSong() const;

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