Compare commits

...

348 Commits

Author SHA1 Message Date
John Preston
bf345da87b Version 1.8.11: Fix build for Xcode. 2019-10-02 00:57:16 +03:00
John Preston
1e5aa2a78d Version 1.8.11.
- Bug fixes and other minor improvements.
2019-10-01 23:59:46 +03:00
John Preston
8cf62c312f Allow not to cache emoji sprites. 2019-10-01 23:59:46 +03:00
John Preston
3b0bf7cb1e Move some more widgets and effects. 2019-10-01 23:59:46 +03:00
John Preston
673072ea5b Reorganize GYP files. 2019-10-01 23:59:46 +03:00
John Preston
849deb57e2 Move many widget classes to lib_ui. 2019-10-01 23:59:46 +03:00
John Preston
dda587a2fc Move QtConnectionOwner to base/qt_connection. 2019-10-01 23:59:46 +03:00
John Preston
c057f28425 Move standard buttons to lib_ui. 2019-10-01 23:59:46 +03:00
John Preston
a16c6ca41a Support more request types. 2019-10-01 23:59:46 +03:00
John Preston
c5845f17ae Remove twidget header. 2019-10-01 23:59:46 +03:00
John Preston
e2f54eb3e9 Move some style code to lib_ui. 2019-10-01 23:59:46 +03:00
John Preston
5a1c8e6a0a Move fonts to a separate .qrc file. 2019-10-01 23:59:46 +03:00
John Preston
b73390a3f6 Support emoji in popup menu items. 2019-10-01 23:59:46 +03:00
John Preston
0c713a930a Remove app.h / facades.h from precompiled header. 2019-10-01 23:59:46 +03:00
John Preston
a87529b8c9 Version 1.8.10.
- Bug fixes and other minor improvements.
2019-10-01 23:59:46 +03:00
John Preston
0c30bbf40a Fix file reference refresh in stickers / GIFs.
Fixes #6592.
2019-09-30 13:25:09 +03:00
John Preston
f75bfb4369 Version 1.8.9.
- Bug fixes and other minor improvements.
2019-09-27 20:04:25 +03:00
John Preston
0c34ba638c Don't show revoke checkbox in Saved Messages. 2019-09-27 20:01:36 +03:00
John Preston
4af9c801ae Don't show sticker suggestions if can't send.
Fixes #6581.
2019-09-27 19:56:54 +03:00
John Preston
e06bf16980 Improve name / title limits. 2019-09-27 19:46:27 +03:00
John Preston
1018745b0b Fix userpic display in custom notifications.
Fixes #6532.
2019-09-27 19:36:19 +03:00
John Preston
47b157bf32 Fix possible crash in custom notifications. 2019-09-27 18:53:44 +03:00
John Preston
09547bd6d5 Fix possible crash in scheduled messages. 2019-09-27 18:49:49 +03:00
RadRussianRus
d17f21c10d Use exact version of libxkbcommon in Travis build 2019-09-27 18:05:26 +03:00
udf
f11339361d Make "All Users" checkbox behave like the "All Actions" checkbox 2019-09-27 18:04:50 +03:00
John Preston
04b9d4bdb5 Fix animated emoji reloading. 2019-09-11 14:00:18 +03:00
John Preston
1d8d2b6251 Fix theme link preview title. 2019-09-10 18:30:47 +03:00
John Preston
190bf8fc5f Version 1.8.8.
- Create new themes based on your color and wallpaper choices.
- Share your themes with other users via links.
- Update your theme for all its users when you change something.
2019-09-10 14:22:20 +03:00
John Preston
f463d3ec6d Divide autoupdate paths for OS versions. 2019-09-10 14:22:20 +03:00
23rd
f9d10094ac Added warning when try to attach new media while editing message. 2019-09-10 14:22:20 +03:00
RadRussianRus
9fc87c3cb8 Fix last message tail drawing in theme preview 2019-09-10 10:48:20 +03:00
Sean
0d9c30423f Add chrpath to CMake instruction
Ref: 02a8b69
2019-09-10 10:13:51 +03:00
John Preston
c4bc76c022 Beta version 1.8.7.
- Bug fixes and other minor improvements.
2019-09-10 00:52:34 +03:00
John Preston
241f5e1d26 Make theme preview fit in minimal window size. 2019-09-10 00:50:15 +03:00
John Preston
76f0abecfd Show sticker set on Ctrl+Click in stickers list. 2019-09-10 00:48:34 +03:00
John Preston
ca45fb617e Fix applying background after theme edit. 2019-09-10 00:36:16 +03:00
John Preston
dfd63e66ff Add import theme to the editor. 2019-09-09 23:58:41 +03:00
John Preston
d85f162bff Fix saving cloud themes from media viewer. 2019-09-09 23:58:30 +03:00
John Preston
1d4fbc64e2 Ensure theme list radiobutton contrast. 2019-09-09 23:25:54 +03:00
John Preston
9fd32fc85b Ensure contrast in critical theme parts. 2019-09-09 23:02:24 +03:00
John Preston
f7f195eb98 Fix transfer ownership button visibility.
Regression was introduced in 569340c7d3.
2019-09-09 21:07:12 +03:00
John Preston
12c7bd8ee1 Suggest theme title based on accent color. 2019-09-09 20:27:07 +03:00
John Preston
cd5ef069c0 Ask to unlock Telegram before saving theme. 2019-09-09 20:26:53 +03:00
John Preston
f90fdce422 Fix crash on quit from passcode with a box. 2019-09-09 18:54:38 +03:00
John Preston
423254f7eb Fix theme editor closing in a small window.
Fixes #6510.
2019-09-09 18:37:04 +03:00
John Preston
c9c0d74b68 Fix build for Linux. 2019-09-09 17:50:26 +03:00
John Preston
8bf6013342 Add 'Export theme' / 'Show palette file' menu. 2019-09-09 17:44:08 +03:00
John Preston
c1ab1acd44 Fix crash in scheduled messages 'Send Now'. 2019-09-09 17:43:39 +03:00
John Preston
90b955534a Beta version 1.8.6: Improve theme preview. 2019-09-09 16:13:16 +03:00
John Preston
61c7bf2f5e Beta version 1.8.6.
- Bug fixes and other minor improvements.
2019-09-09 14:56:55 +03:00
John Preston
ee5423762a Generate previews for cloud theme links. 2019-09-09 14:56:05 +03:00
John Preston
6d29dc3b36 Apply themes in settings without preview. 2019-09-09 12:13:07 +03:00
John Preston
aa10934e85 Fix messing up accent color if editor is opened. 2019-09-09 12:02:06 +03:00
John Preston
9ff1fbcf47 Support theme document file origins. 2019-09-09 11:51:07 +03:00
John Preston
51c1dc20e1 Unregister randomId if send message fails. 2019-09-09 11:13:41 +03:00
John Preston
3a3bf84cfc Fix crash in SeparatePanel destruction.
We should destroy layers before panel widget destructor started.
We do it already for MainWindow in clearWidgetsHook.
2019-09-09 10:52:03 +03:00
John Preston
037b936613 Fix possible crash with emoji load failure. 2019-09-09 10:45:18 +03:00
John Preston
b631d09a40 Fix possible crash in event loop nesting. 2019-09-09 10:45:18 +03:00
John Zimmermann
fd4f384c3b Allow GDK wayland backend when Telegram is running under Wayland
and make sure that it never tries to load gtk2 under wayland, it can
lead to Telegram crashing
2019-09-09 10:25:01 +03:00
RadRussianRus
428a501bac Show Beginning button only for message date
Also hide button when you're already at beginning.
2019-09-09 10:17:23 +03:00
RadRussianRus
3ca57ae50d Added "Beginning" button to calendar 2019-09-09 10:17:23 +03:00
RadRussianRus
f1eddcd584 Make link preview title clickable 2019-09-09 10:15:32 +03:00
Sameer Hoosen
f979df3dfe Allow searching for messages by a user who has left a chat (#6417)
Fixes #5667
2019-09-09 10:05:29 +03:00
John Preston
eebcdb842d Generate previews async in cloud themes list. 2019-09-09 09:59:57 +03:00
John Preston
048658f838 Fix deleting theme from the list. 2019-09-09 08:54:17 +03:00
John Preston
8fd17e2e8f Fix theme with background with colorizer. 2019-09-09 08:53:16 +03:00
John Preston
5c13214244 Beta version 1.8.5: Fix build for old OS X. 2019-09-08 23:42:40 +03:00
John Preston
12a020cd09 Beta version 1.8.5: Fix build for Linux. 2019-09-08 23:38:45 +03:00
John Preston
b98e02f326 Beta version 1.8.5: Fix build on macOS. 2019-09-08 23:04:25 +03:00
John Preston
9a521c5340 Beta version 1.8.5.
- Create new themes based on your color and wallpaper choices.
- Share your themes with other users via links.
- Update your theme for all its users when you change something.
2019-09-08 21:38:58 +03:00
John Preston
5f26e92f5c Fix scheduled messages from ShareBox.
Fixes #6497.
2019-09-08 21:26:17 +03:00
John Preston
3d78c637a2 Strip editor information from palette. 2019-09-08 21:21:54 +03:00
John Preston
2ab2eed633 Push new saved theme to the list. 2019-09-08 21:05:35 +03:00
John Preston
bb7018424a Copy adjusted color values to editor. 2019-09-08 21:00:31 +03:00
John Preston
a770b5d4cd Allow share and remove cloud themes. 2019-09-08 20:32:25 +03:00
John Preston
c92a9585e1 Check changes when saving theme. 2019-09-08 19:29:43 +03:00
John Preston
cedb2d31af Check palette changes on editor cancel. 2019-09-08 16:40:15 +03:00
John Preston
95da2dbc34 Move create theme button to three-dot menu. 2019-09-08 15:47:22 +03:00
John Preston
f9b2a8d6ac Show progress in theme saving. 2019-09-08 14:52:42 +03:00
John Preston
a773ad7b02 Fix reverting edited theme values. 2019-09-06 19:31:07 +03:00
John Preston
32287a51f9 Don't show warning on theme autoupdate. 2019-09-06 19:31:07 +03:00
John Preston
37a4c79c81 Open theme preview after loading. 2019-09-06 19:31:07 +03:00
John Preston
95ee17bd54 Optimize theme edit data clearing. 2019-09-06 19:31:07 +03:00
John Preston
469c6770fb Handle t.me/addtheme links. 2019-09-06 19:31:07 +03:00
John Preston
e38123cc48 Send theme install requests. 2019-09-06 19:31:07 +03:00
John Preston
e3d7bf771f Update current theme in realtime. 2019-09-06 19:31:07 +03:00
John Preston
1cda90c3c5 Check fields before uploading theme. 2019-09-06 19:31:07 +03:00
John Preston
9c86f0e0a5 Save themes, apply and close editor on save. 2019-09-06 19:31:07 +03:00
John Preston
910f16312c Show not supported themes placeholders. 2019-09-06 19:31:07 +03:00
23rd
639b4bdd27 Fixed badge update during theme testing. 2019-09-06 19:31:07 +03:00
John Preston
4951eeac98 Allow creating desktop part of multi-theme. 2019-09-06 19:31:07 +03:00
John Preston
79106e0c01 Correctly show custom themes in list. 2019-09-06 19:31:07 +03:00
John Preston
7485f0c960 Don't change real theme when editing. 2019-09-06 19:31:07 +03:00
John Preston
03bdd80b2f Open theme editor for existing themes. 2019-09-06 19:31:07 +03:00
John Preston
dd74f57a66 Display full themes list in Settings. 2019-09-06 19:31:07 +03:00
John Preston
534772722e Improve theme selector design. 2019-09-06 19:31:07 +03:00
John Preston
6343221d7b Remove many includes from stdafx.pch. 2019-09-06 19:31:07 +03:00
John Preston
4929de2bfb Apply cloud themes. 2019-09-06 19:31:06 +03:00
John Preston
ac8f924909 Show tdesktop cloud themes in a box. 2019-09-06 19:31:06 +03:00
John Preston
95afcbb485 Format a valid theme file name. 2019-09-06 19:31:06 +03:00
John Preston
229bc56cc8 Upload saved theme to the cloud. 2019-09-06 19:31:06 +03:00
John Preston
4b045a602c Add a box for new theme creating. 2019-09-06 19:31:06 +03:00
John Preston
1e3b72ab74 Ignore background when checking which theme is used. 2019-09-06 19:31:06 +03:00
John Preston
bc63d9fe53 Fix check display in background box. 2019-09-06 19:31:06 +03:00
John Preston
d7cb8b7065 Version 1.8.4: Fix crash in local messages. 2019-09-06 16:41:43 +03:00
John Preston
30f4d870c5 Version 1.8.4: Fix crash on failed message delete. 2019-09-06 14:46:09 +03:00
John Preston
ad515c6f4a Version 1.8.4.
- Bug fixes and other minor improvements.
2019-09-06 14:39:04 +03:00
John Preston
056949416d Apply some rlottie fixes. 2019-09-06 14:37:41 +03:00
John Preston
ab6d9ff73c Fix sending first message in chat. 2019-09-06 14:14:04 +03:00
John Preston
8b766dda8e Fix crash in scheduled message in admin log.
Fixes #6495.
2019-09-06 12:20:59 +03:00
John Preston
ee0f66d746 Fix crash in history clear while sending messages.
Don't remove messages that are currently being sent from locals.
2019-09-06 10:56:01 +03:00
John Preston
7893ad0558 Mark all types of local history items. 2019-09-06 10:53:44 +03:00
John Preston
b0c5a75fb9 Version 1.8.3.
- Right click the 'Send' button and select 'Schedule Message'
to automatically send something at a specified time.
- Schedule reminders for yourself in the 'Saved Messages' chat.
- Get a notification when any of your scheduled messages are sent.
- Customize your app's appearance by choosing accent colors
for the 'Day', 'Night' and 'Tinted' themes.
- Choose who can find you on Telegram
when they add your number to their phone contacts.
- Send a single 😁, 😧, 😡, :poo:, 😢 or
😮 to check out what's new in the animated emoji department.
2019-09-05 21:49:20 +03:00
John Preston
1c313da888 Add dummy handler for addtheme links. 2019-09-05 21:49:20 +03:00
John Preston
ee210ea701 Update API scheme. 2019-09-05 20:42:45 +03:00
John Preston
e0c0d79be9 Fix build for GCC. 2019-09-02 15:22:01 +03:00
John Preston
cb3bad31fa Put 'Classic' theme first. 2019-09-02 14:27:22 +03:00
John Preston
917696be36 Closed alpha version 1.8.2.3. 2019-09-02 13:43:37 +03:00
John Preston
253816641c Submit by keyboard in scheduled section. 2019-09-02 13:39:43 +03:00
John Preston
ca2692473d Fix inactive window notifications clearing. 2019-09-02 13:39:43 +03:00
John Preston
8000dfac01 Fix crash on admin log exit by Escape. 2019-09-02 13:39:43 +03:00
John Preston
85cca51154 Make accent color radiobutton animated. 2019-09-02 13:39:43 +03:00
John Preston
87ea49e094 Allow schedule messages while in slowmode counter. 2019-09-02 13:39:43 +03:00
John Preston
66bf48e21e Remove 'Send without sound' in Saved messages. 2019-08-31 14:43:19 +03:00
John Preston
451056d2ab Track reminder unread status without cheating. 2019-08-31 14:32:15 +03:00
John Preston
791ae64a90 Fix crash in local messages tracking. 2019-08-31 14:25:24 +03:00
John Preston
3b4563772e Disable edit self restrictions in UI. 2019-08-31 14:25:02 +03:00
John Preston
16b786186b Fix schedule time selection. 2019-08-31 14:21:52 +03:00
John Preston
7be1c4ca2f Fix crash in right click on CalendarBox.
Any attempt to destroy a widget on right click will crash,
because Qt anyway sends QContextMenuEvent to the same widget.

Fixes #6464.
2019-08-31 14:15:30 +03:00
John Preston
17fba16c23 Fix crash on quit without initial passcode unlock. 2019-08-30 23:30:12 +03:00
John Preston
5fc4dcd172 Closed alpha version 1.8.2.2. 2019-08-30 18:45:07 +03:00
John Preston
b27a2cd34a Use edit_hide flag to hide 'edited' badge. 2019-08-30 18:00:32 +03:00
John Preston
569340c7d3 Move transfer ownership button. 2019-08-30 17:06:37 +03:00
John Preston
3715fa4b1e Fix scheduled notifications on inbox read. 2019-08-30 17:06:21 +03:00
John Preston
d95e54cb1a Check if it is possible to 'Send now.' 2019-08-30 16:17:46 +03:00
John Preston
470b67f557 Refresh file reference from scheduled messages. 2019-08-30 15:42:01 +03:00
John Preston
c46bcef9ff Add 'Send now' confirmation to langpack. 2019-08-30 15:24:42 +03:00
John Preston
c31cda0587 Use better accent color suggestions. 2019-08-30 13:32:15 +03:00
John Preston
5758f756c9 Fix draft clearing on message send.
Regression was introduced in caef7dde24.
2019-08-30 13:31:45 +03:00
John Preston
f199205592 Limit scheduled requests for one per minute. 2019-08-29 20:16:37 +03:00
John Preston
2b656f7745 Closed alpha version 1.8.2.1. 2019-08-29 16:16:22 +03:00
23rd
7be286751b Fixed text width in toast widget.
- Fixed #6381.
2019-08-29 16:16:22 +03:00
23rd
04617e4a12 Fixed display of tooltip for date of stickers.
- Fixed #6426.
 - Regression was introduced in b814c6307a.
2019-08-29 16:16:22 +03:00
John Preston
405ccb8580 Add editing of AddedByPhone privacy. 2019-08-29 11:44:03 +03:00
John Preston
131ef4f15a Fix palette generation on macOS. 2019-08-29 10:01:28 +03:00
John Preston
07f45b7eab Improve published scheduled notifications. 2019-08-29 10:01:28 +03:00
John Preston
87addd41b1 Add new phrases for reminders. 2019-08-29 10:01:28 +03:00
John Preston
0a4f91a53d Notify about published scheduled messages. 2019-08-29 10:01:28 +03:00
John Preston
3b76a908a4 Try to fix a crash in QNetworkReplyHttpImpl. 2019-08-29 10:01:28 +03:00
John Preston
9e3bc966c8 Fix crash in window destruction. 2019-08-29 10:01:28 +03:00
John Preston
149c69809d Apply peer restrictions. 2019-08-29 10:01:28 +03:00
John Preston
97a239a8b4 Show full emoji in fields in all scales. 2019-08-29 10:01:28 +03:00
John Preston
bd7cee2252 Improve accent color choose design. 2019-08-29 10:01:28 +03:00
John Preston
66d0d6e8fe Use HSV color space for colorizing. 2019-08-29 10:01:28 +03:00
John Preston
117d6192fa Ensure contrast in colorized themes. 2019-08-29 10:01:28 +03:00
John Preston
763bdf8798 Apply lightness limits in accent colors. 2019-08-29 10:01:28 +03:00
John Preston
56a82600f8 Add HSL color picker box for theming. 2019-08-29 10:01:28 +03:00
John Preston
a3e993253c Keep colorized theme in editor. 2019-08-29 10:01:28 +03:00
John Preston
04d5158ae3 Remove unused LeftOutlineButton type. 2019-08-29 10:01:28 +03:00
John Preston
38e4daacd4 Use HSL color space for accent colors. 2019-08-29 10:01:28 +03:00
John Preston
44d156760e Fix build in Xcode. 2019-08-27 19:10:37 +03:00
John Preston
d66541989e Improve first socket message. 2019-08-27 19:10:37 +03:00
John Preston
56c4d164f3 Fix crash in group creation. 2019-08-27 19:10:37 +03:00
John Preston
e2c1c4c8de Fix jump to first chat with archive. 2019-08-27 19:10:37 +03:00
John Preston
a465117689 Allow choosing accent colors from palette. 2019-08-27 19:10:37 +03:00
John Preston
c2117e7722 Save embedded themes accent colors. 2019-08-27 19:10:37 +03:00
John Preston
7de28fc4bd Apply saturation and value in colorizer. 2019-08-27 19:10:37 +03:00
John Preston
529ef64257 Sort colors by hue distance. 2019-08-27 19:10:37 +03:00
John Preston
9cb5423d40 Allow changing accent color in default themes. 2019-08-27 19:10:37 +03:00
John Preston
dd136350fb Fix save color in theme editor. 2019-08-27 19:10:37 +03:00
John Preston
ef7087348a Allow to schedule created polls. 2019-08-27 19:10:37 +03:00
John Preston
8eac2dcb78 Correctly show scheduled polls. 2019-08-27 19:10:37 +03:00
John Preston
f690f93f32 Allow schedule of files, stickers, GIFs. 2019-08-27 19:10:37 +03:00
John Preston
77ebdd3576 Send from scheduled messages section. 2019-08-27 19:10:37 +03:00
John Preston
3e895d0e85 Make tabbed selector working in scheduled section. 2019-08-27 19:10:37 +03:00
John Preston
385a7eb00d Start scheduled compose controls. 2019-08-27 19:10:37 +03:00
John Preston
1c9775baf9 Improve scheduled messages icon design. 2019-08-27 19:10:37 +03:00
John Preston
fb96d2eef8 Improve schedule box design. 2019-08-27 19:10:37 +03:00
John Preston
debeb61540 Start schedule message box. 2019-08-27 19:10:37 +03:00
John Preston
03cdddfe18 Allow sending scheduled messages. 2019-08-27 19:10:37 +03:00
John Preston
caef7dde24 Bundle silent and scheduled to Api::SendOptions. 2019-08-27 19:10:37 +03:00
John Preston
0b08810d5a Update hash counting for scheduled messages. 2019-08-27 19:10:37 +03:00
John Preston
694f771131 Allow to send scheduled messages instantly. 2019-08-27 19:10:37 +03:00
John Preston
956bb876f6 Sort scheduled messages on an update. 2019-08-27 19:10:37 +03:00
John Preston
99037d3d46 Improve scheduled messages top bar. 2019-08-27 19:10:37 +03:00
John Preston
ea0a616453 Allow deleting scheduled messages. 2019-08-27 19:10:37 +03:00
John Preston
815a18be94 Add initial scheduled list implementation. 2019-08-27 19:10:37 +03:00
John Preston
3814b0833d Keep track of scheduled messages. 2019-08-27 19:10:37 +03:00
John Preston
549789bfb7 Update API scheme to layer 105. 2019-08-27 19:10:37 +03:00
John Preston
a539fad3e2 Version 1.8.2: Fix passcode unlock. 2019-08-20 16:03:21 +03:00
John Preston
ee96d78656 Version 1.8.2: Fix build for Qt 5.3.2. 2019-08-20 14:29:21 +03:00
John Preston
288c1130b9 Version 1.8.2: Fix build. 2019-08-20 14:28:40 +03:00
John Preston
d1083a1fb4 Version 1.8.2.
- Bug fixes and other minor improvements.
2019-08-20 12:49:51 +03:00
John Preston
e1fe373504 Fix localtime-related slowmode errors. 2019-08-20 12:42:37 +03:00
23rd
1a06714f3a Added ability to apply formatting from Mac menu. 2019-08-20 12:02:37 +03:00
John Preston
7316d24ca4 Quick fix for 'Cant forward here' error. 2019-08-19 15:26:12 +03:00
John Preston
b814c6307a Check that sticker drag point is inside. 2019-08-19 15:26:12 +03:00
23rd
06fbb2edc4 Added permanent date display for sticker when fast action is enabled. 2019-08-19 15:26:12 +03:00
23rd
0ee47bb10f Fixed fast action button and date position for stickers. 2019-08-19 15:26:12 +03:00
23rd
9f228d8146 Fixed fast share button in messages with big emoji and reply. 2019-08-19 15:26:12 +03:00
23rd
de270d5283 Fixed right action click handler on stickers without stickerpack. 2019-08-19 15:26:12 +03:00
John Preston
3f041545bf Fix crash in local passcode reset. 2019-08-19 15:26:12 +03:00
John Preston
1139a59818 Fix crash in editing message to empty. 2019-08-19 15:26:12 +03:00
John Preston
1e64ba8f29 Fix app config refresh timeout. 2019-08-19 15:26:12 +03:00
John Preston
65342559c7 Use specific tag for libxkbcommon in docs. 2019-08-19 15:23:37 +03:00
John Preston
e150db1d0b Use specific tag for libkxbcommon in snap build. 2019-08-19 13:34:24 +03:00
John Preston
4548b14b45 Fix libvdpau repository link in snap build. 2019-08-19 13:15:32 +03:00
John Preston
dac1128dc9 Version 1.8.1.
- Bug fixes and other minor improvements.
2019-08-09 20:36:21 +01:00
John Preston
c3944d95b4 Fix local caching.
Regression was introduced in 9cf4cf6dca.

Fixes #6377.
2019-08-09 20:33:05 +01:00
John Preston
02b65a42f7 Move message client flags to a separate field. 2019-08-09 20:19:23 +01:00
John Preston
4869ce2247 Version 1.8.
- Right click the Send button to send any message
without sound - in case the recipient is sleeping.
- Enable Slow Mode in Group Permissions to control
how frequently members can post.
- Set custom titles for group admins - like 'Founder',
'CFO' or 'Spam Fighter'.
- Toggle looped playback for animated stickers in Chat Settings.
- Send a single ❤️, :like:, 😒, 😳 or :party:
to add a mighty animated emoji to the chat.
2019-08-09 11:51:36 +01:00
John Preston
e40267f45b Add :party: emoji replacement. 2019-08-09 11:51:36 +01:00
John Preston
e9ab8df737 Use QLocale::uiLanguages for system language. 2019-08-09 11:51:36 +01:00
Vitaly Lipatov
9276101809 Get language name and country name from QLocale (Linux) 2019-08-09 11:51:36 +01:00
John Zimmermann
0c3773486e linux/specific_linux.h: remove unnecessary include of execinfo 2019-08-09 11:51:35 +01:00
Konstantin Popov
b831766375 Make .desktop file search respect TDESKTOP_LAUNCHER_FILENAME 2019-08-09 11:51:35 +01:00
Nicholas Guriev
325989ed47 Fix small typo 2019-08-09 11:51:35 +01:00
John Preston
63203ecc00 Fix int32 overflow in local storage stats. 2019-08-09 11:51:35 +01:00
John Preston
93793d8bdd Remove thumbnail for colored animated emoji. 2019-08-09 11:51:35 +01:00
John Preston
6710372d27 Fix build in Xcode. 2019-08-09 11:51:35 +01:00
John Preston
a8c94deca8 Retry signing if failed. 2019-08-09 11:51:35 +01:00
John Preston
e97d014a01 Closed alpha version 1.7.15.7. 2019-08-09 11:51:35 +01:00
John Preston
7ffa348e43 Replace 'Telegram Messenger LLP' with 'Telegram FZ-LLC'. 2019-08-09 11:51:35 +01:00
John Preston
b999d87388 Divide scheme.tl to mtproto.tl and api.tl. 2019-08-09 11:51:35 +01:00
John Preston
fb00d523c3 Add theme names to langpack. 2019-08-09 11:51:35 +01:00
John Preston
d7f7a03eb4 Don't refresh large emoji messages when sent. 2019-08-09 11:51:35 +01:00
John Preston
bc83df9d7f Repaint skin-toned animated emoji. 2019-08-09 11:51:33 +01:00
John Preston
61c1c10ed9 Closed alpha version 1.7.15.6. 2019-08-09 11:50:49 +01:00
John Preston
4bad642190 Remove some more Auth() calls. 2019-08-09 11:50:49 +01:00
John Preston
f48732f813 Extract username from t.me/username searches. 2019-08-09 11:50:49 +01:00
John Preston
10e28913ca Search single messages by message link. 2019-08-09 11:50:49 +01:00
John Preston
230dd29af5 More large emoji layout improvements. 2019-08-09 11:50:49 +01:00
John Preston
169db40e9f Closed alpha version 1.7.15.5. 2019-08-09 11:50:49 +01:00
John Preston
116b483a88 Improve layout of large emoji. 2019-08-09 11:50:49 +01:00
John Preston
57cdef4e6b Improve large emoji outline. 2019-08-09 11:50:49 +01:00
John Preston
1b1b1780db Reuse global emoji large images. 2019-08-09 11:50:49 +01:00
John Preston
e479daca03 Use outlined large emoji. 2019-08-09 11:50:49 +01:00
John Preston
51d350c356 Fix stickers and emoji render. 2019-08-09 11:50:49 +01:00
John Preston
9da4bd671e Closed alpha version 1.7.15.4. 2019-08-09 11:50:49 +01:00
John Preston
d298953653 Add large emoji implementation. 2019-08-09 11:50:49 +01:00
John Preston
1d52ba7a42 Extract sticker-specific HistoryView code. 2019-08-09 11:50:49 +01:00
John Preston
850940116d Move HistoryMedia to HistoryView namespace. 2019-08-09 11:50:49 +01:00
John Preston
fd59147b8a Allow disabling animated emoji. 2019-08-09 11:50:49 +01:00
John Preston
93a52bb66e Move some settings from global to Session. 2019-08-09 11:50:49 +01:00
John Preston
7e4c9f98a6 Track all single-emoji messages. 2019-08-09 11:50:49 +01:00
John Preston
7d2896dd42 Scale animated emoji by server config. 2019-08-09 11:50:49 +01:00
John Preston
f7f797dd78 Initial animated emoji implementation. 2019-08-09 11:50:49 +01:00
John Preston
4b7b1c35e1 Improve storing of played animated stickers. 2019-08-09 11:50:49 +01:00
John Preston
708b1d7ad4 Add 'Loop animated stickers' setting. 2019-08-09 11:50:49 +01:00
John Preston
abf49e1672 Update API scheme. 2019-08-09 11:50:49 +01:00
John Preston
052e4bc508 Fix complex box closing.
~BoxContent should be called when _layers is already in valid state.
2019-08-09 11:50:49 +01:00
John Preston
80fedcbbae Improve slowmode error phrases. 2019-08-09 11:50:49 +01:00
John Preston
787b5f549a Update slowmode intervals. 2019-08-09 11:50:49 +01:00
John Preston
e8a28a57df Fix adding contacts.
Regression was introduced in 06982fdf04.
2019-08-09 11:50:49 +01:00
John Preston
b05ea9fc25 Fix some problems in working with rlottie. 2019-08-09 11:50:49 +01:00
John Preston
8d94cfb61b Fix support mode sending. 2019-08-09 11:50:49 +01:00
John Preston
d8a58991c4 Closed alpha version 1.7.15.3. 2019-07-28 18:01:13 +02:00
John Preston
03cf8b6ac2 Improve transport nonce generation. 2019-07-28 18:01:13 +02:00
John Preston
32e8ae2b9e Fix sticker suggestions by emoji.
Regression was introduced in 27f248645c.
2019-07-28 15:45:46 +02:00
John Preston
27d84befa8 Remove emoji from custom admin ranks. 2019-07-28 15:39:06 +02:00
John Preston
3bf709d459 Add Ui::Text::Italic modifier. 2019-07-27 23:09:15 +02:00
John Preston
2a8b6f05c8 Fix build for GCC. 2019-07-27 17:19:04 +02:00
John Preston
f29b331470 Fix build for Xcode. 2019-07-27 17:19:04 +02:00
John Preston
a8b74d4b6b Closed alpha version 1.7.15.2. 2019-07-27 17:19:04 +02:00
John Preston
415d817034 Show notification without sound for silent message. 2019-07-27 17:19:04 +02:00
John Preston
3f2cc01f48 Allow sending silent messages. 2019-07-27 17:19:04 +02:00
John Preston
f1cd70d8a8 Fix some instructions and build process. 2019-07-27 17:19:04 +02:00
John Preston
bacaf805b5 Pass SessionNavigation to some boxes. 2019-07-25 20:55:11 +02:00
John Preston
137fa0378c Remove some more Auth() calls. 2019-07-24 16:00:30 +02:00
John Preston
ff44094ded Rename AuthSession to Main::Session. 2019-07-24 13:45:24 +02:00
John Preston
06982fdf04 Remove some calls to Auth(). 2019-07-24 13:13:51 +02:00
John Preston
9cf4cf6dca Move MTP::Instance to Main::Account. 2019-07-24 10:46:23 +02:00
John Preston
db2018c765 Fix owner badges in groups. 2019-07-24 08:37:10 +02:00
John Preston
020e62fb7a Closed alpha version 1.7.15.1. 2019-07-24 08:02:58 +02:00
John Preston
2ed5552279 Improve custom badge input phrases. 2019-07-24 08:02:20 +02:00
John Preston
4d92d74de0 Add python extensions to the list of executables. 2019-07-24 08:02:20 +02:00
John Preston
4fd50cfb70 Fix build in Xcode. 2019-07-24 08:02:20 +02:00
John Preston
27f248645c Use correct animated sticker thumbnails size. 2019-07-24 08:02:20 +02:00
John Preston
3f1a2d0b58 Support emoji in custom admin badges. 2019-07-24 08:02:20 +02:00
John Preston
6fe36e6534 Improve custom rank input UI. 2019-07-24 08:02:20 +02:00
John Preston
f36e2981ca Display custom admin ranks in messages. 2019-07-24 08:02:20 +02:00
John Preston
22f210ea8e Support sending albums to slowmode groups. 2019-07-24 08:02:20 +02:00
John Preston
b23bfe8b02 crl: std::terminate instead of exceptions. 2019-07-24 08:02:20 +02:00
John Preston
6aa930d510 Remove exceptions in TL parsing. 2019-07-24 08:02:20 +02:00
John Preston
4a10a88ecf Show error when forwarding to slowmode group. 2019-07-24 08:02:20 +02:00
John Preston
f4754f210c Update full channel on updateChannel. 2019-07-24 08:02:20 +02:00
John Preston
873b77cf40 Use UserData::isBot where applicable. 2019-07-24 08:02:20 +02:00
John Preston
a7d9aa947b Don't allow in-notification reply with slowmode. 2019-07-24 08:02:20 +02:00
John Preston
234b0ffcf0 Disallow accidental failing forwarding. 2019-07-24 08:02:20 +02:00
John Preston
c50ade565a Track sending and failed messages. 2019-07-24 08:02:20 +02:00
John Preston
0005e0a3ce Track local messages, restore on history jump. 2019-07-24 08:02:20 +02:00
John Preston
04bf24288a Show slowmode error messages. 2019-07-24 08:02:20 +02:00
John Preston
01d0479335 Show slowmode button in chat. 2019-07-24 08:02:20 +02:00
John Preston
055c145af5 Fix manage group box after migration. 2019-07-24 08:02:20 +02:00
John Preston
328b090877 Save slowmode settings in groups. 2019-07-24 08:02:20 +02:00
John Preston
1d2c86839b Add slowmode slider to EditPeerPermissionsBox. 2019-07-24 08:02:20 +02:00
John Preston
59574532c6 Add slowmode send message button state. 2019-07-24 08:02:20 +02:00
John Preston
4544a2e331 Update API scheme to layer 104. 2019-07-24 08:02:20 +02:00
John Preston
c0f8ab8da0 Fix build for old Qt version. 2019-07-18 21:52:13 +02:00
John Preston
d02617867c Beta version 1.7.15.
- Bug fixes and other minor improvements.
2019-07-18 18:18:05 +02:00
John Preston
78c99a1583 Revert "Change macOS sign certificate name."
This reverts commit 4757ad6c97.
2019-07-18 18:18:05 +02:00
John Preston
9ec797857d rlottie: Return thread support. 2019-07-18 18:18:05 +02:00
John Preston
b310011dee rlottie: Disable image loader code. 2019-07-18 12:12:02 +02:00
23rd
633084ed9c Fixed position of already shown tabbed panel.
- Fixed #6301.
2019-07-18 12:09:53 +02:00
23rd
ab8889b2fa Fixed position of emoji panel in SendFilesBox when new file is added. 2019-07-18 12:09:53 +02:00
23rd
d563e746ab Added ability to go to bot from "via @bot" header with Ctrl.
- Fixed #2074.
2019-07-18 12:09:53 +02:00
John Preston
7c6ede0908 Save domain in different localstorage key. 2019-07-15 16:00:30 +02:00
John Preston
c22aeb8b40 Fix crash in proxy list. 2019-07-15 16:00:30 +02:00
John Preston
cabe06256b Validate dcOption secrets. 2019-07-15 16:00:30 +02:00
John Preston
2fad4e9956 Closed alpha version 1.7.14.2. 2019-07-15 16:00:30 +02:00
John Preston
c5df4db621 Fix possible race conditions in msgid(). 2019-07-15 16:00:30 +02:00
John Preston
68b1024dd4 Sync local time with HTTP 'Date' header value. 2019-07-15 16:00:29 +02:00
John Preston
c894ce30c4 Fix socket packet reading. 2019-07-15 15:59:20 +02:00
John Preston
33fce38f90 Fix build in Xcode and GCC. 2019-07-15 15:59:20 +02:00
John Preston
e123399f7c Closed alpha version 1.7.14.1. 2019-07-15 15:59:20 +02:00
John Preston
0805c09446 Allow one or two '=' at the end of base64. 2019-07-15 15:59:20 +02:00
John Preston
8135f4b427 Support base64 encoding of the secret. 2019-07-15 15:59:20 +02:00
John Preston
2f0331b2e0 Implement send/receive for the new type. 2019-07-15 15:59:20 +02:00
John Preston
69b6b48738 Add new socket type. 2019-07-15 15:59:20 +02:00
John Preston
3cda267787 Abstract out tcp socket from tcp connection. 2019-07-15 15:59:20 +02:00
John Preston
ffba901620 Add a confirmation when blocking a user. 2019-07-15 15:47:40 +02:00
John Preston
2351865961 Update libtgvoip submodule. 2019-07-15 15:00:15 +02:00
John Preston
7a11d1e31f Fix stickers on Linux 32 bit. 2019-07-15 14:17:22 +02:00
John Preston
f2b3d9714f Fix sticker sending from clipboard. 2019-07-15 14:12:28 +02:00
John Preston
8477956117 Fix emoji button.
Fixes #6264.
2019-07-15 14:12:28 +02:00
John Preston
77b8d56c03 Show 'None' if no users are blocked. 2019-07-15 14:12:27 +02:00
John Preston
90fb9eccd4 Optimize InvokeQueued(). 2019-07-10 19:37:00 +02:00
John Preston
c3c6571835 Add checks for incorrect sticker cache bytes. 2019-07-10 19:36:27 +02:00
John Preston
b9d3ba621e Change some private header includes. 2019-07-10 19:36:27 +02:00
John Preston
4757ad6c97 Change macOS sign certificate name. 2019-07-10 19:36:26 +02:00
John Preston
9c909c8992 Lower the animated sticker requirements.
Fixes #6237.
2019-07-09 14:53:23 +02:00
John Preston
0710dde4d5 Use private Qt color API only in official build.
Fixes #6219.
2019-07-09 13:43:57 +02:00
John Preston
8008d8a3d4 Fix build for Xcode. 2019-07-07 17:11:45 +02:00
John Preston
5c5eead0f5 Version 1.7.14.
- Bug fixes and other minor improvements.
2019-07-07 15:34:35 +02:00
John Preston
696c70e34a Update rlottie, remove asserts in debug build. 2019-07-07 13:14:19 +02:00
John Preston
75fd2d0257 Version 1.7.13.
- Send ultra-lightweight high-quality animated stickers
to express emotion with motion.
- Receive animated stickers instantly on any connection
at just 20-30 KB per sticker.
- Enjoy smooth animations at 60 frames per second.
- Create new animated sets and upload them to @stickers
for everybody to use.
- Try out these sample stickers: t.me/addstickers/hotcherry
- Use strikethrough and underline formatting.
2019-07-06 15:27:31 +02:00
John Preston
4e595d5cef Fix GIFs preview. 2019-07-06 15:24:23 +02:00
23rd
8486ad368e Fixed restrictions for channels.
- Regression was introduced in 567bf60644.
2019-07-06 10:58:10 +03:00
John Preston
ae075e9b90 Beta version 1.7.12: Fix build for old OS X. 2019-07-05 23:13:31 +02:00
John Preston
86d7143d94 Beta version 1.7.12.
- Bug fixes and other minor improvements.
2019-07-05 22:55:09 +02:00
John Preston
4e52da91c7 Fix animation stopping in chat. 2019-07-05 19:47:40 +02:00
John Preston
198de85ce5 Limit to 30 FPS in small stickers. 2019-07-05 19:15:25 +02:00
John Preston
72a9d61b97 Fix glitches on macOS stickers. 2019-07-05 18:13:27 +02:00
John Preston
96a26b44a9 Fix build for Xcode. 2019-07-05 17:08:33 +02:00
John Preston
f19ff1f9b2 Correctly start lottie animations. 2019-07-05 16:43:21 +02:00
John Preston
4bd0683e91 Guard UpdateRequest handling. 2019-07-05 16:29:02 +02:00
John Preston
567bf60644 Use getters in MTP classes. 2019-07-05 16:23:07 +02:00
John Preston
3ca28c0cf9 Fix crash in unsupported message with buttons.
Fixes #6212.
2019-07-05 09:42:51 +02:00
860 changed files with 38171 additions and 18111 deletions

View File

@@ -170,6 +170,7 @@ buildXkbCommon() {
git clone https://github.com/xkbcommon/libxkbcommon.git
cd "$EXTERNAL/libxkbcommon"
git checkout xkbcommon-0.8.4
./autogen.sh --prefix=$XKB_PATH
make $MAKE_ARGS
sudo make install

View File

@@ -92,6 +92,67 @@ index bcd29b6fe1..bcb0672f69 100644
if (QPlatformCursor *platformCursor = q->screen()->handle()->cursor()) {
QCursor *c = QGuiApplication::overrideCursor();
if (!c && hasCursor)
diff --git a/src/gui/painting/qbezier.cpp b/src/gui/painting/qbezier.cpp
index 8e0e76f787..a61bd62834 100644
--- a/src/gui/painting/qbezier.cpp
+++ b/src/gui/painting/qbezier.cpp
@@ -45,6 +45,34 @@ QT_BEGIN_NAMESPACE
//#define QDEBUG_BEZIER
+// Patch: Workaround VS2019 compiler bug, see QTBUG-75280.
+#ifdef Q_OS_WIN
+Q_NEVER_INLINE void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const
+{
+ Q_ASSERT(firstHalf);
+ Q_ASSERT(secondHalf);
+
+ qreal c = (x2 + x3)*.5;
+ firstHalf->x2 = (x1 + x2)*.5;
+ secondHalf->x3 = (x3 + x4)*.5;
+ firstHalf->x1 = x1;
+ secondHalf->x4 = x4;
+ firstHalf->x3 = (firstHalf->x2 + c)*.5;
+ secondHalf->x2 = (secondHalf->x3 + c)*.5;
+ firstHalf->x4 = secondHalf->x1 = (firstHalf->x3 + secondHalf->x2)*.5;
+
+ c = (y2 + y3)/2;
+ firstHalf->y2 = (y1 + y2)*.5;
+ secondHalf->y3 = (y3 + y4)*.5;
+ firstHalf->y1 = y1;
+ secondHalf->y4 = y4;
+ firstHalf->y3 = (firstHalf->y2 + c)*.5;
+ secondHalf->y2 = (secondHalf->y3 + c)*.5;
+ firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2)*.5;
+}
+// Patch: Workaround VS2019 compiler bug, see QTBUG-75280.
+#endif // Q_OS_WIN
+
/*!
\internal
*/
diff --git a/src/gui/painting/qbezier_p.h b/src/gui/painting/qbezier_p.h
index dd1cd94acf..aedc8b6a4b 100644
--- a/src/gui/painting/qbezier_p.h
+++ b/src/gui/painting/qbezier_p.h
@@ -215,6 +215,8 @@ inline QPointF QBezier::secondDerivedAt(qreal t) const
a * y1 + b * y2 + c * y3 + d * y4);
}
+// Patch: Workaround VS2019 compiler bug, see QTBUG-75280.
+#ifndef Q_OS_WIN
inline void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const
{
Q_ASSERT(firstHalf);
@@ -238,6 +240,8 @@ inline void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const
secondHalf->y2 = (secondHalf->y3 + c)*.5;
firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2)*.5;
}
+// Patch: Workaround VS2019 compiler bug, see QTBUG-75280.
+#endif // Q_OS_WIN
inline void QBezier::parameterSplitLeft(qreal t, QBezier *left)
{
diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h
index 918c98997b..4158259743 100644
--- a/src/gui/painting/qpaintengine_p.h
@@ -234,6 +295,40 @@ index c4cb8e65c0..45793e364f 100644
channels[i].socket->close();
delete channels[i].socket;
}
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 94235a48dd..9abd2cc0a1 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -2045,6 +2045,9 @@ void QNetworkReplyHttpImplPrivate::finished()
{
Q_Q(QNetworkReplyHttpImpl);
+ // Patch: Fix crash in Linux (by crashreports).
+ QPointer<QNetworkReplyHttpImpl> guard = q;
+
if (state == Finished || state == Aborted || state == WaitingForSession)
return;
@@ -2075,6 +2078,9 @@ void QNetworkReplyHttpImplPrivate::finished()
#endif
}
+ // Patch: Fix crash in Linux (by crashreports).
+ if (!guard) return;
+
// if we don't know the total size of or we received everything save the cache
if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
completeCacheSave();
@@ -2084,6 +2090,9 @@ void QNetworkReplyHttpImplPrivate::finished()
if (isHttpRedirectResponse() && errorCode == QNetworkReply::NoError)
return;
+ // Patch: Fix crash in Linux (by crashreports).
+ if (!guard) return;
+
state = Finished;
q->setFinished(true);
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index 41834b21ae..8cdf4ab145 100644
--- a/src/network/socket/qnativesocketengine_win.cpp
@@ -253,7 +348,7 @@ index 41834b21ae..8cdf4ab145 100644
setError(QAbstractSocket::NetworkError, AddressNotAvailableErrorString);
socketState = QAbstractSocket::UnconnectedState;
diff --git a/src/platformsupport/cglconvenience/cglconvenience.mm b/src/platformsupport/cglconvenience/cglconvenience.mm
index fb609ae485..7cca45ded4 100644
index fb609ae485..ef1c638d91 100644
--- a/src/platformsupport/cglconvenience/cglconvenience.mm
+++ b/src/platformsupport/cglconvenience/cglconvenience.mm
@@ -128,7 +128,12 @@ void *qcgl_createNSOpenGLPixelFormat(const QSurfaceFormat &format)

View File

@@ -72,8 +72,11 @@ linkCropLimit: 360px;
linkFont: normalFont;
linkOverFont: font(fsize underline);
dateRadius: 6px;
buttonRadius: 3px;
roundRadiusLarge: 6px;
roundRadiusSmall: 3px;
dateRadius: roundRadiusLarge;
buttonRadius: roundRadiusSmall;
setLittleSkip: 9px;

View File

@@ -51,11 +51,6 @@ attentionButtonFgOver: #d14e4e; // default attention button text with mouse over
attentionButtonBgOver: #fcdfde; // default attention button background with mouse over
attentionButtonBgRipple: #f4c3c2; // default attention button ripple effect
outlineButtonBg: windowBg; // default left outlined button background (like shared media links in profiles)
outlineButtonBgOver: lightButtonBgOver; // default left outlined button background with mouse over
outlineButtonOutlineFg: windowBgActive; // default left outlined button left outline border
outlineButtonBgRipple: lightButtonBgRipple; // default left outlined button ripple effect
menuBg: windowBg; // default popup menu background
menuBgOver: windowBgOver; // default popup menu item background with mouse over
menuBgRipple: windowBgRipple; // default popup menu item ripple effect

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -76,6 +76,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_month_day_year" = "{month} {day}, {year}";
"lng_month_year" = "{month} {year}";
"lng_calendar_beginning" = "Beginning";
"lng_box_ok" = "OK";
"lng_box_done" = "Done";
@@ -145,6 +147,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_sure_ban_admin" = "This user is an admin. Are you sure you want to go ahead and restrict them?";
"lng_sure_enable_socks" = "Are you sure you want to enable this proxy?\n\nServer: {server}\nPort: {port}\n\nYou can change your proxy server later in the Settings (Connection Type).";
"lng_sure_enable" = "Enable";
"lng_proxy_invalid" = "The proxy link is invalid.";
"lng_proxy_unsupported" = "Your Telegram Desktop version doesn't support this proxy type or the proxy link is invalid. Please update Telegram Desktop to the latest version.";
"lng_edit_deleted" = "This message was deleted";
"lng_edit_too_long" = "Your message text is too long";
@@ -162,6 +166,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_media_album_error" = "This file cannot be saved as a part of an album.";
"lng_edit_media_invalid_file" = "Sorry, no way to use this file.";
"lng_edit_caption_attach" = "Sorry, you can't attach a new media while you're editing your message.";
"lng_intro_about" = "Welcome to the official Telegram Desktop app.\nIt's fast and secure.";
"lng_start_msgs" = "START MESSAGING";
@@ -295,6 +300,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_notification_reply" = "Reply";
"lng_notification_hide_all" = "Hide all";
"lng_notification_sample" = "This is a sample notification";
"lng_notification_reminder" = "Reminder";
"lng_settings_section_general" = "General";
"lng_settings_change_lang" = "Change language";
@@ -323,6 +329,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_replace_emojis" = "Replace emoji";
"lng_settings_suggest_emoji" = "Suggest emoji replacements";
"lng_settings_suggest_by_emoji" = "Suggest popular stickers by emoji";
"lng_settings_loop_stickers" = "Loop animated stickers";
"lng_settings_large_emoji" = "Large emoji";
"lng_settings_view_emojis" = "View list";
"lng_settings_send_enter" = "Send by Enter";
"lng_settings_send_ctrlenter" = "Send by Ctrl+Enter";
@@ -332,7 +340,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_bg_use_default" = "Use default color theme";
"lng_settings_bg_from_gallery" = "Choose from gallery";
"lng_settings_bg_from_file" = "Choose from file";
"lng_settings_bg_edit_theme" = "Launch theme editor";
"lng_settings_bg_theme_edit" = "Edit theme";
"lng_settings_bg_theme_create" = "Create new theme";
"lng_settings_bg_cloud_themes" = "Custom themes";
"lng_settings_bg_show_all" = "Show all themes";
"lng_settings_bg_tile" = "Tile background";
"lng_settings_adaptive_wide" = "Adaptive layout for wide screens";
@@ -381,6 +392,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_stickers_emoji" = "Stickers and emoji";
"lng_settings_messages" = "Messages";
"lng_settings_themes" = "Themes";
"lng_settings_theme_day" = "Day";
"lng_settings_theme_classic" = "Classic";
"lng_settings_theme_tinted" = "Tinted";
"lng_settings_theme_night" = "Night";
"lng_settings_theme_accent_title" = "Choose accent color";
"lng_settings_data_storage" = "Data and storage";
"lng_settings_information" = "Edit profile";
"lng_settings_passcode_title" = "Local passcode";
@@ -400,6 +416,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_theme_reverting#other" = "Reverting to the old theme in {count} seconds.";
"lng_theme_keep_changes" = "Keep changes";
"lng_theme_revert" = "Revert";
"lng_theme_no_desktop" = "Sorry, this theme doesn't include a version for Telegram Desktop.";
"lng_theme_share" = "Share";
"lng_theme_edit" = "Edit";
"lng_theme_delete" = "Delete";
"lng_theme_delete_sure" = "Are you sure you want to delete this theme?";
"lng_background_header" = "Background preview";
"lng_background_text1" = "Ah, you kids today with techno music! You should enjoy the classics, like Hasselhoff!";
"lng_background_text2" = "I can't even take you seriously right now.";
@@ -560,6 +581,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_proxy_sponsor_warning" = "This proxy may display a sponsored channel in your chat list. This doesn't reveal any of your Telegram traffic.";
"lng_settings_blocked_users" = "Blocked users";
"lng_settings_no_blocked_users" = "None";
"lng_settings_show_sessions" = "Show all sessions";
"lng_settings_export_data" = "Export Telegram data";
"lng_settings_destroy_if" = "If away for...";
@@ -620,6 +642,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_privacy_phone_number_always_title" = "Always share with";
"lng_edit_privacy_phone_number_never_title" = "Never share with";
"lng_edit_privacy_phone_number_find" = "Who can find me by my number";
"lng_edit_privacy_phone_number_contacts" = "Users who add your number to their contacts will see it on Telegram only if they are your contacts.";
"lng_edit_privacy_lastseen_title" = "Last seen privacy";
"lng_edit_privacy_lastseen_header" = "Who can see your last seen time";
"lng_edit_privacy_lastseen_warning" = "Important: you won't be able to see Last Seen times for people with whom you don't share your Last Seen time. Approximate last seen will be shown instead (recently, within a week, within a month).";
@@ -1089,6 +1114,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edited" = "edited";
"lng_edited_date" = "Edited: {date}";
"lng_admin_badge" = "admin";
"lng_owner_badge" = "owner";
"lng_channel_badge" = "channel";
"lng_fast_reply" = "Reply";
"lng_cancel_edit_post_sure" = "Cancel editing?";
@@ -1124,6 +1150,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_media_size_limit" = "Limit by size";
"lng_media_size_up_to" = "up to {size}";
"lng_media_chat_background" = "Chat background";
"lng_media_color_theme" = "Color theme";
"lng_emoji_category1" = "People";
"lng_emoji_category2" = "Nature";
@@ -1229,6 +1256,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_cant_invite_make_admin" = "Make admin";
"lng_send_button" = "Send";
"lng_send_silent_message" = "Send without sound";
"lng_schedule_message" = "Schedule message";
"lng_reminder_message" = "Set a reminder";
"lng_schedule_title" = "Send this message on...";
"lng_remind_title" = "Remind me on...";
"lng_schedule_at" = "at";
"lng_message_ph" = "Write a message...";
"lng_broadcast_ph" = "Broadcast a message...";
"lng_broadcast_silent_ph" = "Silent broadcast...";
@@ -1248,6 +1281,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_saved_short" = "Save";
"lng_saved_forward_here" = "Forward messages here for quick access";
"lng_scheduled_messages" = "Scheduled Messages";
"lng_reminder_messages" = "Reminders";
"lng_scheduled_send_now" = "Send message now?";
"lng_scheduled_send_now_many#one" = "Send {count} message now?";
"lng_scheduled_send_now_many#other" = "Send {count} messages now?";
"lng_archived_name" = "Archived chats";
"lng_archived_add" = "Archive";
"lng_archived_remove" = "Unarchive";
@@ -1375,6 +1414,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_reply_msg" = "Reply";
"lng_context_edit_msg" = "Edit";
"lng_context_forward_msg" = "Forward Message";
"lng_context_send_now_msg" = "Send now";
"lng_context_delete_msg" = "Delete Message";
"lng_context_select_msg" = "Select Message";
"lng_context_report_msg" = "Report Message";
@@ -1384,6 +1424,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_copy_selected" = "Copy Selected Text";
"lng_context_copy_selected_items" = "Copy Selected as Text";
"lng_context_forward_selected" = "Forward Selected";
"lng_context_send_now_selected" = "Send selected now";
"lng_context_delete_selected" = "Delete Selected";
"lng_context_clear_selection" = "Clear Selection";
"lng_send_image_empty" = "Could not send an empty file: {name}";
@@ -1461,6 +1502,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_selected_clear" = "Cancel";
"lng_selected_delete" = "Delete";
"lng_selected_forward" = "Forward";
"lng_selected_send_now" = "Send now";
"lng_selected_cancel_sure_this" = "Cancel uploading?";
"lng_selected_upload_stop" = "Stop";
"lng_selected_delete_sure_this" = "Do you want to delete this message?";
@@ -1525,6 +1567,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_theme_preview_generating" = "Generating color theme preview...";
"lng_theme_preview_invalid" = "Invalid data in this theme file.";
"lng_theme_preview_apply" = "Apply this theme";
"lng_theme_preview_users#one" = "{count} person is using this theme";
"lng_theme_preview_users#other" = "{count} people are using this theme";
"lng_new_authorization" = "{name},\nWe detected a login into your account from a new device on {day}, {date} at {time}\n\nDevice: {device}\nLocation: {location}\n\nIf this wasn't you, you can go to Settings — Show all sessions and terminate that session.\n\nIf you think that somebody logged in to your account against your will, you can enable two-step verification in Settings.\n\nSincerely,\nThe Telegram Team";
@@ -1568,9 +1612,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_theme_editor_save_palette" = "Save palette file";
"lng_theme_editor_choose_name" = "Save theme file";
"lng_theme_editor_error" = "The editor encountered an error :( See 'log.txt' for details.";
"lng_theme_editor_sure_close" = "Are you sure you want to close the editor? Your changes won't be saved.";
"lng_theme_editor_need_auth" = "You need to log in to save your theme.";
"lng_theme_editor_need_unlock" = "You need to unlock Telegram to save your theme.";
"lng_theme_editor_done" = "Theme exported successfully!";
"lng_theme_editor_title" = "Edit color palette";
"lng_theme_editor_export_button" = "Export theme";
"lng_theme_editor_save_button" = "Save theme";
"lng_theme_editor_create_title" = "Create theme";
"lng_theme_editor_attach_title" = "Attach desktop theme";
"lng_theme_editor_create" = "Create";
"lng_theme_editor_name" = "Theme name";
"lng_theme_editor_create_description" = "New theme will be based on your currently selected colors and wallpaper. Alternatively, you can import existing theme or color palette from file.";
"lng_theme_editor_attach_description" = "You can create desktop part of your theme based on your currently selected colors and wallpaper. Alternatively, you can import existing theme or color palette from file.";
"lng_theme_editor_import_existing" = "Import existing theme";
"lng_theme_editor_save_title" = "Save theme";
"lng_theme_editor_link_about" = "Your theme will be updated for all users each time you change it. Anyone can install it using this link.\n\nTheme links must be longer than 5 characters and use a-z, 0-9 and underscores.";
"lng_theme_editor_menu_export" = "Export theme";
"lng_theme_editor_menu_import" = "Import theme";
"lng_theme_editor_menu_show" = "Show palette file";
"lng_payments_not_supported" = "Sorry, Telegram Desktop doesn't support payments yet. Please use one of our mobile apps to do this.";
"lng_payments_receipt_label" = "Receipt";
@@ -1625,6 +1686,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_rights_edit_admin" = "Manage permissions";
"lng_rights_edit_admin_header" = "What can this admin do?";
"lng_rights_edit_admin_rank_name" = "Custom title";
"lng_rights_edit_admin_rank_about" = "A custom title that will be shown to all members instead of '{title}'.";
"lng_rights_about_add_admins_yes" = "This admin will be able to add new admins with the same (or more limited) permissions.";
"lng_rights_about_add_admins_no" = "This admin will not be able to add new admins.";
@@ -1637,6 +1700,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_rights_user_restrictions" = "User restrictions";
"lng_rights_user_restrictions_header" = "What can this member do?";
"lng_rights_default_restrictions_header" = "What can members of this group do?";
"lng_rights_slowmode_header" = "Slow mode";
"lng_rights_slowmode_off" = "Off";
"lng_rights_slowmode_seconds#one" = "{count}s";
"lng_rights_slowmode_seconds#other" = "{count}s";
"lng_rights_slowmode_minutes#one" = "{count}m";
"lng_rights_slowmode_minutes#other" = "{count}m";
"lng_rights_slowmode_hours#one" = "{count}h";
"lng_rights_slowmode_hours#other" = "{count}h";
"lng_rights_slowmode_about" = "Members will be able to send only one message per this interval.";
"lng_rights_slowmode_about_interval" = "Members will be able to send only one message {interval}.";
"lng_rights_slowmode_interval_seconds#one" = "every {count} second";
"lng_rights_slowmode_interval_seconds#other" = "every {count} seconds";
"lng_rights_slowmode_interval_minutes#one" = "every {count} minute";
"lng_rights_slowmode_interval_minutes#other" = "every {count} minutes";
"lng_slowmode_enabled"= "Slow mode is enabled. You can send your next message in {left}.";
"lng_slowmode_no_many" = "Slow mode is enabled. You can't send more than one message at a time.";
"lng_slowmode_too_long" = "Sorry, this text is too long to send as one message.\n\nSlow mode is enabled. You can't send more than one message at a time.";
"lng_rights_channel_info" = "Change channel info";
"lng_rights_channel_post" = "Post messages";
@@ -1697,6 +1778,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_exceptions_list_title" = "Exceptions";
"lng_removed_list_title" = "Removed users";
"lng_admin_log_slow_mode_seconds#one" = "{count} second";
"lng_admin_log_slow_mode_seconds#other" = "{count} seconds";
"lng_admin_log_slow_mode_minutes#one" = "{count} minute";
"lng_admin_log_slow_mode_minutes#other" = "{count} minutes";
"lng_admin_log_title_all" = "All actions";
"lng_admin_log_title_selected" = "Selected actions";
"lng_admin_log_filter" = "Filter";
@@ -1771,6 +1857,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_admin_log_removed_linked_channel" = "{from} removed the linked channel";
"lng_admin_log_changed_location_chat" = "{from} changed the group location to {address}";
"lng_admin_log_removed_location_chat" = "{from} removed the group location";
"lng_admin_log_changed_slow_mode" = "{from} changed slow mode to {duration}";
"lng_admin_log_removed_slow_mode" = "{from} disabled slow mode";
"lng_admin_log_user_with_username" = "{name} ({mention})";
"lng_admin_log_restricted_forever" = "indefinitely";
"lng_admin_log_restricted_until" = "until {date}";

View File

@@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/gui">
<file alias="fonts/OpenSans-Regular.ttf">../fonts/OpenSans-Regular.ttf</file>
<file alias="fonts/OpenSans-Bold.ttf">../fonts/OpenSans-Bold.ttf</file>
<file alias="fonts/OpenSans-Semibold.ttf">../fonts/OpenSans-Semibold.ttf</file>
</qresource>
</RCC>

View File

@@ -1,71 +0,0 @@
<RCC>
<qresource prefix="/export">
<file alias="css/style.css">../export_html/css/style.css</file>
<file alias="images/back.png">../export_html/images/back.png</file>
<file alias="images/back@2x.png">../export_html/images/back@2x.png</file>
<file alias="images/media_call.png">../export_html/images/media_call.png</file>
<file alias="images/media_call@2x.png">../export_html/images/media_call@2x.png</file>
<file alias="images/media_contact.png">../export_html/images/media_contact.png</file>
<file alias="images/media_contact@2x.png">../export_html/images/media_contact@2x.png</file>
<file alias="images/media_file.png">../export_html/images/media_file.png</file>
<file alias="images/media_file@2x.png">../export_html/images/media_file@2x.png</file>
<file alias="images/media_game.png">../export_html/images/media_game.png</file>
<file alias="images/media_game@2x.png">../export_html/images/media_game@2x.png</file>
<file alias="images/media_location.png">../export_html/images/media_location.png</file>
<file alias="images/media_location@2x.png">../export_html/images/media_location@2x.png</file>
<file alias="images/media_music.png">../export_html/images/media_music.png</file>
<file alias="images/media_music@2x.png">../export_html/images/media_music@2x.png</file>
<file alias="images/media_photo.png">../export_html/images/media_photo.png</file>
<file alias="images/media_photo@2x.png">../export_html/images/media_photo@2x.png</file>
<file alias="images/media_shop.png">../export_html/images/media_shop.png</file>
<file alias="images/media_shop@2x.png">../export_html/images/media_shop@2x.png</file>
<file alias="images/media_video.png">../export_html/images/media_video.png</file>
<file alias="images/media_video@2x.png">../export_html/images/media_video@2x.png</file>
<file alias="images/media_voice.png">../export_html/images/media_voice.png</file>
<file alias="images/media_voice@2x.png">../export_html/images/media_voice@2x.png</file>
<file alias="images/section_calls.png">../export_html/images/section_calls.png</file>
<file alias="images/section_calls@2x.png">../export_html/images/section_calls@2x.png</file>
<file alias="images/section_chats.png">../export_html/images/section_chats.png</file>
<file alias="images/section_chats@2x.png">../export_html/images/section_chats@2x.png</file>
<file alias="images/section_contacts.png">../export_html/images/section_contacts.png</file>
<file alias="images/section_contacts@2x.png">../export_html/images/section_contacts@2x.png</file>
<file alias="images/section_frequent.png">../export_html/images/section_frequent.png</file>
<file alias="images/section_frequent@2x.png">../export_html/images/section_frequent@2x.png</file>
<file alias="images/section_other.png">../export_html/images/section_other.png</file>
<file alias="images/section_other@2x.png">../export_html/images/section_other@2x.png</file>
<file alias="images/section_photos.png">../export_html/images/section_photos.png</file>
<file alias="images/section_photos@2x.png">../export_html/images/section_photos@2x.png</file>
<file alias="images/section_sessions.png">../export_html/images/section_sessions.png</file>
<file alias="images/section_sessions@2x.png">../export_html/images/section_sessions@2x.png</file>
<file alias="images/section_web.png">../export_html/images/section_web.png</file>
<file alias="images/section_web@2x.png">../export_html/images/section_web@2x.png</file>
<file alias="js/script.js">../export_html/js/script.js</file>
</qresource>
<qresource prefix="/gui">
<file alias="fonts/OpenSans-Regular.ttf">../fonts/OpenSans-Regular.ttf</file>
<file alias="fonts/OpenSans-Bold.ttf">../fonts/OpenSans-Bold.ttf</file>
<file alias="fonts/OpenSans-Semibold.ttf">../fonts/OpenSans-Semibold.ttf</file>
<file alias="art/bg.jpg">../art/bg.jpg</file>
<file alias="art/bg_initial.jpg">../art/bg_initial.jpg</file>
<file alias="art/logo_256.png">../art/logo_256.png</file>
<file alias="art/logo_256_no_margin.png">../art/logo_256_no_margin.png</file>
<file alias="art/sunrise.jpg">../art/sunrise.jpg</file>
<file alias="day-blue.tdesktop-theme">../day-blue.tdesktop-theme</file>
<file alias="night.tdesktop-theme">../night.tdesktop-theme</file>
<file alias="night-green.tdesktop-theme">../night-green.tdesktop-theme</file>
</qresource>
<qresource prefix="/sounds">
<file alias="msg_incoming.mp3">../sounds/msg_incoming.mp3</file>
<file alias="call_incoming.mp3">../sounds/call_incoming.mp3</file>
<file alias="call_outgoing.mp3">../sounds/call_outgoing.mp3</file>
<file alias="call_busy.mp3">../sounds/call_busy.mp3</file>
<file alias="call_connect.mp3">../sounds/call_connect.mp3</file>
<file alias="call_end.mp3">../sounds/call_end.mp3</file>
</qresource>
<qresource prefix="/qt-project.org">
<file>qmime/freedesktop.org.xml</file>
</qresource>
<qresource prefix="/misc">
<file alias="default_shortcuts-custom.json">../default_shortcuts-custom.json</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,10 @@
<RCC>
<qresource prefix="/sounds">
<file alias="msg_incoming.mp3">../../sounds/msg_incoming.mp3</file>
<file alias="call_busy.mp3">../../sounds/call_busy.mp3</file>
<file alias="call_connect.mp3">../../sounds/call_connect.mp3</file>
<file alias="call_end.mp3">../../sounds/call_end.mp3</file>
<file alias="call_incoming.mp3">../../sounds/call_incoming.mp3</file>
<file alias="call_outgoing.mp3">../../sounds/call_outgoing.mp3</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,60 @@
<RCC>
<qresource prefix="/export">
<file alias="css/style.css">../../export_html/css/style.css</file>
<file alias="images/back.png">../../export_html/images/back.png</file>
<file alias="images/back@2x.png">../../export_html/images/back@2x.png</file>
<file alias="images/media_call.png">../../export_html/images/media_call.png</file>
<file alias="images/media_call@2x.png">../../export_html/images/media_call@2x.png</file>
<file alias="images/media_contact.png">../../export_html/images/media_contact.png</file>
<file alias="images/media_contact@2x.png">../../export_html/images/media_contact@2x.png</file>
<file alias="images/media_file.png">../../export_html/images/media_file.png</file>
<file alias="images/media_file@2x.png">../../export_html/images/media_file@2x.png</file>
<file alias="images/media_game.png">../../export_html/images/media_game.png</file>
<file alias="images/media_game@2x.png">../../export_html/images/media_game@2x.png</file>
<file alias="images/media_location.png">../../export_html/images/media_location.png</file>
<file alias="images/media_location@2x.png">../../export_html/images/media_location@2x.png</file>
<file alias="images/media_music.png">../../export_html/images/media_music.png</file>
<file alias="images/media_music@2x.png">../../export_html/images/media_music@2x.png</file>
<file alias="images/media_photo.png">../../export_html/images/media_photo.png</file>
<file alias="images/media_photo@2x.png">../../export_html/images/media_photo@2x.png</file>
<file alias="images/media_shop.png">../../export_html/images/media_shop.png</file>
<file alias="images/media_shop@2x.png">../../export_html/images/media_shop@2x.png</file>
<file alias="images/media_video.png">../../export_html/images/media_video.png</file>
<file alias="images/media_video@2x.png">../../export_html/images/media_video@2x.png</file>
<file alias="images/media_voice.png">../../export_html/images/media_voice.png</file>
<file alias="images/media_voice@2x.png">../../export_html/images/media_voice@2x.png</file>
<file alias="images/section_calls.png">../../export_html/images/section_calls.png</file>
<file alias="images/section_calls@2x.png">../../export_html/images/section_calls@2x.png</file>
<file alias="images/section_chats.png">../../export_html/images/section_chats.png</file>
<file alias="images/section_chats@2x.png">../../export_html/images/section_chats@2x.png</file>
<file alias="images/section_contacts.png">../../export_html/images/section_contacts.png</file>
<file alias="images/section_contacts@2x.png">../../export_html/images/section_contacts@2x.png</file>
<file alias="images/section_frequent.png">../../export_html/images/section_frequent.png</file>
<file alias="images/section_frequent@2x.png">../../export_html/images/section_frequent@2x.png</file>
<file alias="images/section_other.png">../../export_html/images/section_other.png</file>
<file alias="images/section_other@2x.png">../../export_html/images/section_other@2x.png</file>
<file alias="images/section_photos.png">../../export_html/images/section_photos.png</file>
<file alias="images/section_photos@2x.png">../../export_html/images/section_photos@2x.png</file>
<file alias="images/section_sessions.png">../../export_html/images/section_sessions.png</file>
<file alias="images/section_sessions@2x.png">../../export_html/images/section_sessions@2x.png</file>
<file alias="images/section_web.png">../../export_html/images/section_web.png</file>
<file alias="images/section_web@2x.png">../../export_html/images/section_web@2x.png</file>
<file alias="js/script.js">../../export_html/js/script.js</file>
</qresource>
<qresource prefix="/gui">
<file alias="art/bg.jpg">../../art/bg.jpg</file>
<file alias="art/bg_initial.jpg">../../art/bg_initial.jpg</file>
<file alias="art/logo_256.png">../../art/logo_256.png</file>
<file alias="art/logo_256_no_margin.png">../../art/logo_256_no_margin.png</file>
<file alias="art/sunrise.jpg">../../art/sunrise.jpg</file>
<file alias="day-blue.tdesktop-theme">../../day-blue.tdesktop-theme</file>
<file alias="night.tdesktop-theme">../../night.tdesktop-theme</file>
<file alias="night-green.tdesktop-theme">../../night-green.tdesktop-theme</file>
</qresource>
<qresource prefix="/qt-project.org">
<file>../qmime/freedesktop.org.xml</file>
</qresource>
<qresource prefix="/misc">
<file alias="default_shortcuts-custom.json">../../default_shortcuts-custom.json</file>
</qresource>
</RCC>

View File

@@ -1,10 +0,0 @@
<RCC>
<qresource prefix="/sounds">
<file alias="msg_incoming.mp3">../sounds/msg_incoming.mp3</file>
<file alias="call_busy.mp3">../sounds/call_busy.mp3</file>
<file alias="call_connect.mp3">../sounds/call_connect.mp3</file>
<file alias="call_end.mp3">../sounds/call_end.mp3</file>
<file alias="call_incoming.mp3">../sounds/call_incoming.mp3</file>
<file alias="call_outgoing.mp3">../sounds/call_outgoing.mp3</file>
</qresource>
</RCC>

View File

@@ -1,7 +1,3 @@
// Core types (no need to gen)
//vector#1cb5c415 {t:Type} # [ t ] = Vector t;
///////////////////////////////
/////////////////// Layer cons
///////////////////////////////
@@ -28,115 +24,10 @@
//invokeWithLayer18#1c900537 query:!X = X;
//invokeWithLayer#da9b0d0d layer:int query:!X = X; // after 18 layer
///////////////////////////////
/// Authorization key creation
///////////////////////////////
resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;
p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data;
p_q_inner_data_dc#a9f55f95 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int = P_Q_inner_data;
p_q_inner_data_temp#3c6a84d4 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 expires_in:int = P_Q_inner_data;
p_q_inner_data_temp_dc#56fddf88 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int expires_in:int = P_Q_inner_data;
server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params;
server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;
server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data;
client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data;
dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;
destroy_auth_key_ok#f660e1d4 = DestroyAuthKeyRes;
destroy_auth_key_none#0a9f2259 = DestroyAuthKeyRes;
destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes;
---functions---
req_pq#60469778 nonce:int128 = ResPQ;
req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params;
set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer;
destroy_auth_key#d1435160 = DestroyAuthKeyRes;
///////////////////////////////
////////////// System messages
///////////////////////////////
---types---
msgs_ack#62d6b459 msg_ids:Vector<long> = MsgsAck;
bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification;
bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;
msgs_state_req#da69fb52 msg_ids:Vector<long> = MsgsStateReq;
msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
msgs_all_info#8cc0d131 msg_ids:Vector<long> info:string = MsgsAllInfo;
msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
msg_resend_req#7d861a08 msg_ids:Vector<long> = MsgResendReq;
//rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult; // parsed manually
rpc_error#2144ca19 error_code:int error_message:string = RpcError;
rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
rpc_answer_dropped_running#cd78e586 = RpcDropAnswer;
rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer;
future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt;
future_salts#ae500895 req_msg_id:long now:int salts:vector<future_salt> = FutureSalts;
pong#347773c5 msg_id:long ping_id:long = Pong;
destroy_session_ok#e22045fc session_id:long = DestroySessionRes;
destroy_session_none#62d350c9 session_id:long = DestroySessionRes;
new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession;
//message msg_id:long seqno:int bytes:int body:Object = Message; // parsed manually
//msg_container#73f1f8dc messages:vector<message> = MessageContainer; // parsed manually
//msg_copy#e06046b2 orig_message:Message = MessageCopy; // parsed manually, not used - use msg_container
//gzip_packed#3072cfa1 packed_data:string = Object; // parsed manually
http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait;
//ipPort ipv4:int port:int = IpPort;
//help.configSimple#d997c3c5 date:int expires:int dc_id:int ip_port_list:Vector<ipPort> = help.ConfigSimple;
ipPort#d433ad73 ipv4:int port:int = IpPort;
ipPortSecret#37982646 ipv4:int port:int secret:bytes = IpPort;
accessPointRule#4679b65f phone_prefix_rules:string dc_id:int ips:vector<IpPort> = AccessPointRule;
help.configSimple#5a592a6c date:int expires:int rules:vector<AccessPointRule> = help.ConfigSimple;
---functions---
rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;
get_future_salts#b921bd04 num:int = FutureSalts;
ping#7abe77ec ping_id:long = Pong;
ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;
destroy_session#e7512126 session_id:long = DestroySessionRes;
contest.saveDeveloperInfo#9a5f6e95 vk_id:int name:string phone_number:string age:int city:string = Bool;
///////////////////////////////
///////// Main application API
///////////////////////////////
---types---
boolFalse#bc799737 = Bool;
boolTrue#997275b5 = Bool;
@@ -217,7 +108,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType;
userEmpty#200250ba id:int = User;
user#2e13f4c3 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
user#938458c1 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
userProfilePhoto#ecd75d8c photo_id:long photo_small:FileLocation photo_big:FileLocation dc_id:int = UserProfilePhoto;
@@ -232,11 +123,11 @@ userStatusLastMonth#77ebc742 = UserStatus;
chatEmpty#9ba2d800 id:int = Chat;
chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
chatForbidden#7328bdb id:int title:string = Chat;
channel#4df30834 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#1b7c9db3 flags:# can_set_username:flags.7?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
channelFull#10916653 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation pts:int = ChatFull;
chatFull#1b7c9db3 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
channelFull#2d895c74 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true has_scheduled:flags.19?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int pts:int = ChatFull;
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
@@ -249,7 +140,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#475cdbd5 photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto;
messageEmpty#83e5de54 id:int = Message;
message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message;
message#452c0e65 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message;
messageMediaEmpty#3ded6320 = MessageMedia;
@@ -303,11 +194,10 @@ photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize;
geoPointEmpty#1117dd5f = GeoPoint;
geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint;
auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone;
auth.sentCode#38faab5f flags:# phone_registered:flags.0?true type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int terms_of_service:flags.3?help.TermsOfService = auth.SentCode;
auth.sentCode#5e002502 flags:# type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode;
auth.authorization#cd050916 flags:# tmp_sessions:flags.0?int user:User = auth.Authorization;
auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization;
auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorization;
@@ -332,7 +222,7 @@ inputReportReasonOther#e1746d0a text:string = ReportReason;
inputReportReasonCopyright#9b89f93a = ReportReason;
inputReportReasonGeoIrrelevant#dbd4feed = ReportReason;
userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull;
userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull;
contact#f911c994 user_id:int mutual:Bool = Contact;
@@ -454,6 +344,9 @@ updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBanne
updateFolderPeers#19360dc0 folder_peers:Vector<FolderPeer> pts:int pts_count:int = Update;
updatePeerSettings#6a7e7366 peer:Peer settings:PeerSettings = Update;
updatePeerLocated#b4afcfb0 peers:Vector<PeerLocated> = Update;
updateNewScheduledMessage#39a51dfb message:Message = Update;
updateDeleteScheduledMessages#90866cee peer:Peer messages:Vector<int> = Update;
updateTheme#8216fba3 theme:Theme = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@@ -484,7 +377,7 @@ config#330b4067 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:fla
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
help.appUpdate#1da7158f flags:# popup:flags.0?true id:int version:string text:string entities:Vector<MessageEntity> document:flags.1?Document url:flags.2?string = help.AppUpdate;
help.appUpdate#1da7158f flags:# can_not_skip:flags.0?true id:int version:string text:string entities:Vector<MessageEntity> document:flags.1?Document url:flags.2?string = help.AppUpdate;
help.noAppUpdate#c45a6536 = help.AppUpdate;
help.inviteText#18cb9f78 message:string = help.InviteText;
@@ -550,6 +443,7 @@ inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey;
inputPrivacyKeyForwards#a4dd4c08 = InputPrivacyKey;
inputPrivacyKeyProfilePhoto#5719bacc = InputPrivacyKey;
inputPrivacyKeyPhoneNumber#352dafa = InputPrivacyKey;
inputPrivacyKeyAddedByPhone#d1219bdd = InputPrivacyKey;
privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
privacyKeyChatInvite#500e6dfa = PrivacyKey;
@@ -558,6 +452,7 @@ privacyKeyPhoneP2P#39491cc8 = PrivacyKey;
privacyKeyForwards#69ec56a3 = PrivacyKey;
privacyKeyProfilePhoto#96151fed = PrivacyKey;
privacyKeyPhoneNumber#d19ae46d = PrivacyKey;
privacyKeyAddedByPhone#42ffd42b = PrivacyKey;
inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
@@ -601,7 +496,7 @@ messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMess
webPageEmpty#eb1477e8 id:long = WebPage;
webPagePending#c586da1c id:long date:int = WebPage;
webPage#5f07b4bc flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page = WebPage;
webPage#fa64e172 flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document documents:flags.11?Vector<Document> cached_page:flags.10?Page = WebPage;
webPageNotModified#85849473 = WebPage;
authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization;
@@ -627,6 +522,7 @@ chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:f
inputStickerSetEmpty#ffb62b95 = InputStickerSet;
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
stickerSet#eeb46f27 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize thumb_dc_id:flags.4?int count:int hash:int = StickerSet;
@@ -690,8 +586,8 @@ channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges:
channelParticipant#15ebac1d user_id:int date:int = ChannelParticipant;
channelParticipantSelf#a3289a6d user_id:int inviter_id:int date:int = ChannelParticipant;
channelParticipantCreator#e3e2e1f9 user_id:int = ChannelParticipant;
channelParticipantAdmin#5daa6e23 flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights = ChannelParticipant;
channelParticipantCreator#808d15a4 flags:# user_id:int rank:flags.0?string = ChannelParticipant;
channelParticipantAdmin#ccbebbaf flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights rank:flags.2?string = ChannelParticipant;
channelParticipantBanned#1c0facaf flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChatBannedRights = ChannelParticipant;
channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter;
@@ -892,7 +788,7 @@ payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password
payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = payments.ValidatedRequestedInfo;
payments.paymentResult#4e5f810d updates:Updates = payments.PaymentResult;
payments.paymentVerficationNeeded#6b56b921 url:string = payments.PaymentResult;
payments.paymentVerificationNeeded#d8411139 url:string = payments.PaymentResult;
payments.paymentReceipt#500911e1 flags:# date:int bot_id:int invoice:Invoice provider_id:int info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption currency:string total_amount:long credentials_title:string users:Vector<User> = payments.PaymentReceipt;
@@ -959,6 +855,7 @@ channelAdminLogEventActionDefaultBannedRights#2df5fc0a prev_banned_rights:ChatBa
channelAdminLogEventActionStopPoll#8f079643 message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeLinkedChat#a26f881b prev_value:int new_value:int = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeLocation#e6b76ae prev_value:ChannelLocation new_value:ChannelLocation = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleSlowMode#53909779 prev_value:int new_value:int = ChannelAdminLogEventAction;
channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
@@ -1134,7 +1031,7 @@ inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
account.wallPapersNotModified#1c199183 = account.WallPapers;
account.wallPapers#702b65a9 hash:int wallpapers:Vector<WallPaper> = account.WallPapers;
codeSettings#302f59f3 flags:# allow_flashcall:flags.0?true current_number:flags.1?true app_hash_persistent:flags.2?true app_hash:flags.3?string = CodeSettings;
codeSettings#debebe83 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true = CodeSettings;
wallPaperSettings#a12f40b8 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int intensity:flags.3?int = WallPaperSettings;
@@ -1170,6 +1067,17 @@ channelLocation#209b82db geo_point:GeoPoint address:string = ChannelLocation;
peerLocated#ca461b5d peer:Peer expires:int distance:int = PeerLocated;
restrictionReason#d072acb4 platform:string reason:string text:string = RestrictionReason;
inputTheme#3c5693e9 id:long access_hash:long = InputTheme;
inputThemeSlug#f5890df1 slug:string = InputTheme;
themeDocumentNotModified#483d270c = Theme;
theme#f7d90ce0 flags:# creator:flags.0?true default:flags.1?true id:long access_hash:long slug:string title:string document:flags.2?Document installs_count:int = Theme;
account.themesNotModified#f41eb622 = account.Themes;
account.themes#7f676421 hash:int themes:Vector<Theme> = account.Themes;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -1181,7 +1089,7 @@ invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;
invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization;
auth.signUp#80eee427 phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization;
auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
auth.logOut#5717da40 = Bool;
auth.resetAuthorizations#9fab0d1a = Bool;
@@ -1251,6 +1159,13 @@ account.installWallPaper#feed5769 wallpaper:InputWallPaper settings:WallPaperSet
account.resetWallPapers#bb3b9804 = Bool;
account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings;
account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool;
account.uploadTheme#1c3db333 flags:# file:InputFile thumb:flags.0?InputFile file_name:string mime_type:string = Document;
account.createTheme#2b7ffd7f slug:string title:string document:InputDocument = Theme;
account.updateTheme#3b8ea202 flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument = Theme;
account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool;
account.installTheme#7ae43737 flags:# dark:flags.0?true format:flags.1?string theme:flags.1?InputTheme = Bool;
account.getTheme#8d9d742b format:string theme:InputTheme document_id:long = Theme;
account.getThemes#285946f8 format:string hash:int = account.Themes;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
@@ -1285,9 +1200,9 @@ messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?t
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
messages.sendMessage#fa88427a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
messages.sendMedia#b8d1262b flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer = Updates;
messages.sendMessage#520c3870 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings;
messages.report#bd82b658 peer:InputPeer id:Vector<int> reason:ReportReason = Bool;
@@ -1331,9 +1246,9 @@ messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs;
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates;
messages.sendInlineBotResult#220815b0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string schedule_date:flags.10?int = Updates;
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
messages.editMessage#d116f31e flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer;
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
@@ -1367,7 +1282,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory;
messages.getRecentLocations#bbc45b09 peer:InputPeer limit:int hash:int = messages.Messages;
messages.sendMultiMedia#2095512f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> = Updates;
messages.sendMultiMedia#cc0110cb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int = Updates;
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets;
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
@@ -1389,6 +1304,10 @@ messages.getSearchCounters#732eef00 peer:InputPeer filters:Vector<MessagesFilter
messages.requestUrlAuth#e33f5613 peer:InputPeer msg_id:int button_id:int = UrlAuthResult;
messages.acceptUrlAuth#f729ea98 flags:# write_allowed:flags.0?true peer:InputPeer msg_id:int button_id:int = UrlAuthResult;
messages.hidePeerSettingsBar#4facb138 peer:InputPeer = Bool;
messages.getScheduledHistory#e2c2685b peer:InputPeer hash:int = messages.Messages;
messages.getScheduledMessages#bdbb0464 peer:InputPeer id:Vector<int> = messages.Messages;
messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector<int> = Updates;
messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector<int> = Updates;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@@ -1400,7 +1319,7 @@ photos.deletePhotos#87cf7f2f id:Vector<InputPhoto> = Vector<long>;
photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
upload.getFile#e3a6cfb5 location:InputFileLocation offset:int limit:int = upload.File;
upload.getFile#b15a9afc flags:# precise:flags.0?true location:InputFileLocation offset:int limit:int = upload.File;
upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile;
upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile;
@@ -1438,7 +1357,7 @@ channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channe
channels.getChannels#a7f6bbb id:Vector<InputChannel> = messages.Chats;
channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull;
channels.createChannel#3d5fb10f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string geo_point:flags.2?InputGeoPoint address:flags.2?string = Updates;
channels.editAdmin#70f893ba channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights = Updates;
channels.editAdmin#d33c8902 channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights rank:string = Updates;
channels.editTitle#566decd0 channel:InputChannel title:string = Updates;
channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates;
channels.checkUsername#10e6bd2c channel:InputChannel username:string = Bool;
@@ -1461,6 +1380,7 @@ channels.getGroupsForDiscussion#f5dad378 = messages.Chats;
channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool;
channels.editCreator#8f38cd1f channel:InputChannel user_id:InputUser password:InputCheckPasswordSRP = Updates;
channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint address:string = Bool;
channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates;
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
@@ -1495,4 +1415,4 @@ langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLangua
folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
folders.deleteFolder#1c295881 folder_id:int = Updates;
// LAYER 103
// LAYER 105

View File

@@ -0,0 +1,114 @@
// Core types (no need to gen)
//vector#1cb5c415 {t:Type} # [ t ] = Vector t;
///////////////////////////////
/// Authorization key creation
///////////////////////////////
resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;
p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data;
p_q_inner_data_dc#a9f55f95 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int = P_Q_inner_data;
p_q_inner_data_temp#3c6a84d4 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 expires_in:int = P_Q_inner_data;
p_q_inner_data_temp_dc#56fddf88 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int expires_in:int = P_Q_inner_data;
server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params;
server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;
server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data;
client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data;
dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;
destroy_auth_key_ok#f660e1d4 = DestroyAuthKeyRes;
destroy_auth_key_none#0a9f2259 = DestroyAuthKeyRes;
destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes;
---functions---
req_pq#60469778 nonce:int128 = ResPQ;
req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params;
set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer;
destroy_auth_key#d1435160 = DestroyAuthKeyRes;
///////////////////////////////
////////////// System messages
///////////////////////////////
---types---
msgs_ack#62d6b459 msg_ids:Vector<long> = MsgsAck;
bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification;
bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;
msgs_state_req#da69fb52 msg_ids:Vector<long> = MsgsStateReq;
msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
msgs_all_info#8cc0d131 msg_ids:Vector<long> info:string = MsgsAllInfo;
msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
msg_resend_req#7d861a08 msg_ids:Vector<long> = MsgResendReq;
//rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult; // parsed manually
rpc_error#2144ca19 error_code:int error_message:string = RpcError;
rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
rpc_answer_dropped_running#cd78e586 = RpcDropAnswer;
rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer;
future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt;
future_salts#ae500895 req_msg_id:long now:int salts:vector<future_salt> = FutureSalts;
pong#347773c5 msg_id:long ping_id:long = Pong;
destroy_session_ok#e22045fc session_id:long = DestroySessionRes;
destroy_session_none#62d350c9 session_id:long = DestroySessionRes;
new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession;
//message msg_id:long seqno:int bytes:int body:Object = Message; // parsed manually
//msg_container#73f1f8dc messages:vector<message> = MessageContainer; // parsed manually
//msg_copy#e06046b2 orig_message:Message = MessageCopy; // parsed manually, not used - use msg_container
//gzip_packed#3072cfa1 packed_data:string = Object; // parsed manually
http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait;
//ipPort ipv4:int port:int = IpPort;
//help.configSimple#d997c3c5 date:int expires:int dc_id:int ip_port_list:Vector<ipPort> = help.ConfigSimple;
ipPort#d433ad73 ipv4:int port:int = IpPort;
ipPortSecret#37982646 ipv4:int port:int secret:bytes = IpPort;
accessPointRule#4679b65f phone_prefix_rules:string dc_id:int ips:vector<IpPort> = AccessPointRule;
help.configSimple#5a592a6c date:int expires:int rules:vector<AccessPointRule> = help.ConfigSimple;
tlsClientHello blocks:vector<TlsBlock> = TlsClientHello;
tlsBlockString data:string = TlsBlock;
tlsBlockRandom length:int = TlsBlock;
tlsBlockZero length:int = TlsBlock;
tlsBlockDomain = TlsBlock;
tlsBlockGrease seed:int = TlsBlock;
tlsBlockPublicKey = TlsBlock;
tlsBlockScope entries:Vector<TlsBlock> = TlsBlock;
---functions---
rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;
get_future_salts#b921bd04 num:int = FutureSalts;
ping#7abe77ec ping_id:long = Pong;
ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;
destroy_session#e7512126 session_id:long = DestroySessionRes;

View File

@@ -9,10 +9,10 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="1.7.11.0" />
Version="1.8.11.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
<PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName>
<Description>Telegram Desktop official messenger</Description>
<Logo>Assets\logo\logo.png</Logo>
</Properties>

View File

@@ -1,6 +1,5 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
@@ -34,8 +33,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,7,11,0
PRODUCTVERSION 1,7,11,0
FILEVERSION 1,8,11,0
PRODUCTVERSION 1,8,11,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -50,12 +49,12 @@ BEGIN
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "1.7.11.0"
VALUE "FileVersion", "1.8.11.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2019"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "1.7.11.0"
VALUE "ProductVersion", "1.8.11.0"
END
END
BLOCK "VarFileInfo"
@@ -66,16 +65,3 @@ END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@@ -1,6 +1,5 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource1.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
@@ -25,8 +24,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,7,11,0
PRODUCTVERSION 1,7,11,0
FILEVERSION 1,8,11,0
PRODUCTVERSION 1,8,11,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -41,12 +40,12 @@ BEGIN
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "1.7.11.0"
VALUE "FileVersion", "1.8.11.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2019"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "1.7.11.0"
VALUE "ProductVersion", "1.8.11.0"
END
END
BLOCK "VarFileInfo"
@@ -57,16 +56,3 @@ END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

Binary file not shown.

View File

@@ -1,14 +0,0 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Updater.rc
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@@ -307,7 +307,7 @@ void updateRegistry() {
RegSetValueEx(rkey, L"DisplayVersion", 0, REG_SZ, (const BYTE*)versionStr, ((versionLen / 2) + 1) * sizeof(WCHAR));
wsprintf(nameStr, L"Telegram Desktop version %s", versionStr);
RegSetValueEx(rkey, L"DisplayName", 0, REG_SZ, (const BYTE*)nameStr, (wcslen(nameStr) + 1) * sizeof(WCHAR));
wsprintf(publisherStr, L"Telegram Messenger LLP");
wsprintf(publisherStr, L"Telegram FZ-LLC");
RegSetValueEx(rkey, L"Publisher", 0, REG_SZ, (const BYTE*)publisherStr, (wcslen(publisherStr) + 1) * sizeof(WCHAR));
wsprintf(icongroupStr, L"Telegram Desktop");
RegSetValueEx(rkey, L"Inno Setup: Icon Group", 0, REG_SZ, (const BYTE*)icongroupStr, (wcslen(icongroupStr) + 1) * sizeof(WCHAR));

View File

@@ -0,0 +1,46 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class History;
namespace Api {
struct SendOptions {
TimeId scheduled = 0;
bool silent = false;
bool handleSupportSwitch = false;
bool removeWebPageId = false;
};
enum class SendType {
Normal,
Scheduled,
};
struct SendAction {
explicit SendAction(not_null<History*> history) : history(history) {
}
not_null<History*> history;
SendOptions options;
MsgId replyTo = 0;
bool clearDraft = true;
bool generateLocal = true;
};
struct MessageToSend {
explicit MessageToSend(not_null<History*> history) : action(history) {
}
SendAction action;
TextWithTags textWithTags;
WebPageId webPageId = 0;
};
} // namespace Api

View File

@@ -0,0 +1,42 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Api {
[[nodiscard]] inline uint32 HashInit() {
return 0;
}
inline void HashUpdate(uint32 &already, uint32 value) {
already = (already * 20261) + uint32(value);
}
inline void HashUpdate(uint32 &already, int32 value) {
HashUpdate(already, uint32(value));
}
inline void HashUpdate(uint32 &already, uint64 value) {
HashUpdate(already, uint32(value >> 32));
HashUpdate(already, uint32(value & 0xFFFFFFFFULL));
}
[[nodiscard]] inline int32 HashFinalize(uint32 already) {
return int32(already & 0x7FFFFFFF);
}
template <typename IntRange>
[[nodiscard]] inline int32 CountHash(IntRange &&range) {
auto result = HashInit();
for (const auto value : range) {
HashUpdate(result, value);
}
return HashFinalize(result);
}
} // namespace Api

View File

@@ -0,0 +1,193 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_sending.h"
#include "api/api_text_entities.h"
#include "base/unixtime.h"
#include "data/data_document.h"
#include "data/data_photo.h"
#include "data/data_channel.h" // ChannelData::addsSignature.
#include "data/data_user.h" // UserData::name
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "history/history.h"
#include "history/history_message.h" // NewMessageFlags.
#include "chat_helpers/message_field.h" // ConvertTextTagsToEntities.
#include "ui/text/text_entity.h" // TextWithEntities.
#include "main/main_session.h"
#include "mainwidget.h"
#include "apiwrap.h"
#include "app.h"
namespace Api {
namespace {
template <typename MediaData>
void SendExistingMedia(
Api::MessageToSend &&message,
not_null<MediaData*> media,
Fn<MTPInputMedia()> inputMedia,
Data::FileOrigin origin) {
const auto history = message.action.history;
const auto peer = history->peer;
const auto session = &history->session();
const auto api = &session->api();
message.action.clearDraft = false;
message.action.generateLocal = true;
api->sendAction(message.action);
const auto newId = FullMsgId(
peerToChannel(peer->id),
session->data().nextLocalMessageId());
const auto randomId = rand_value<uint64>();
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
auto clientFlags = NewMessageClientFlags();
auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (message.action.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
}
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
const auto silentPost = message.action.options.silent
|| (channelPost && session->data().notifySilentPosts(peer));
if (channelPost) {
flags |= MTPDmessage::Flag::f_views;
flags |= MTPDmessage::Flag::f_post;
}
if (!channelPost) {
flags |= MTPDmessage::Flag::f_from_id;
} else if (peer->asChannel()->addsSignature()) {
flags |= MTPDmessage::Flag::f_post_author;
}
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
auto messageFromId = channelPost ? 0 : session->userId();
auto messagePostAuthor = channelPost ? session->user()->name : QString();
auto caption = TextWithEntities{
message.textWithTags.text,
TextUtilities::ConvertTextTagsToEntities(message.textWithTags.tags)
};
TextUtilities::Trim(caption);
auto sentEntities = EntitiesToMTP(
caption.entities,
ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) {
sendFlags |= MTPmessages_SendMedia::Flag::f_entities;
}
const auto replyTo = message.action.replyTo;
const auto captionText = caption.text;
if (message.action.options.scheduled) {
flags |= MTPDmessage::Flag::f_from_scheduled;
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
} else {
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
session->data().registerMessageRandomId(randomId, newId);
history->addNewLocalMessage(
newId.msg,
flags,
clientFlags,
0,
replyTo,
HistoryItem::NewMessageDate(message.action.options.scheduled),
messageFromId,
messagePostAuthor,
media,
caption,
MTPReplyMarkup());
auto failHandler = std::make_shared<Fn<void(const RPCError&, QByteArray)>>();
auto performRequest = [=] {
const auto usedFileReference = media->fileReference();
history->sendRequestId = api->request(MTPmessages_SendMedia(
MTP_flags(sendFlags),
peer->input,
MTP_int(replyTo),
inputMedia(),
MTP_string(captionText),
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities,
MTP_int(message.action.options.scheduled)
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result, randomId);
}).fail([=](const RPCError &error) {
(*failHandler)(error, usedFileReference);
}).afterRequest(history->sendRequestId
).send();
};
*failHandler = [=](const RPCError &error, QByteArray usedFileReference) {
if (error.code() == 400
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
api->refreshFileReference(origin, [=](const auto &result) {
if (media->fileReference() != usedFileReference) {
performRequest();
} else {
api->sendMessageFail(error, peer, randomId, newId);
}
});
} else {
api->sendMessageFail(error, peer, randomId, newId);
}
};
performRequest();
if (const auto main = App::main()) {
main->finishForwarding(message.action);
}
}
} // namespace
void SendExistingDocument(
Api::MessageToSend &&message,
not_null<DocumentData*> document) {
const auto inputMedia = [=] {
return MTP_inputMediaDocument(
MTP_flags(0),
document->mtpInput(),
MTPint());
};
SendExistingMedia(
std::move(message),
document,
inputMedia,
document->stickerOrGifOrigin());
if (document->sticker()) {
if (const auto main = App::main()) {
main->incrementSticker(document);
document->session().data().notifyRecentStickersUpdated();
}
}
}
void SendExistingPhoto(
Api::MessageToSend &&message,
not_null<PhotoData*> photo) {
const auto inputMedia = [=] {
return MTP_inputMediaPhoto(
MTP_flags(0),
photo->mtpInput(),
MTPint());
};
SendExistingMedia(
std::move(message),
photo,
inputMedia,
Data::FileOrigin());
}
} // namespace Api

View File

@@ -0,0 +1,25 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class History;
class DocumentData;
namespace Api {
struct MessageToSend;
void SendExistingDocument(
Api::MessageToSend &&message,
not_null<DocumentData*> document);
void SendExistingPhoto(
Api::MessageToSend &&message,
not_null<PhotoData*> photo);
} // namespace Api

View File

@@ -0,0 +1,235 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_single_message_search.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_channel.h"
#include "data/data_search_controller.h"
#include "core/local_url_handlers.h"
#include "history/history_item.h"
#include "base/qthelp_url.h"
#include "apiwrap.h"
namespace Api {
namespace {
using Key = details::SingleMessageSearchKey;
Key ExtractKey(const QString &query) {
const auto trimmed = query.trimmed();
const auto local = Core::TryConvertUrlToLocal(trimmed);
const auto check = local.isEmpty() ? trimmed : local;
const auto parse = [&] {
const auto delimeter = check.indexOf('?');
return (delimeter > 0)
? qthelp::url_parse_params(
check.mid(delimeter + 1),
qthelp::UrlParamNameTransform::ToLower)
: QMap<QString, QString>();
};
if (check.startsWith(qstr("tg://privatepost"), Qt::CaseInsensitive)) {
const auto params = parse();
const auto channel = params.value("channel");
const auto post = params.value("post").toInt();
return (channel.toInt() && post) ? Key{ channel, post } : Key();
} else if (check.startsWith(qstr("tg://resolve"), Qt::CaseInsensitive)) {
const auto params = parse();
const auto domain = params.value("domain");
const auto post = params.value("post").toInt();
return (!domain.isEmpty() && post) ? Key{ domain, post } : Key();
}
return Key();
}
} // namespace
SingleMessageSearch::SingleMessageSearch(not_null<Main::Session*> session)
: _session(session) {
}
SingleMessageSearch::~SingleMessageSearch() {
clear();
}
void SingleMessageSearch::clear() {
_cache.clear();
_requestKey = Key();
_session->api().request(base::take(_requestId)).cancel();
}
std::optional<HistoryItem*> SingleMessageSearch::lookup(
const QString &query,
Fn<void()> ready) {
const auto key = ExtractKey(query);
if (!key) {
return nullptr;
}
const auto i = _cache.find(key);
if (i != end(_cache)) {
return _session->data().message(i->second);
}
if (!(_requestKey == key)) {
_session->api().request(base::take(_requestId)).cancel();
_requestKey = key;
}
return performLookup(ready);
}
std::optional<HistoryItem*> SingleMessageSearch::performLookupByChannel(
not_null<ChannelData*> channel,
Fn<void()> ready) {
Expects(!_requestKey.empty());
const auto postId = _requestKey.postId;
if (const auto item = _session->data().message(channel, postId)) {
_cache.emplace(_requestKey, item->fullId());
return item;
} else if (!ready) {
return nullptr;
}
const auto fail = [=] {
_cache.emplace(_requestKey, FullMsgId());
ready();
};
_requestId = _session->api().request(MTPchannels_GetMessages(
channel->inputChannel,
MTP_vector<MTPInputMessage>(1, MTP_inputMessageID(MTP_int(postId)))
)).done([=](const MTPmessages_Messages &result) {
const auto received = Api::ParseSearchResult(
channel,
Storage::SharedMediaType::kCount,
postId,
Data::LoadDirection::Around,
result);
if (!received.messageIds.empty()
&& received.messageIds.front() == postId) {
_cache.emplace(
_requestKey,
FullMsgId(channel->bareId(), postId));
ready();
} else {
fail();
}
}).fail([=](const RPCError &error) {
fail();
}).send();
return std::nullopt;
}
std::optional<HistoryItem*> SingleMessageSearch::performLookupById(
ChannelId channelId,
Fn<void()> ready) {
Expects(!_requestKey.empty());
if (const auto channel = _session->data().channelLoaded(channelId)) {
return performLookupByChannel(channel, ready);
} else if (!ready) {
return nullptr;
}
const auto fail = [=] {
_cache.emplace(_requestKey, FullMsgId());
ready();
};
_requestId = _session->api().request(MTPchannels_GetChannels(
MTP_vector<MTPInputChannel>(
1,
MTP_inputChannel(MTP_int(channelId), MTP_long(0)))
)).done([=](const MTPmessages_Chats &result) {
result.match([&](const auto &data) {
const auto peer = _session->data().processChats(data.vchats());
if (peer && peer->id == peerFromChannel(channelId)) {
if (performLookupByChannel(peer->asChannel(), ready)) {
ready();
}
} else {
fail();
}
});
}).fail([=](const RPCError &error) {
fail();
}).send();
return std::nullopt;
}
std::optional<HistoryItem*> SingleMessageSearch::performLookupByUsername(
const QString &username,
Fn<void()> ready) {
Expects(!_requestKey.empty());
if (const auto peer = _session->data().peerByUsername(username)) {
if (const auto channel = peer->asChannel()) {
return performLookupByChannel(channel, ready);
}
_cache.emplace(_requestKey, FullMsgId());
return nullptr;
} else if (!ready) {
return nullptr;
}
const auto fail = [=] {
_cache.emplace(_requestKey, FullMsgId());
ready();
};
_requestId = _session->api().request(MTPcontacts_ResolveUsername(
MTP_string(username)
)).done([=](const MTPcontacts_ResolvedPeer &result) {
result.match([&](const MTPDcontacts_resolvedPeer &data) {
_session->data().processUsers(data.vusers());
_session->data().processChats(data.vchats());
const auto peerId = peerFromMTP(data.vpeer());
const auto peer = peerId
? _session->data().peerLoaded(peerId)
: nullptr;
if (const auto channel = peer ? peer->asChannel() : nullptr) {
if (performLookupByChannel(channel, ready)) {
ready();
}
} else {
fail();
}
});
}).fail([=](const RPCError &error) {
fail();
}).send();
return std::nullopt;
}
std::optional<HistoryItem*> SingleMessageSearch::performLookup(
Fn<void()> ready) {
Expects(!_requestKey.empty());
if (!_requestKey.domainOrId[0].isDigit()) {
return performLookupByUsername(_requestKey.domainOrId, ready);
}
const auto channelId = _requestKey.domainOrId.toInt();
return performLookupById(channelId, ready);
}
QString ConvertPeerSearchQuery(const QString &query) {
const auto trimmed = query.trimmed();
const auto local = Core::TryConvertUrlToLocal(trimmed);
const auto check = local.isEmpty() ? trimmed : local;
if (!check.startsWith(qstr("tg://resolve"), Qt::CaseInsensitive)) {
return query;
}
const auto delimeter = check.indexOf('?');
const auto params = (delimeter > 0)
? qthelp::url_parse_params(
check.mid(delimeter + 1),
qthelp::UrlParamNameTransform::ToLower)
: QMap<QString, QString>();
return params.value("domain", query);
}
} // namespace Api

View File

@@ -0,0 +1,76 @@
/*
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 Main {
class Session;
} // namespace Main
namespace Api {
namespace details {
struct SingleMessageSearchKey {
QString domainOrId;
MsgId postId = 0;
[[nodiscard]] bool empty() const {
return domainOrId.isEmpty() || !postId;
}
[[nodiscard]] explicit operator bool() const {
return !empty();
}
[[nodiscard]] bool operator<(const SingleMessageSearchKey &other) const {
return std::tie(domainOrId, postId)
< std::tie(other.domainOrId, other.postId);
}
[[nodiscard]] bool operator==(
const SingleMessageSearchKey &other) const {
return std::tie(domainOrId, postId)
== std::tie(other.domainOrId, other.postId);
}
};
} // namespace details
class SingleMessageSearch {
public:
explicit SingleMessageSearch(not_null<Main::Session*> session);
~SingleMessageSearch();
void clear();
// If 'ready' callback is empty, the result must not be 'nullopt'.
[[nodiscard]] std::optional<HistoryItem*> lookup(
const QString &query,
Fn<void()> ready = nullptr);
private:
using Key = details::SingleMessageSearchKey;
[[nodiscard]] std::optional<HistoryItem*> performLookup(
Fn<void()> ready);
[[nodiscard]] std::optional<HistoryItem*> performLookupById(
ChannelId channelId,
Fn<void()> ready);
[[nodiscard]] std::optional<HistoryItem*> performLookupByUsername(
const QString &username,
Fn<void()> ready);
[[nodiscard]] std::optional<HistoryItem*> performLookupByChannel(
not_null<ChannelData*> channel,
Fn<void()> ready);
const not_null<Main::Session*> _session;
std::map<Key, FullMsgId> _cache;
mtpRequestId _requestId = 0;
Key _requestKey;
};
[[nodiscard]] QString ConvertPeerSearchQuery(const QString &query);
} // namespace Api

View File

@@ -0,0 +1,129 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_text_entities.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_user.h"
namespace Api {
namespace {
using namespace TextUtilities;
} // namespace
EntitiesInText EntitiesFromMTP(const QVector<MTPMessageEntity> &entities) {
auto result = EntitiesInText();
if (!entities.isEmpty()) {
result.reserve(entities.size());
for_const (auto &entity, entities) {
switch (entity.type()) {
case mtpc_messageEntityUrl: { auto &d = entity.c_messageEntityUrl(); result.push_back({ EntityType::Url, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityTextUrl: { auto &d = entity.c_messageEntityTextUrl(); result.push_back({ EntityType::CustomUrl, d.voffset().v, d.vlength().v, Clean(qs(d.vurl())) }); } break;
case mtpc_messageEntityEmail: { auto &d = entity.c_messageEntityEmail(); result.push_back({ EntityType::Email, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityHashtag: { auto &d = entity.c_messageEntityHashtag(); result.push_back({ EntityType::Hashtag, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityCashtag: { auto &d = entity.c_messageEntityCashtag(); result.push_back({ EntityType::Cashtag, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityPhone: break; // Skipping phones.
case mtpc_messageEntityMention: { auto &d = entity.c_messageEntityMention(); result.push_back({ EntityType::Mention, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityMentionName: {
auto &d = entity.c_messageEntityMentionName();
auto data = [&d] {
if (auto user = Auth().data().userLoaded(d.vuser_id().v)) {
return MentionNameDataFromFields({
d.vuser_id().v,
user->accessHash() });
}
return MentionNameDataFromFields(d.vuser_id().v);
};
result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data() });
} break;
case mtpc_inputMessageEntityMentionName: {
auto &d = entity.c_inputMessageEntityMentionName();
auto data = ([&d]() -> QString {
if (d.vuser_id().type() == mtpc_inputUserSelf) {
return MentionNameDataFromFields(Auth().userId());
} else if (d.vuser_id().type() == mtpc_inputUser) {
auto &user = d.vuser_id().c_inputUser();
return MentionNameDataFromFields({ user.vuser_id().v, user.vaccess_hash().v });
}
return QString();
})();
if (!data.isEmpty()) {
result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data });
}
} break;
case mtpc_messageEntityBotCommand: { auto &d = entity.c_messageEntityBotCommand(); result.push_back({ EntityType::BotCommand, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityBold: { auto &d = entity.c_messageEntityBold(); result.push_back({ EntityType::Bold, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityItalic: { auto &d = entity.c_messageEntityItalic(); result.push_back({ EntityType::Italic, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityUnderline: { auto &d = entity.c_messageEntityUnderline(); result.push_back({ EntityType::Underline, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityStrike: { auto &d = entity.c_messageEntityStrike(); result.push_back({ EntityType::StrikeOut, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityCode: { auto &d = entity.c_messageEntityCode(); result.push_back({ EntityType::Code, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back({ EntityType::Pre, d.voffset().v, d.vlength().v, Clean(qs(d.vlanguage())) }); } break;
// #TODO entities
}
}
}
return result;
}
MTPVector<MTPMessageEntity> EntitiesToMTP(
const EntitiesInText &entities,
ConvertOption option) {
auto v = QVector<MTPMessageEntity>();
v.reserve(entities.size());
for_const (auto &entity, entities) {
if (entity.length() <= 0) continue;
if (option == ConvertOption::SkipLocal
&& entity.type() != EntityType::Bold
&& entity.type() != EntityType::Italic
&& entity.type() != EntityType::Underline
&& entity.type() != EntityType::StrikeOut
&& entity.type() != EntityType::Code // #TODO entities
&& entity.type() != EntityType::Pre
&& entity.type() != EntityType::MentionName
&& entity.type() != EntityType::CustomUrl) {
continue;
}
auto offset = MTP_int(entity.offset());
auto length = MTP_int(entity.length());
switch (entity.type()) {
case EntityType::Url: v.push_back(MTP_messageEntityUrl(offset, length)); break;
case EntityType::CustomUrl: v.push_back(MTP_messageEntityTextUrl(offset, length, MTP_string(entity.data()))); break;
case EntityType::Email: v.push_back(MTP_messageEntityEmail(offset, length)); break;
case EntityType::Hashtag: v.push_back(MTP_messageEntityHashtag(offset, length)); break;
case EntityType::Cashtag: v.push_back(MTP_messageEntityCashtag(offset, length)); break;
case EntityType::Mention: v.push_back(MTP_messageEntityMention(offset, length)); break;
case EntityType::MentionName: {
auto inputUser = ([](const QString &data) -> MTPInputUser {
auto fields = MentionNameDataToFields(data);
if (fields.userId == Auth().userId()) {
return MTP_inputUserSelf();
} else if (fields.userId) {
return MTP_inputUser(MTP_int(fields.userId), MTP_long(fields.accessHash));
}
return MTP_inputUserEmpty();
})(entity.data());
if (inputUser.type() != mtpc_inputUserEmpty) {
v.push_back(MTP_inputMessageEntityMentionName(offset, length, inputUser));
}
} break;
case EntityType::BotCommand: v.push_back(MTP_messageEntityBotCommand(offset, length)); break;
case EntityType::Bold: v.push_back(MTP_messageEntityBold(offset, length)); break;
case EntityType::Italic: v.push_back(MTP_messageEntityItalic(offset, length)); break;
case EntityType::Underline: v.push_back(MTP_messageEntityUnderline(offset, length)); break;
case EntityType::StrikeOut: v.push_back(MTP_messageEntityStrike(offset, length)); break;
case EntityType::Code: v.push_back(MTP_messageEntityCode(offset, length)); break; // #TODO entities
case EntityType::Pre: v.push_back(MTP_messageEntityPre(offset, length, MTP_string(entity.data()))); break;
}
}
return MTP_vector<MTPMessageEntity>(std::move(v));
}
} // namespace Api

View File

@@ -0,0 +1,23 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ui/text/text_entity.h"
namespace Api {
EntitiesInText EntitiesFromMTP(const QVector<MTPMessageEntity> &entities);
enum class ConvertOption {
WithLocal,
SkipLocal,
};
MTPVector<MTPMessageEntity> EntitiesToMTP(
const EntitiesInText &entities,
ConvertOption option = ConvertOption::WithLocal);
} // namespace Api

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <rpl/event_stream.h>
#include "api/api_common.h"
#include "base/timer.h"
#include "base/flat_map.h"
#include "base/flat_set.h"
@@ -16,13 +16,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_messages.h"
class TaskQueue;
class AuthSession;
struct MessageGroupId;
struct SendingAlbum;
enum class SendMediaType;
struct FileLoadTo;
class mtpFileLoader;
namespace Main {
class Session;
} // namespace Main
namespace Data {
struct UpdatedFileReferences;
class WallPaper;
@@ -46,23 +49,51 @@ struct CloudPasswordState;
} // namespace Core
namespace Api {
namespace details {
template <typename IntRange>
inline int32 CountHash(IntRange &&range) {
uint32 acc = 0;
for (auto value : range) {
acc += (acc * 20261) + uint32(value);
inline QString ToString(const QString &value) {
return value;
}
inline QString ToString(int32 value) {
return QString::number(value);
}
inline QString ToString(uint64 value) {
return QString::number(value);
}
} // namespace details
template <
typename ...Types,
typename = std::enable_if_t<(sizeof...(Types) > 0)>>
QString RequestKey(Types &&...values) {
const auto strings = { details::ToString(values)... };
if (strings.size() == 1) {
return *strings.begin();
}
return int32(acc & 0x7FFFFFFF);
auto result = QString();
result.reserve(
ranges::accumulate(strings, 0, ranges::plus(), &QString::size));
for (const auto &string : strings) {
result.append(string);
}
return result;
}
} // namespace Api
class ApiWrap : public MTP::Sender, private base::Subscriber {
public:
using SendAction = Api::SendAction;
using MessageToSend = Api::MessageToSend;
struct Privacy {
enum class Key {
PhoneNumber,
AddedByPhone,
LastSeen,
Calls,
Invites,
@@ -99,11 +130,17 @@ public:
bool operator!=(const BlockedUsersSlice &other) const;
};
explicit ApiWrap(not_null<AuthSession*> session);
explicit ApiWrap(not_null<Main::Session*> session);
AuthSession &session() const;
Main::Session &session() const;
void applyUpdates(
const MTPUpdates &updates,
uint64 sentMessageRandomId = 0);
void registerModifyRequest(const QString &key, mtpRequestId requestId);
void clearModifyRequest(const QString &key);
void applyUpdates(const MTPUpdates &updates, uint64 sentMessageRandomId = 0);
void applyNotifySettings(
MTPInputNotifyPeer peer,
const MTPPeerNotifySettings &settings);
@@ -214,10 +251,6 @@ public:
void deleteAllFromUser(
not_null<ChannelData*> channel,
not_null<UserData*> from);
void saveDefaultRestrictions(
not_null<PeerData*> peer,
const MTPChatBannedRights &rights,
Fn<void(bool)> callback = nullptr);
void requestWebPageDelayed(WebPageData *page);
void clearWebPageRequest(WebPageData *page);
@@ -337,30 +370,21 @@ public:
not_null<PeerData*> peer,
const std::vector<not_null<UserData*>> &users);
struct SendOptions {
SendOptions(not_null<History*> history);
not_null<History*> history;
MsgId replyTo = 0;
WebPageId webPageId = 0;
bool clearDraft = false;
bool generateLocal = true;
bool handleSupportSwitch = false;
};
rpl::producer<SendOptions> sendActions() const {
rpl::producer<SendAction> sendActions() const {
return _sendActions.events();
}
void sendAction(const SendOptions &options);
void sendAction(const SendAction &action);
void forwardMessages(
HistoryItemsList &&items,
const SendOptions &options,
const SendAction &action,
FnMut<void()> &&successCallback = nullptr);
void shareContact(
const QString &phone,
const QString &firstName,
const QString &lastName,
const SendOptions &options);
void shareContact(not_null<UserData*> user, const SendOptions &options);
const SendAction &action);
void shareContact(not_null<UserData*> user, const SendAction &action);
void readServerHistory(not_null<History*> history);
void readServerHistoryForce(not_null<History*> history);
//void readFeed( // #feed
@@ -371,66 +395,54 @@ public:
QByteArray result,
VoiceWaveform waveform,
int duration,
const SendOptions &options);
const SendAction &action);
void sendFiles(
Storage::PreparedList &&list,
SendMediaType type,
TextWithTags &&caption,
std::shared_ptr<SendingAlbum> album,
const SendOptions &options);
const SendAction &action);
void sendFile(
const QByteArray &fileContent,
SendMediaType type,
const SendOptions &options);
const SendAction &action);
void editMedia(
Storage::PreparedList &&list,
SendMediaType type,
TextWithTags &&caption,
const SendOptions &options,
const SendAction &action,
MsgId msgIdToEdit);
void sendUploadedPhoto(
FullMsgId localId,
const MTPInputFile &file,
bool silent);
Api::SendOptions options);
void sendUploadedDocument(
FullMsgId localId,
const MTPInputFile &file,
const std::optional<MTPInputFile> &thumb,
bool silent);
Api::SendOptions options);
void editUploadedFile(
FullMsgId localId,
const MTPInputFile &file,
const std::optional<MTPInputFile> &thumb,
bool silent,
Api::SendOptions options,
bool isDocument);
void cancelLocalItem(not_null<HistoryItem*> item);
struct MessageToSend {
MessageToSend(not_null<History*> history);
not_null<History*> history;
TextWithTags textWithTags;
MsgId replyTo = 0;
WebPageId webPageId = 0;
bool clearDraft = true;
bool handleSupportSwitch = false;
};
void sendMessage(MessageToSend &&message);
void sendBotStart(not_null<UserData*> bot, PeerData *chat = nullptr);
void sendInlineResult(
not_null<UserData*> bot,
not_null<InlineBots::Result*> data,
const SendOptions &options);
void sendExistingDocument(
not_null<DocumentData*> document,
Data::FileOrigin origin,
TextWithEntities caption,
const SendOptions &options);
void requestSupportContact(FnMut<void(const MTPUser&)> callback);
const SendAction &action);
void sendMessageFail(
const RPCError &error,
not_null<PeerData*> peer,
uint64 randomId = 0,
FullMsgId itemId = FullMsgId());
void uploadPeerPhoto(not_null<PeerData*> peer, QImage &&image);
void clearPeerPhoto(not_null<PhotoData*> photo);
@@ -459,7 +471,7 @@ public:
void createPoll(
const PollData &data,
const SendOptions &options,
const SendAction &action,
FnMut<void()> done,
FnMut<void(const RPCError &error)> fail);
void sendPollVotes(
@@ -536,10 +548,6 @@ private:
not_null<ChannelData*> channel,
int availableCount,
const QVector<MTPChannelParticipant> &list);
void applyAdminsList(
not_null<ChannelData*> channel,
int availableCount,
const QVector<MTPChannelParticipant> &list);
void resolveWebPages();
void gotWebPages(
ChannelData *channel,
@@ -608,7 +616,7 @@ private:
const QString &firstName,
const QString &lastName,
UserId userId,
const SendOptions &options);
const SendAction &action);
void deleteHistory(
not_null<PeerData*> peer,
@@ -627,7 +635,6 @@ private:
not_null<ChannelData*> channel,
not_null<UserData*> from);
void sendMessageFail(const RPCError &error);
void uploadAlbumMedia(
not_null<HistoryItem*> item,
const MessageGroupId &groupId,
@@ -643,13 +650,13 @@ private:
void sendMedia(
not_null<HistoryItem*> item,
const MTPInputMedia &media,
bool silent);
Api::SendOptions options);
void sendMediaWithRandomId(
not_null<HistoryItem*> item,
const MTPInputMedia &media,
bool silent,
Api::SendOptions options,
uint64 randomId);
FileLoadTo fileLoadTaskOptions(const SendOptions &options) const;
FileLoadTo fileLoadTaskOptions(const SendAction &action) const;
//void readFeeds(); // #feed
@@ -681,7 +688,9 @@ private:
void sendDialogRequests();
not_null<AuthSession*> _session;
not_null<Main::Session*> _session;
base::flat_map<QString, int> _modifyRequests;
MessageDataRequests _messageDataRequests;
QMap<ChannelData*, MessageDataRequests> _channelMessageDataRequests;
@@ -710,10 +719,6 @@ private:
not_null<UserData*>>;
base::flat_map<KickRequest, mtpRequestId> _kickRequests;
base::flat_map<
not_null<PeerData*>,
mtpRequestId> _defaultRestrictionsRequests;
base::flat_set<not_null<ChannelData*>> _selfParticipantRequests;
base::flat_map<
@@ -794,7 +799,7 @@ private:
not_null<Data::Folder*>,
DialogsLoadState> _foldersLoadState;
rpl::event_stream<SendOptions> _sendActions;
rpl::event_stream<SendAction> _sendActions;
struct ReadRequest {
ReadRequest(mtpRequestId requestId, MsgId upTo)

View File

@@ -40,13 +40,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "numbers.h"
#include "observer_peer.h"
#include "auth_session.h"
#include "main/main_session.h"
#include "styles/style_overview.h"
#include "styles/style_mediaview.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_history.h"
#include "styles/style_boxes.h"
#include <QtCore/QBuffer>
#include <QtGui/QFontDatabase>
#ifdef OS_MAC_OLD
#include <libexif/exif-data.h>
#endif // OS_MAC_OLD
@@ -60,8 +63,6 @@ namespace {
*pressedLinkItem = nullptr,
*mousedItem = nullptr;
style::font monofont;
struct CornersPixmaps {
QPixmap p[4];
};
@@ -102,50 +103,6 @@ namespace App {
return result;
}
MainWindow *wnd() {
return (Core::IsAppLaunched() && Core::App().activeWindow())
? Core::App().activeWindow()->widget().get()
: nullptr;
}
MainWidget *main() {
if (auto window = wnd()) {
return window->mainWidget();
}
return nullptr;
}
void addSavedGif(DocumentData *doc) {
auto &saved = Auth().data().savedGifsRef();
int32 index = saved.indexOf(doc);
if (index) {
if (index > 0) saved.remove(index);
saved.push_front(doc);
if (saved.size() > Global::SavedGifsLimit()) saved.pop_back();
Local::writeSavedGifs();
Auth().data().notifySavedGifsUpdated();
Auth().data().setLastSavedGifsUpdate(0);
Auth().api().updateStickers();
}
}
void checkSavedGif(HistoryItem *item) {
if (!item->Has<HistoryMessageForwarded>() && (item->out() || item->history()->peer == Auth().user())) {
if (const auto media = item->media()) {
if (const auto document = media->document()) {
if (document->isGifv()) {
addSavedGif(document);
}
}
}
}
}
QString peerName(const PeerData *peer, bool forDialogs) {
return peer ? ((forDialogs && peer->isUser() && !peer->asUser()->nameOrPhone.isEmpty()) ? peer->asUser()->nameOrPhone : peer->name) : tr::lng_deleted(tr::now);
}
void prepareCorners(RoundCorners index, int32 radius, const QBrush &brush, const style::color *shadow = nullptr, QImage *cors = nullptr) {
Expects(::corners.size() > index);
@@ -179,14 +136,6 @@ namespace App {
}
}
void tryFontFamily(QString &family, const QString &tryFamily) {
if (family.isEmpty()) {
if (!QFontInfo(QFont(tryFamily)).family().trimmed().compare(tryFamily, Qt::CaseInsensitive)) {
family = tryFamily;
}
}
}
void createMaskCorners() {
QImage mask[4];
prepareCorners(SmallMaskCorners, st::buttonRadius, QColor(255, 255, 255), nullptr, mask);
@@ -245,16 +194,6 @@ namespace App {
}
void initMedia() {
if (!::monofont) {
QString family;
tryFontFamily(family, qsl("Consolas"));
tryFontFamily(family, qsl("Liberation Mono"));
tryFontFamily(family, qsl("Menlo"));
tryFontFamily(family, qsl("Courier"));
if (family.isEmpty()) family = QFontDatabase::systemFont(QFontDatabase::FixedFont).family();
::monofont = style::font(st::normalFont->f.pixelSize(), 0, family);
}
createCorners();
using Update = Window::Theme::BackgroundUpdate;
@@ -334,16 +273,11 @@ namespace App {
mousedItem(nullptr);
}
const style::font &monofont() {
return ::monofont;
}
void quit() {
if (quitting()) {
return;
} else if (AuthSession::Exists()
&& Auth().data().exportInProgress()) {
Auth().data().stopExportWithConfirmation([] { App::quit(); });
} else if (Core::IsAppLaunched()
&& Core::App().exportPreventsQuit()) {
return;
}
setLaunchState(QuitRequested);
@@ -493,15 +427,6 @@ namespace App {
rectWithCorners(p, rect, st::msgInBg, MessageInCorners, corners);
}
QImage *cornersMask(ImageRoundRadius radius) {
switch (radius) {
case ImageRoundRadius::Large: return ::cornersMaskLarge;
case ImageRoundRadius::Small:
default: break;
}
return ::cornersMaskSmall;
}
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, const CornersPixmaps &corner, const style::color *shadow, RectParts parts) {
auto cornerWidth = corner.p[0].width() / cIntRetinaFactor();
auto cornerHeight = corner.p[0].height() / cIntRetinaFactor();

View File

@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "data/data_types.h"
#include "ui/rect_part.h"
enum class ImageRoundRadius;
class MainWindow;
@@ -18,10 +19,16 @@ namespace HistoryView {
class Element;
} // namespace HistoryView
namespace Media {
namespace Clip {
class Reader;
} // namespace Clip
} // namespace Media
using HistoryItemsMap = base::flat_set<not_null<HistoryItem*>>;
using GifItems = QHash<Media::Clip::Reader*, HistoryItem*>;
enum RoundCorners {
enum RoundCorners : int {
SmallMaskCorners = 0x00, // for images
LargeMaskCorners,
@@ -60,15 +67,8 @@ enum RoundCorners {
};
namespace App {
MainWindow *wnd();
MainWidget *main();
QString formatPhone(QString phone);
void addSavedGif(DocumentData *doc);
void checkSavedGif(HistoryItem *item);
[[nodiscard]] QString peerName(const PeerData *peer, bool forDialogs = false);
void hoveredItem(HistoryView::Element *item);
HistoryView::Element *hoveredItem();
void pressedItem(HistoryView::Element *item);
@@ -81,8 +81,6 @@ namespace App {
HistoryView::Element *mousedItem();
void clearMousedItems();
const style::font &monofont();
void initMedia();
void deinitMedia();
@@ -106,7 +104,6 @@ namespace App {
void complexOverlayRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners);
void complexLocationRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners);
QImage *cornersMask(ImageRoundRadius radius);
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full);
inline void roundRect(Painter &p, const QRect &rect, style::color bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full) {
return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index, shadow, parts);

View File

@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QLatin1String>
namespace base {
template <typename Type>
@@ -30,6 +32,17 @@ inline bool contains(const Container &container, const T &value) {
return std::find(std::begin(container), end, value) != end;
}
template <typename D, typename T>
inline constexpr D up_cast(T object) {
using DV = std::decay_t<decltype(*D())>;
using TV = std::decay_t<decltype(*T())>;
if constexpr (std::is_base_of_v<DV, TV>) {
return object;
} else {
return nullptr;
}
}
// We need a custom comparator for set<std::unique_ptr<T>>::find to work with pointers.
// thanks to http://stackoverflow.com/questions/18939882/raw-pointer-lookup-for-sets-of-unique-ptrs
template <typename T>
@@ -68,6 +81,23 @@ struct pointer_comparator {
};
inline QString FromUtf8Safe(const char *string, int size = -1) {
if (!string || !size) {
return QString();
} else if (size < 0) {
size = strlen(string);
}
const auto result = QString::fromUtf8(string, size);
const auto back = result.toUtf8();
return (back.size() != size || memcmp(back.constData(), string, size))
? QString::fromLocal8Bit(string, size)
: result;
}
inline QString FromUtf8Safe(const QByteArray &string) {
return FromUtf8Safe(string.constData(), string.size());
}
} // namespace base
template <typename T>

View File

@@ -9,6 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <cstdlib>
// Ensures/Expects.
#include <gsl/gsl_assert>
namespace base {
namespace assertion {

View File

@@ -0,0 +1,18 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/basic_types.h"
// Methods that must be implemented outside lib_base.
namespace base {
void EnterFromEventLoop(FnMut<void()> &&method);
} // namespace

View File

@@ -7,3 +7,4 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "base/base_pch.h"
// Precompiled header helper.

View File

@@ -0,0 +1,61 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "base/crc32hash.h"
namespace base {
namespace {
class Crc32Table {
public:
Crc32Table() {
auto poly = std::uint32_t(0x04c11db7);
for (auto i = 0; i != 256; ++i) {
_data[i] = reflect(i, 8) << 24;
for (auto j = 0; j != 8; ++j) {
_data[i] = (_data[i] << 1) ^ (_data[i] & (1 << 31) ? poly : 0);
}
_data[i] = reflect(_data[i], 32);
}
}
std::uint32_t operator[](int index) const {
return _data[index];
}
private:
std::uint32_t reflect(std::uint32_t val, char ch) {
auto result = std::uint32_t(0);
for (int i = 1; i < (ch + 1); ++i) {
if (val & 1) {
result |= 1 << (ch - i);
}
val >>= 1;
}
return result;
}
std::uint32_t _data[256];
};
} // namespace
std::int32_t crc32(const void *data, int len) {
static const auto kTable = Crc32Table();
const auto buffer = static_cast<const std::uint8_t*>(data);
auto crc = std::uint32_t(0xffffffff);
for (auto i = 0; i != len; ++i) {
crc = (crc >> 8) ^ kTable[(crc & 0xFF) ^ buffer[i]];
}
return static_cast<std::int32_t>(crc ^ 0xffffffff);
}
} // namespace base

View File

@@ -0,0 +1,16 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <cstdint>
namespace base {
std::int32_t crc32(const void *data, int len);
} // namespace base

View File

@@ -0,0 +1,62 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QEvent>
#include <QtCore/QCoreApplication>
#include "base/basic_types.h"
namespace base {
class InvokeQueuedEvent : public QEvent {
public:
static constexpr auto kType = QEvent::Type(60666);
explicit InvokeQueuedEvent(FnMut<void()> &&method)
: QEvent(kType)
, _method(std::move(method)) {
}
void invoke() {
_method();
}
private:
FnMut<void()> _method;
};
} // namespace base
template <typename Lambda>
inline void InvokeQueued(const QObject *context, Lambda &&lambda) {
QCoreApplication::postEvent(
const_cast<QObject*>(context),
new base::InvokeQueuedEvent(std::forward<Lambda>(lambda)));
}
class SingleQueuedInvokation : public QObject {
public:
SingleQueuedInvokation(Fn<void()> callback) : _callback(callback) {
}
void call() {
if (_pending.testAndSetAcquire(0, 1)) {
InvokeQueued(this, [this] {
if (_pending.testAndSetRelease(1, 0)) {
_callback();
}
});
}
}
private:
Fn<void()> _callback;
QAtomicInt _pending = { 0 };
};

View File

@@ -0,0 +1,121 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QPointer>
// Smart pointer for QObject*, has move semantics, destroys object if it doesn't have a parent.
template <typename Object>
class object_ptr {
public:
object_ptr(std::nullptr_t) noexcept {
}
// No default constructor, but constructors with at least
// one argument are simply make functions.
template <typename Parent, typename... Args>
explicit object_ptr(Parent &&parent, Args&&... args)
: _object(new Object(std::forward<Parent>(parent), std::forward<Args>(args)...)) {
}
static object_ptr<Object> fromRaw(Object *value) noexcept {
object_ptr<Object> result = { nullptr };
result._object = value;
return result;
}
Object *release() noexcept {
return static_cast<Object*>(base::take(_object).data());
}
object_ptr(const object_ptr &other) = delete;
object_ptr &operator=(const object_ptr &other) = delete;
object_ptr(object_ptr &&other) noexcept : _object(base::take(other._object)) {
}
object_ptr &operator=(object_ptr &&other) noexcept {
auto temp = std::move(other);
destroy();
std::swap(_object, temp._object);
return *this;
}
template <
typename OtherObject,
typename = std::enable_if_t<
std::is_base_of_v<Object, OtherObject>>>
object_ptr(object_ptr<OtherObject> &&other) noexcept
: _object(base::take(other._object)) {
}
template <
typename OtherObject,
typename = std::enable_if_t<
std::is_base_of_v<Object, OtherObject>>>
object_ptr &operator=(object_ptr<OtherObject> &&other) noexcept {
_object = base::take(other._object);
return *this;
}
object_ptr &operator=(std::nullptr_t) noexcept {
_object = nullptr;
return *this;
}
// So we can pass this pointer to methods like connect().
Object *data() const noexcept {
return static_cast<Object*>(_object.data());
}
operator Object*() const noexcept {
return data();
}
explicit operator bool() const noexcept {
return _object != nullptr;
}
Object *operator->() const noexcept {
return data();
}
Object &operator*() const noexcept {
return *data();
}
// Use that instead "= new Object(parent, ...)"
template <typename Parent, typename... Args>
Object *create(Parent &&parent, Args&&... args) {
destroy();
_object = new Object(
std::forward<Parent>(parent),
std::forward<Args>(args)...);
return data();
}
void destroy() noexcept {
delete base::take(_object);
}
void destroyDelayed() {
if (_object) {
if (auto widget = base::up_cast<QWidget*>(data())) {
widget->hide();
}
base::take(_object)->deleteLater();
}
}
~object_ptr() noexcept {
if (auto pointer = _object) {
if (!pointer->parent()) {
destroy();
}
}
}
private:
template <typename OtherObject>
friend class object_ptr;
QPointer<QObject> _object;
};

View File

@@ -19,6 +19,7 @@ extern "C" {
#include <openssl/modes.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
} // extern "C"
#ifdef small
@@ -56,19 +57,38 @@ private:
class BigNum {
public:
BigNum() : _data(BN_new()) {
BigNum() = default;
BigNum(const BigNum &other)
: _data((other.failed() || other.isZero())
? nullptr
: BN_dup(other.raw()))
, _failed(other._failed) {
}
BigNum(const BigNum &other) : BigNum() {
*this = other;
BigNum(BigNum &&other)
: _data(std::exchange(other._data, nullptr))
, _failed(std::exchange(other._failed, false)) {
}
BigNum &operator=(const BigNum &other) {
if (other.failed() || !BN_copy(raw(), other.raw())) {
if (other.failed()) {
_failed = true;
} else if (other.isZero()) {
clear();
_failed = false;
} else if (!_data) {
_data = BN_dup(other.raw());
_failed = false;
} else {
_failed = !BN_copy(raw(), other.raw());
}
return *this;
}
BigNum &operator=(BigNum &&other) {
std::swap(_data, other._data);
std::swap(_failed, other._failed);
return *this;
}
~BigNum() {
BN_clear_free(raw());
clear();
}
explicit BigNum(unsigned int word) : BigNum() {
@@ -78,64 +98,74 @@ public:
setBytes(bytes);
}
void setWord(unsigned int word) {
if (!BN_set_word(raw(), word)) {
_failed = true;
BigNum &setWord(unsigned int word) {
if (!word) {
clear();
_failed = false;
} else {
_failed = !BN_set_word(raw(), word);
}
return *this;
}
void setBytes(bytes::const_span bytes) {
if (!BN_bin2bn(
BigNum &setBytes(bytes::const_span bytes) {
if (bytes.empty()) {
clear();
_failed = false;
} else {
_failed = !BN_bin2bn(
reinterpret_cast<const unsigned char*>(bytes.data()),
bytes.size(),
raw())) {
_failed = true;
raw());
}
return *this;
}
void setAdd(const BigNum &a, const BigNum &b) {
BigNum &setAdd(const BigNum &a, const BigNum &b) {
if (a.failed() || b.failed()) {
_failed = true;
} else if (!BN_add(raw(), a.raw(), b.raw())) {
_failed = true;
} else {
_failed = !BN_add(raw(), a.raw(), b.raw());
}
return *this;
}
void setSub(const BigNum &a, const BigNum &b) {
BigNum &setSub(const BigNum &a, const BigNum &b) {
if (a.failed() || b.failed()) {
_failed = true;
} else if (!BN_sub(raw(), a.raw(), b.raw())) {
_failed = true;
} else {
_failed = !BN_sub(raw(), a.raw(), b.raw());
}
return *this;
}
void setSubWord(unsigned int word) {
if (failed()) {
return;
} else if (!BN_sub_word(raw(), word)) {
_failed = true;
}
}
void setMul(
BigNum &setMul(
const BigNum &a,
const BigNum &b,
const Context &context = Context()) {
if (a.failed() || b.failed()) {
_failed = true;
} else if (!BN_mul(raw(), a.raw(), b.raw(), context.raw())) {
_failed = true;
} else {
_failed = !BN_mul(raw(), a.raw(), b.raw(), context.raw());
}
return *this;
}
BN_ULONG setDivWord(BN_ULONG word) {
Expects(word != 0);
if (failed()) {
return (BN_ULONG)-1;
}
auto result = BN_div_word(raw(), word);
if (result == (BN_ULONG)-1) {
BigNum &setModAdd(
const BigNum &a,
const BigNum &b,
const BigNum &m,
const Context &context = Context()) {
if (a.failed() || b.failed() || m.failed()) {
_failed = true;
} else if (a.isNegative() || b.isNegative() || m.isNegative()) {
_failed = true;
} else if (!BN_mod_add(raw(), a.raw(), b.raw(), m.raw(), context.raw())) {
_failed = true;
} else if (isNegative()) {
_failed = true;
} else {
_failed = false;
}
return result;
return *this;
}
void setModSub(
BigNum &setModSub(
const BigNum &a,
const BigNum &b,
const BigNum &m,
@@ -148,9 +178,12 @@ public:
_failed = true;
} else if (isNegative()) {
_failed = true;
} else {
_failed = false;
}
return *this;
}
void setModMul(
BigNum &setModMul(
const BigNum &a,
const BigNum &b,
const BigNum &m,
@@ -163,9 +196,29 @@ public:
_failed = true;
} else if (isNegative()) {
_failed = true;
} else {
_failed = false;
}
return *this;
}
void setModExp(
BigNum &setModInverse(
const BigNum &a,
const BigNum &m,
const Context &context = Context()) {
if (a.failed() || m.failed()) {
_failed = true;
} else if (a.isNegative() || m.isNegative()) {
_failed = true;
} else if (!BN_mod_inverse(raw(), a.raw(), m.raw(), context.raw())) {
_failed = true;
} else if (isNegative()) {
_failed = true;
} else {
_failed = false;
}
return *this;
}
BigNum &setModExp(
const BigNum &base,
const BigNum &power,
const BigNum &m,
@@ -178,23 +231,34 @@ public:
_failed = true;
} else if (isNegative()) {
_failed = true;
} else {
_failed = false;
}
return *this;
}
bool isNegative() const {
return failed() ? false : BN_is_negative(raw());
[[nodiscard]] bool isZero() const {
return !failed() && (!_data || BN_is_zero(raw()));
}
bool isPrime(const Context &context = Context()) const {
if (failed()) {
[[nodiscard]] bool isOne() const {
return !failed() && _data && BN_is_one(raw());
}
[[nodiscard]] bool isNegative() const {
return !failed() && _data && BN_is_negative(raw());
}
[[nodiscard]] bool isPrime(const Context &context = Context()) const {
if (failed() || !_data) {
return false;
}
constexpr auto kMillerRabinIterationCount = 30;
auto result = BN_is_prime_ex(
const auto result = BN_is_prime_ex(
raw(),
kMillerRabinIterationCount,
context.raw(),
NULL);
nullptr);
if (result == 1) {
return true;
} else if (result != 0) {
@@ -203,27 +267,42 @@ public:
return false;
}
BN_ULONG modWord(BN_ULONG word) const {
Expects(word != 0);
BigNum &subWord(unsigned int word) {
if (failed()) {
return (BN_ULONG)-1;
return *this;
} else if (!BN_sub_word(raw(), word)) {
_failed = true;
}
return *this;
}
BigNum &divWord(BN_ULONG word, BN_ULONG *mod = nullptr) {
Expects(word != 0);
auto result = BN_mod_word(raw(), word);
const auto result = failed()
? (BN_ULONG)-1
: BN_div_word(raw(), word);
if (result == (BN_ULONG)-1) {
_failed = true;
}
return result;
if (mod) {
*mod = result;
}
return *this;
}
[[nodiscard]] BN_ULONG countModWord(BN_ULONG word) const {
Expects(word != 0);
return failed() ? (BN_ULONG)-1 : BN_mod_word(raw(), word);
}
int bitsSize() const {
[[nodiscard]] int bitsSize() const {
return failed() ? 0 : BN_num_bits(raw());
}
int bytesSize() const {
[[nodiscard]] int bytesSize() const {
return failed() ? 0 : BN_num_bytes(raw());
}
bytes::vector getBytes() const {
[[nodiscard]] bytes::vector getBytes() const {
if (failed()) {
return {};
}
@@ -236,73 +315,84 @@ public:
return result;
}
BIGNUM *raw() {
[[nodiscard]] BIGNUM *raw() {
if (!_data) _data = BN_new();
return _data;
}
const BIGNUM *raw() const {
[[nodiscard]] const BIGNUM *raw() const {
if (!_data) _data = BN_new();
return _data;
}
BIGNUM *takeRaw() {
return base::take(_data);
[[nodiscard]] BIGNUM *takeRaw() {
return _failed
? nullptr
: _data
? std::exchange(_data, nullptr)
: BN_new();
}
bool failed() const {
[[nodiscard]] bool failed() const {
return _failed;
}
static BigNum Add(const BigNum &a, const BigNum &b) {
BigNum result;
result.setAdd(a, b);
return result;
[[nodiscard]] static BigNum Add(const BigNum &a, const BigNum &b) {
return BigNum().setAdd(a, b);
}
static BigNum Sub(const BigNum &a, const BigNum &b) {
BigNum result;
result.setSub(a, b);
return result;
[[nodiscard]] static BigNum Sub(const BigNum &a, const BigNum &b) {
return BigNum().setSub(a, b);
}
static BigNum Mul(
[[nodiscard]] static BigNum Mul(
const BigNum &a,
const BigNum &b,
const Context &context = Context()) {
BigNum result;
result.setMul(a, b, context);
return result;
return BigNum().setMul(a, b, context);
}
static BigNum ModSub(
[[nodiscard]] static BigNum ModAdd(
const BigNum &a,
const BigNum &b,
const BigNum &mod,
const Context &context = Context()) {
BigNum result;
result.setModSub(a, b, mod, context);
return result;
return BigNum().setModAdd(a, b, mod, context);
}
static BigNum ModMul(
[[nodiscard]] static BigNum ModSub(
const BigNum &a,
const BigNum &b,
const BigNum &mod,
const Context &context = Context()) {
BigNum result;
result.setModMul(a, b, mod, context);
return result;
return BigNum().setModSub(a, b, mod, context);
}
static BigNum ModExp(
[[nodiscard]] static BigNum ModMul(
const BigNum &a,
const BigNum &b,
const BigNum &mod,
const Context &context = Context()) {
return BigNum().setModMul(a, b, mod, context);
}
[[nodiscard]] static BigNum ModInverse(
const BigNum &a,
const BigNum &mod,
const Context &context = Context()) {
return BigNum().setModInverse(a, mod, context);
}
[[nodiscard]] static BigNum ModExp(
const BigNum &base,
const BigNum &power,
const BigNum &mod,
const Context &context = Context()) {
BigNum result;
result.setModExp(base, power, mod, context);
return result;
return BigNum().setModExp(base, power, mod, context);
}
static BigNum Failed() {
BigNum result;
[[nodiscard]] static BigNum Failed() {
auto result = BigNum();
result._failed = true;
return result;
}
private:
BIGNUM *_data = nullptr;
void clear() {
BN_clear_free(std::exchange(_data, nullptr));
}
mutable BIGNUM *_data = nullptr;
mutable bool _failed = false;
};
@@ -434,6 +524,17 @@ inline void AddRandomSeed(bytes::const_span data) {
RAND_seed(data.data(), data.size());
}
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
[[nodiscard]] inline T RandomValue() {
unsigned char buffer[sizeof(T)];
if (!RAND_bytes(buffer, sizeof(T))) {
Unexpected("Could not generate random bytes!");
}
auto result = T();
memcpy(&result, buffer, sizeof(T));
return result;
}
inline bytes::vector Pbkdf2Sha512(
bytes::const_span password,
bytes::const_span salt,
@@ -445,6 +546,24 @@ inline bytes::vector Pbkdf2Sha512(
EVP_sha512());
}
inline bytes::vector HmacSha256(
bytes::const_span key,
bytes::const_span data) {
auto result = bytes::vector(kSha256Size);
auto length = (unsigned int)kSha256Size;
HMAC(
EVP_sha256(),
key.data(),
key.size(),
reinterpret_cast<const unsigned char*>(data.data()),
data.size(),
reinterpret_cast<unsigned char*>(result.data()),
&length);
return result;
}
} // namespace openssl
namespace bytes {

View File

@@ -0,0 +1,49 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/algorithm.h"
#include <QtCore/QObject>
namespace base {
class qt_connection final {
public:
qt_connection(QMetaObject::Connection data = {}) : _data(data) {
}
qt_connection(qt_connection &&other) : _data(base::take(other._data)) {
}
qt_connection &operator=(qt_connection &&other) {
reset(base::take(other._data));
return *this;
}
~qt_connection() {
disconnect();
}
void release() {
_data = QMetaObject::Connection();
}
void reset(QMetaObject::Connection data = {}) {
disconnect();
_data = data;
}
private:
void disconnect() {
if (_data) {
QObject::disconnect(base::take(_data));
}
}
QMetaObject::Connection _data;
};
} // namespace base

View File

@@ -7,9 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "core/sandbox.h"
#include "base/base_integration.h"
namespace Core {
namespace base {
// This method allows to create an rpl::producer from a Qt object
// and a signal with none or one reported value.
@@ -20,34 +20,34 @@ namespace Core {
// This means that all postponeCall's will be invoked right after
// the value processing by the current consumer finishes.
template <typename Object, typename Signal>
auto QtSignalProducer(Object *object, Signal signal);
auto qt_signal_producer(Object *object, Signal signal);
namespace details {
template <typename Signal>
struct QtSignalArgument;
struct qt_signal_argument;
template <typename Class, typename Return, typename Value>
struct QtSignalArgument<Return(Class::*)(Value)> {
struct qt_signal_argument<Return(Class::*)(Value)> {
using type = Value;
};
template <typename Class, typename Return>
struct QtSignalArgument<Return(Class::*)()> {
struct qt_signal_argument<Return(Class::*)()> {
using type = void;
};
} // namespace details
template <typename Object, typename Signal>
auto QtSignalProducer(Object *object, Signal signal) {
using Value = typename details::QtSignalArgument<Signal>::type;
auto qt_signal_producer(Object *object, Signal signal) {
using Value = typename details::qt_signal_argument<Signal>::type;
static constexpr auto NoArgument = std::is_same_v<Value, void>;
using Produced = std::conditional_t<
NoArgument,
rpl::empty_value,
std::remove_const_t<std::decay_t<Value>>>;
const auto guarded = make_weak(object);
const auto guarded = QPointer<Object>(object);
return rpl::make_producer<Produced>([=](auto consumer) {
if (!guarded) {
return rpl::lifetime();
@@ -59,7 +59,7 @@ auto QtSignalProducer(Object *object, Signal signal) {
signal,
listener,
std::forward<decltype(handler)>(handler));
const auto weak = make_weak(listener);
const auto weak = QPointer<QObject>(listener);
return rpl::lifetime([=] {
if (weak) {
delete weak;
@@ -67,7 +67,7 @@ auto QtSignalProducer(Object *object, Signal signal) {
});
};
auto put = [=](const Produced &value) {
Sandbox::Instance().customEnterFromEventLoop([&] {
EnterFromEventLoop([&] {
consumer.put_next_copy(value);
});
};
@@ -79,4 +79,4 @@ auto QtSignalProducer(Object *object, Signal signal) {
});
}
} // namespace Core
} // namespace base

View File

@@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QUrl>
#include <QtCore/QString>
#include <QtCore/QRegularExpression>
namespace qthelp {
const QRegularExpression &RegExpDomain();

View File

@@ -108,8 +108,8 @@ void Timer::timerEvent(QTimerEvent *e) {
cancel();
}
if (_callback) {
_callback();
if (const auto onstack = _callback) {
onstack();
}
}

View File

@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QPointer>
namespace base {
template <typename T>

View File

@@ -0,0 +1,174 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "base/unixtime.h"
#include <QDateTime>
#include <QReadWriteLock>
#ifdef Q_OS_WIN
#include <windows.h>
#elif defined Q_OS_MAC
#include <mach/mach_time.h>
#else
#include <time.h>
#endif
namespace base {
namespace unixtime {
namespace {
std::atomic<bool> ValueUpdated/* = false*/;
std::atomic<TimeId> ValueShift/* = 0*/;
std::atomic<bool> HttpValueValid/* = false*/;
std::atomic<TimeId> HttpValueShift/* = 0*/;
class MsgIdManager {
public:
MsgIdManager();
void update();
[[nodiscard]] uint64 next();
private:
void initialize();
QReadWriteLock _lock;
uint64 _startId = 0;
std::atomic<uint32> _incrementedPart = 0;
uint64 _startCounter = 0;
uint64 _randomPart = 0;
float64 _multiplier = 0.;
};
MsgIdManager GlobalMsgIdManager;
[[nodiscard]] float64 GetMultiplier() {
// 0xFFFF0000 instead of 0x100000000 to make msgId grow slightly slower,
// than unixtime and we had time to reconfigure.
#ifdef Q_OS_WIN
LARGE_INTEGER li;
QueryPerformanceFrequency(&li);
return float64(0xFFFF0000L) / float64(li.QuadPart);
#elif defined Q_OS_MAC // Q_OS_WIN
mach_timebase_info_data_t tb = { 0, 0 };
mach_timebase_info(&tb);
const auto frequency = (float64(tb.numer) / tb.denom) / 1000000.;
return frequency * (float64(0xFFFF0000L) / 1000.);
#else // Q_OS_MAC || Q_OS_WIN
return float64(0xFFFF0000L) / 1000000000.;
#endif // Q_OS_MAC || Q_OS_WIN
}
[[nodiscard]] uint64 GetCounter() {
#ifdef Q_OS_WIN
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
return li.QuadPart;
#elif defined Q_OS_MAC // Q_OS_WIN
return mach_absolute_time();
#else // Q_OS_MAC || Q_OS_WIN
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec);
#endif // Q_OS_MAC || Q_OS_WIN
}
MsgIdManager::MsgIdManager() {
auto generator = std::mt19937(std::random_device()());
auto distribution = std::uniform_int_distribution<uint32>();
_randomPart = distribution(generator);
_multiplier = GetMultiplier();
initialize();
srand(uint32(_startCounter & 0xFFFFFFFFUL));
}
void MsgIdManager::update() {
QWriteLocker lock(&_lock);
initialize();
}
void MsgIdManager::initialize() {
_startCounter = GetCounter();
_startId = ((uint64(uint32(now()))) << 32) | _randomPart;
}
uint64 MsgIdManager::next() {
const auto counter = GetCounter();
QReadLocker lock(&_lock);
const auto delta = (counter - _startCounter);
const auto result = _startId + (uint64)floor(delta * _multiplier);
lock.unlock();
return (result & ~0x03L) + (_incrementedPart += 4);
}
TimeId local() {
return (TimeId)time(nullptr);
}
} // namespace
TimeId now() {
return local() + ValueShift.load();
}
void update(TimeId now, bool force) {
if (force) {
ValueUpdated = true;
} else {
auto expected = false;
if (!ValueUpdated.compare_exchange_strong(expected, true)) {
return;
}
}
const auto shift = now + 1 - local();
ValueShift = shift;
HttpValueShift = 0;
HttpValueValid = false;
GlobalMsgIdManager.update();
}
QDateTime parse(TimeId value) {
return (value > 0)
? QDateTime::fromTime_t(value - ValueShift)
: QDateTime();
}
TimeId serialize(const QDateTime &date) {
return date.isNull() ? TimeId(0) : date.toTime_t() + ValueShift;
}
bool http_valid() {
return HttpValueValid;
}
TimeId http_now() {
return now() + HttpValueShift;
}
void http_update(TimeId now) {
HttpValueShift = now - base::unixtime::now();
HttpValueValid = true;
}
void http_invalidate() {
HttpValueValid = false;
}
uint64 mtproto_msg_id() {
return GlobalMsgIdManager.next();
}
} // namespace unixtime
} // namespace base

View File

@@ -0,0 +1,33 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/basic_types.h"
class QDateTime;
namespace base {
namespace unixtime {
// All functions are thread-safe.
[[nodiscard]] TimeId now();
void update(TimeId now, bool force = false);
[[nodiscard]] QDateTime parse(TimeId value);
[[nodiscard]] TimeId serialize(const QDateTime &date);
[[nodiscard]] bool http_valid();
[[nodiscard]] TimeId http_now();
void http_update(TimeId now);
void http_invalidate();
[[nodiscard]] uint64 mtproto_msg_id();
} // namespace unixtime
} // namespace base

View File

@@ -20,6 +20,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/update_checker.h"
#include "styles/style_boxes.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
#include <QtGui/QDesktopServices>
namespace {
rpl::producer<TextWithEntities> Text1() {
@@ -100,7 +104,7 @@ void AboutBox::showVersionHistory() {
}
url = url.arg(qsl("talpha%1_%2").arg(cRealAlphaVersion()).arg(Core::countAlphaVersionSignature(cRealAlphaVersion())));
QApplication::clipboard()->setText(url);
QGuiApplication::clipboard()->setText(url);
Ui::show(Box<InformBox>("The link to the current private alpha version of Telegram Desktop was copied to the clipboard."));
} else {

View File

@@ -11,14 +11,33 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_profile.h"
#include "storage/localstorage.h"
#include "lang/lang_keys.h"
#include "ui/effects/radial_animation.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/shadow.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/text/text_utilities.h"
#include "ui/painter.h"
#include "base/timer.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "app.h"
struct AbstractBox::LoadingProgress {
LoadingProgress(
Fn<void()> &&callback,
const style::InfiniteRadialAnimation &st);
Ui::InfiniteRadialAnimation animation;
base::Timer removeTimer;
};
AbstractBox::LoadingProgress::LoadingProgress(
Fn<void()> &&callback,
const style::InfiniteRadialAnimation &st)
: animation(std::move(callback), st) {
}
void BoxContent::setTitle(rpl::producer<QString> title) {
getDelegate()->setTitle(std::move(title) | Ui::Text::ToWithEntities());
@@ -117,7 +136,7 @@ void BoxContent::onDraggingScrollDelta(int delta) {
}
void BoxContent::onDraggingScrollTimer() {
auto delta = (_draggingScrollDelta > 0) ? qMin(_draggingScrollDelta * 3 / 20 + 1, int32(MaxScrollSpeed)) : qMax(_draggingScrollDelta * 3 / 20 - 1, -int32(MaxScrollSpeed));
auto delta = (_draggingScrollDelta > 0) ? qMin(_draggingScrollDelta * 3 / 20 + 1, int32(Ui::kMaxScrollSpeed)) : qMax(_draggingScrollDelta * 3 / 20 - 1, -int32(Ui::kMaxScrollSpeed));
_scroll->scrollToY(_scroll->scrollTop() + delta);
}
@@ -258,7 +277,6 @@ AbstractBox::AbstractBox(
: LayerWidget(layer)
, _layer(layer)
, _content(std::move(content)) {
subscribe(Lang::Current().updated(), [=] { refreshLang(); });
_content->setParent(this);
_content->setDelegate(this);
@@ -269,6 +287,8 @@ AbstractBox::AbstractBox(
}, lifetime());
}
AbstractBox::~AbstractBox() = default;
void AbstractBox::setLayerType(bool layerType) {
_layerType = layerType;
updateTitlePosition();
@@ -279,15 +299,35 @@ int AbstractBox::titleHeight() const {
}
int AbstractBox::buttonsHeight() const {
auto padding = _layerType ? st::boxLayerButtonPadding : st::boxButtonPadding;
const auto padding = _layerType
? st::boxLayerButtonPadding
: st::boxButtonPadding;
return padding.top() + st::defaultBoxButton.height + padding.bottom();
}
int AbstractBox::buttonsTop() const {
auto padding = _layerType ? st::boxLayerButtonPadding : st::boxButtonPadding;
const auto padding = _layerType
? st::boxLayerButtonPadding
: st::boxButtonPadding;
return height() - padding.bottom() - st::defaultBoxButton.height;
}
QRect AbstractBox::loadingRect() const {
const auto padding = _layerType
? st::boxLayerButtonPadding
: st::boxButtonPadding;
const auto size = st::boxLoadingSize;
const auto skipx = _layerType
? st::boxLayerTitlePosition.x()
: st::boxTitlePosition.x();
const auto skipy = (st::defaultBoxButton.height - size) / 2;
return QRect(
skipx,
height() - padding.bottom() - skipy - size,
size,
size);
}
void AbstractBox::paintEvent(QPaintEvent *e) {
Painter p(this);
auto clip = e->rect();
@@ -309,6 +349,14 @@ void AbstractBox::paintEvent(QPaintEvent *e) {
&& clip.intersects(QRect(0, 0, width(), titleHeight()))) {
paintAdditionalTitle(p);
}
if (_loadingProgress) {
const auto rect = loadingRect();
_loadingProgress->animation.draw(
p,
rect.topLeft(),
rect.size(),
width());
}
}
void AbstractBox::paintAdditionalTitle(Painter &p) {
@@ -350,10 +398,6 @@ bool AbstractBox::closeByOutsideClick() const {
return _closeByOutsideClick;
}
void AbstractBox::refreshLang() {
InvokeQueued(this, [this] { updateButtonsPositions(); });
}
bool AbstractBox::hasTitle() const {
return (_title != nullptr) || !_additionalTitle.current().isEmpty();
}
@@ -416,7 +460,10 @@ QPointer<Ui::RoundButton> AbstractBox::addButton(
auto result = QPointer<Ui::RoundButton>(_buttons.back());
result->setClickedCallback(std::move(clickCallback));
result->show();
updateButtonsPositions();
result->widthValue(
) | rpl::start_with_next([=] {
updateButtonsPositions();
}, result->lifetime());
return result;
}
@@ -428,7 +475,10 @@ QPointer<Ui::RoundButton> AbstractBox::addLeftButton(
auto result = QPointer<Ui::RoundButton>(_leftButton);
result->setClickedCallback(std::move(clickCallback));
result->show();
updateButtonsPositions();
result->widthValue(
) | rpl::start_with_next([=] {
updateButtonsPositions();
}, result->lifetime());
return result;
}
@@ -441,6 +491,36 @@ QPointer<Ui::IconButton> AbstractBox::addTopButton(const style::IconButton &st,
return result;
}
void AbstractBox::showLoading(bool show) {
const auto &st = st::boxLoadingAnimation;
if (!show) {
if (_loadingProgress && !_loadingProgress->removeTimer.isActive()) {
_loadingProgress->removeTimer.callOnce(
st.sineDuration + st.sinePeriod);
_loadingProgress->animation.stop();
}
return;
}
if (!_loadingProgress) {
const auto callback = [=] {
if (!anim::Disabled()) {
const auto t = st::boxLoadingAnimation.thickness;
update(loadingRect().marginsAdded({ t, t, t, t }));
}
};
_loadingProgress = std::make_unique<LoadingProgress>(
callback,
st::boxLoadingAnimation);
_loadingProgress->removeTimer.setCallback([=] {
_loadingProgress = nullptr;
});
} else {
_loadingProgress->removeTimer.cancel();
}
_loadingProgress->animation.start();
}
void AbstractBox::setDimensions(int newWidth, int maxHeight, bool forceCenterPosition) {
_maxContentHeight = maxHeight;
@@ -519,3 +599,39 @@ void BoxContentDivider::paintEvent(QPaintEvent *e) {
auto dividerFillBottom = myrtlrect(0, height() - st::profileDividerBottom.height(), width(), st::profileDividerBottom.height());
st::profileDividerBottom.fill(p, dividerFillBottom);
}
namespace Ui {
namespace internal {
void showBox(
object_ptr<BoxContent> content,
LayerOptions options,
anim::type animated) {
if (auto w = App::wnd()) {
w->ui_showBox(std::move(content), options, animated);
}
}
} // namespace internal
void hideLayer(anim::type animated) {
if (auto w = App::wnd()) {
w->ui_showBox(
{ nullptr },
LayerOption::CloseOther,
animated);
}
}
void hideSettingsAndLayer(anim::type animated) {
if (auto w = App::wnd()) {
w->ui_hideSettingsAndLayer(animated);
}
}
bool isLayerShown() {
if (auto w = App::wnd()) return w->ui_isLayerShown();
return false;
}
} // namespace Ui

View File

@@ -9,8 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/layer_widget.h"
#include "base/unique_qptr.h"
#include "base/flags.h"
#include "ui/effects/animation_value.h"
#include "ui/text/text_entity.h"
#include "ui/rp_widget.h"
class Painter;
namespace style {
struct RoundButton;
struct IconButton;
@@ -46,6 +51,7 @@ public:
virtual QPointer<Ui::IconButton> addTopButton(
const style::IconButton &st,
Fn<void()> clickCallback) = 0;
virtual void showLoading(bool show) = 0;
virtual void updateButtonsPositions() = 0;
virtual void showBox(
@@ -74,7 +80,7 @@ public:
};
class BoxContent : public Ui::RpWidget, protected base::Subscriber {
class BoxContent : public Ui::RpWidget {
Q_OBJECT
public:
@@ -133,6 +139,9 @@ public:
std::move(clickCallback),
st);
}
void showLoading(bool show) {
getDelegate()->showLoading(show);
}
void updateButtonsGeometry() {
getDelegate()->updateButtonsPositions();
}
@@ -219,7 +228,8 @@ protected:
template <typename Widget>
object_ptr<Widget> takeInnerWidget() {
return static_object_cast<Widget>(doTakeInnerWidget());
return object_ptr<Widget>::fromRaw(
static_cast<Widget*>(doTakeInnerWidget().release()));
}
void setInnerVisible(bool scrollAreaVisible);
@@ -263,14 +273,12 @@ private:
};
class AbstractBox
: public Window::LayerWidget
, public BoxContentDelegate
, protected base::Subscriber {
class AbstractBox : public Window::LayerWidget, public BoxContentDelegate {
public:
AbstractBox(
not_null<Window::LayerStackWidget*> layer,
object_ptr<BoxContent> content);
~AbstractBox();
void parentResized() override;
@@ -294,6 +302,7 @@ public:
QPointer<Ui::IconButton> addTopButton(
const style::IconButton &st,
Fn<void()> clickCallback) override;
void showLoading(bool show) override;
void updateButtonsPositions() override;
QPointer<QWidget> outerContainer() override;
@@ -332,17 +341,19 @@ protected:
}
private:
struct LoadingProgress;
void paintAdditionalTitle(Painter &p);
void updateTitlePosition();
void refreshLang();
bool hasTitle() const;
int titleHeight() const;
int buttonsHeight() const;
int buttonsTop() const;
int contentTop() const;
int countFullHeight() const;
int countRealHeight() const;
[[nodiscard]] bool hasTitle() const;
[[nodiscard]] int titleHeight() const;
[[nodiscard]] int buttonsHeight() const;
[[nodiscard]] int buttonsTop() const;
[[nodiscard]] int contentTop() const;
[[nodiscard]] int countFullHeight() const;
[[nodiscard]] int countRealHeight() const;
[[nodiscard]] QRect loadingRect() const;
void updateSize();
not_null<Window::LayerStackWidget*> _layer;
@@ -363,6 +374,7 @@ private:
std::vector<object_ptr<Ui::RoundButton>> _buttons;
object_ptr<Ui::RoundButton> _leftButton = { nullptr };
base::unique_qptr<Ui::IconButton> _topButton = { nullptr };
std::unique_ptr<LoadingProgress> _loadingProgress;
};
@@ -417,3 +429,30 @@ private:
QPointer<BoxContent> _value;
};
// Legacy global method.
namespace Ui {
namespace internal {
void showBox(
object_ptr<BoxContent> content,
LayerOptions options,
anim::type animated);
} // namespace internal
template <typename BoxType>
QPointer<BoxType> show(
object_ptr<BoxType> content,
LayerOptions options = LayerOption::CloseOther,
anim::type animated = anim::type::normal) {
auto result = QPointer<BoxType>(content.data());
internal::showBox(std::move(content), options, animated);
return result;
}
void hideLayer(anim::type animated = anim::type::normal);
void hideSettingsAndLayer(anim::type animated = anim::type::normal);
bool isLayerShown();
} // namespace Ui

View File

@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h"
#include "base/flat_set.h"
#include "boxes/confirm_box.h"
#include "boxes/confirm_phone_box.h" // ExtractPhonePrefix.
#include "boxes/photo_crop_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/peers/add_participants_box.h"
@@ -21,14 +22,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/file_utilities.h"
#include "core/application.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "window/window_session_controller.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
#include "ui/toast/toast.h"
#include "ui/special_buttons.h"
#include "ui/special_fields.h"
#include "ui/text_options.h"
#include "ui/unread_badge.h"
#include "ui/ui_utility.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
@@ -37,11 +40,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h"
#include "apiwrap.h"
#include "observer_peer.h"
#include "auth_session.h"
#include "main/main_session.h"
#include "facades.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
namespace {
constexpr auto kMaxGroupChannelTitle = 255; // See also edit_peer_info_box.
constexpr auto kMaxGroupChannelTitle = 128; // See also edit_peer_info_box.
constexpr auto kMaxUserFirstLastName = 64; // See also edit_contact_box.
constexpr auto kMaxChannelDescription = 255; // See also edit_peer_info_box.
constexpr auto kMinUsernameLength = 5;
@@ -56,6 +64,48 @@ bool IsValidPhone(QString phone) {
|| phone == qsl("4242")));
}
void ChatCreateDone(
not_null<Window::SessionNavigation*> navigation,
QImage image,
const MTPUpdates &updates) {
navigation->session().api().applyUpdates(updates);
auto success = base::make_optional(&updates)
| [](auto updates) -> std::optional<const QVector<MTPChat>*> {
switch (updates->type()) {
case mtpc_updates:
return &updates->c_updates().vchats().v;
case mtpc_updatesCombined:
return &updates->c_updatesCombined().vchats().v;
}
LOG(("API Error: unexpected update cons %1 "
"(GroupInfoBox::creationDone)").arg(updates->type()));
return std::nullopt;
}
| [](auto chats) {
return (!chats->empty()
&& chats->front().type() == mtpc_chat)
? base::make_optional(chats)
: std::nullopt;
}
| [&](auto chats) {
return navigation->session().data().chat(
chats->front().c_chat().vid().v);
}
| [&](not_null<ChatData*> chat) {
if (!image.isNull()) {
chat->session().api().uploadPeerPhoto(
chat,
std::move(image));
}
Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
};
if (!success) {
LOG(("API Error: chat not found in updates "
"(ContactsBox::creationDone)"));
}
}
} // namespace
style::InputField CreateBioFieldStyle() {
@@ -83,7 +133,7 @@ void ShowAddParticipantsError(
if (error == qstr("USER_BOT")) {
const auto channel = chat->asChannel();
if ((users.size() == 1)
&& (users.front()->botInfo != nullptr)
&& users.front()->isBot()
&& channel
&& !channel->isMegagroup()
&& channel->canAddAdmins()) {
@@ -103,7 +153,8 @@ void ShowAddParticipantsError(
auto box = Box<EditAdminBox>(
channel,
user,
MTP_chatAdminRights(MTP_flags(0)));
MTP_chatAdminRights(MTP_flags(0)),
QString());
box->setSaveCallback(saveCallback);
*weak = Ui::show(std::move(box));
};
@@ -117,9 +168,7 @@ void ShowAddParticipantsError(
return;
}
}
const auto bot = ranges::find_if(users, [](not_null<UserData*> user) {
return user->botInfo != nullptr;
});
const auto bot = ranges::find_if(users, &UserData::isBot);
const auto hasBot = (bot != end(users));
const auto text = [&] {
if (error == qstr("USER_BOT")) {
@@ -155,7 +204,10 @@ void ShowAddParticipantsError(
class RevokePublicLinkBox::Inner : public TWidget, private MTP::Sender {
public:
Inner(QWidget *parent, Fn<void()> revokeCallback);
Inner(
QWidget *parent,
not_null<Main::Session*> session,
Fn<void()> revokeCallback);
protected:
void mouseMoveEvent(QMouseEvent *e) override;
@@ -174,6 +226,8 @@ private:
void paintChat(Painter &p, const ChatRow &row, bool selected) const;
void updateSelected();
const not_null<Main::Session*> _session;
PeerData *_selected = nullptr;
PeerData *_pressed = nullptr;
@@ -189,38 +243,42 @@ private:
};
AddContactBox::AddContactBox(QWidget*, QString fname, QString lname, QString phone)
: _first(this, st::defaultInputField, tr::lng_signup_firstname(), fname)
AddContactBox::AddContactBox(
QWidget*,
not_null<Main::Session*> session)
: AddContactBox(nullptr, session, QString(), QString(), QString()) {
}
AddContactBox::AddContactBox(
QWidget*,
not_null<Main::Session*> session,
QString fname,
QString lname,
QString phone)
: _session(session)
, _first(this, st::defaultInputField, tr::lng_signup_firstname(), fname)
, _last(this, st::defaultInputField, tr::lng_signup_lastname(), lname)
, _phone(this, st::defaultInputField, tr::lng_contact_phone(), phone)
, _phone(
this,
st::defaultInputField,
tr::lng_contact_phone(),
ExtractPhonePrefix(session->user()->phone()),
phone)
, _invertOrder(langFirstNameGoesSecond()) {
if (!phone.isEmpty()) {
_phone->setDisabled(true);
}
}
AddContactBox::AddContactBox(QWidget*, UserData *user)
: _user(user)
, _first(this, st::defaultInputField, tr::lng_signup_firstname(), user->firstName)
, _last(this, st::defaultInputField, tr::lng_signup_lastname(), user->lastName)
, _phone(this, st::defaultInputField, tr::lng_contact_phone(), user->phone())
, _invertOrder(langFirstNameGoesSecond()) {
_phone->setDisabled(true);
}
void AddContactBox::prepare() {
if (_invertOrder) {
setTabOrder(_last, _first);
}
if (_user) {
setTitle(tr::lng_edit_contact_title());
} else {
const auto readyToAdd = !_phone->getLastText().isEmpty()
&& (!_first->getLastText().isEmpty() || !_last->getLastText().isEmpty());
setTitle(readyToAdd
? tr::lng_confirm_contact_data()
: tr::lng_enter_contact_data());
}
const auto readyToAdd = !_phone->getLastText().isEmpty()
&& (!_first->getLastText().isEmpty() || !_last->getLastText().isEmpty());
setTitle(readyToAdd
? tr::lng_confirm_contact_data()
: tr::lng_enter_contact_data());
updateButtons();
connect(_first, &Ui::InputField::submitted, [=] { submit(); });
@@ -294,7 +352,9 @@ void AddContactBox::submit() {
}
void AddContactBox::save() {
if (_addRequest) return;
if (_addRequest) {
return;
}
auto firstName = TextUtilities::PrepareForSending(_first->getLastText());
auto lastName = TextUtilities::PrepareForSending(_last->getLastText());
@@ -308,7 +368,7 @@ void AddContactBox::save() {
_first->showError();
}
return;
} else if (!_user && !IsValidPhone(phone)) {
} else if (!IsValidPhone(phone)) {
_phone->setFocus();
_phone->showError();
return;
@@ -318,70 +378,44 @@ void AddContactBox::save() {
lastName = QString();
}
_sentName = firstName;
if (_user) {
_contactId = rand_value<uint64>();
QVector<MTPInputContact> v(1, MTP_inputPhoneContact(MTP_long(_contactId), MTP_string(_user->phone()), MTP_string(firstName), MTP_string(lastName)));
_addRequest = MTP::send(MTPcontacts_ImportContacts(MTP_vector<MTPInputContact>(v)), rpcDone(&AddContactBox::onSaveUserDone), rpcFail(&AddContactBox::onSaveUserFail));
} else {
_contactId = rand_value<uint64>();
QVector<MTPInputContact> v(1, MTP_inputPhoneContact(MTP_long(_contactId), MTP_string(phone), MTP_string(firstName), MTP_string(lastName)));
_addRequest = MTP::send(MTPcontacts_ImportContacts(MTP_vector<MTPInputContact>(v)), rpcDone(&AddContactBox::onImportDone));
}
}
_contactId = rand_value<uint64>();
_addRequest = _session->api().request(MTPcontacts_ImportContacts(
MTP_vector<MTPInputContact>(
1,
MTP_inputPhoneContact(
MTP_long(_contactId),
MTP_string(phone),
MTP_string(firstName),
MTP_string(lastName)))
)).done(crl::guard(this, [=](
const MTPcontacts_ImportedContacts &result) {
result.match([&](const MTPDcontacts_importedContacts &data) {
_session->data().processUsers(data.vusers());
bool AddContactBox::onSaveUserFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
_addRequest = 0;
const auto &err = error.type();
const auto firstName = _first->getLastText().trimmed();
const auto lastName = _last->getLastText().trimmed();
if (err == "CHAT_TITLE_NOT_MODIFIED") {
_user->setName(firstName, lastName, _user->nameOrPhone, _user->username);
closeBox();
return true;
} else if (err == "NO_CHAT_TITLE") {
_first->setFocus();
_first->showError();
return true;
}
_first->setFocus();
return true;
}
void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) {
if (!isBoxShown() || !App::main()) return;
const auto &d = res.c_contacts_importedContacts();
Auth().data().processUsers(d.vusers);
const auto &v = d.vimported.v;
const auto user = [&]() -> UserData* {
if (!v.isEmpty()) {
auto &c = v.front().c_importedContact();
if (c.vclient_id.v == _contactId) {
return Auth().data().userLoaded(c.vuser_id.v);
const auto extractUser = [&](const MTPImportedContact &data) {
return data.match([&](const MTPDimportedContact &data) {
return (data.vclient_id().v == _contactId)
? _session->data().userLoaded(data.vuser_id().v)
: nullptr;
});
};
const auto &list = data.vimported().v;
const auto user = list.isEmpty()
? nullptr
: extractUser(list.front());
if (user) {
if (user->isContact() || user->session().supportMode()) {
Ui::showPeerHistory(user, ShowAtTheEndMsgId);
}
Ui::hideLayer();
} else if (isBoxShown()) {
hideChildren();
_retrying = true;
updateButtons();
update();
}
}
return nullptr;
}();
if (user) {
if (user->isContact() || user->session().supportMode()) {
Ui::showPeerHistory(user, ShowAtTheEndMsgId);
}
Ui::hideLayer();
} else {
hideChildren();
_retrying = true;
updateButtons();
update();
}
}
void AddContactBox::onSaveUserDone(const MTPcontacts_ImportedContacts &res) {
auto &d = res.c_contacts_importedContacts();
Auth().data().processUsers(d.vusers);
closeBox();
});
})).send();
}
void AddContactBox::retry() {
@@ -403,19 +437,19 @@ void AddContactBox::updateButtons() {
if (_retrying) {
addButton(tr::lng_try_other_contact(), [=] { retry(); });
} else {
addButton(
_user ? tr::lng_settings_save() : tr::lng_add_contact(),
[=] { save(); });
addButton(tr::lng_add_contact(), [=] { save(); });
addButton(tr::lng_cancel(), [=] { closeBox(); });
}
}
GroupInfoBox::GroupInfoBox(
QWidget*,
not_null<Window::SessionNavigation*> navigation,
Type type,
const QString &title,
Fn<void(not_null<ChannelData*>)> channelDone)
: _type(type)
: _navigation(navigation)
, _type(type)
, _initialTitle(title)
, _channelDone(std::move(channelDone)) {
}
@@ -439,10 +473,12 @@ void GroupInfoBox::prepare() {
_initialTitle);
_title->setMaxLength(kMaxGroupChannelTitle);
_title->setInstantReplaces(Ui::InstantReplaces::Default());
_title ->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
_title->setInstantReplacesEnabled(
_navigation->session().settings().replaceEmojiValue());
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_title);
_title,
&_navigation->session());
if (_type != Type::Group) {
_description.create(
@@ -454,7 +490,7 @@ void GroupInfoBox::prepare() {
_description->setMaxLength(kMaxChannelDescription);
_description->setInstantReplaces(Ui::InstantReplaces::Default());
_description->setInstantReplacesEnabled(
Global::ReplaceEmojiValue());
_navigation->session().settings().replaceEmojiValue());
connect(_description, &Ui::InputField::resized, [=] { descriptionResized(); });
connect(_description, &Ui::InputField::submitted, [=] { submit(); });
@@ -462,7 +498,8 @@ void GroupInfoBox::prepare() {
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_description);
_description,
&_navigation->session());
}
connect(_title, &Ui::InputField::submitted, [=] { submitName(); });
@@ -536,47 +573,14 @@ void GroupInfoBox::createGroup(
MTP_string(title)
)).done([=](const MTPUpdates &result) {
auto image = _photo->takeResultImage();
Ui::hideLayer();
const auto navigation = _navigation;
Auth().api().applyUpdates(result);
auto success = base::make_optional(&result)
| [](auto updates) -> std::optional<const QVector<MTPChat>*> {
switch (updates->type()) {
case mtpc_updates:
return &updates->c_updates().vchats.v;
case mtpc_updatesCombined:
return &updates->c_updatesCombined().vchats.v;
}
LOG(("API Error: unexpected update cons %1 "
"(GroupInfoBox::creationDone)").arg(updates->type()));
return std::nullopt;
}
| [](auto chats) {
return (!chats->empty()
&& chats->front().type() == mtpc_chat)
? base::make_optional(chats)
: std::nullopt;
}
| [](auto chats) {
return Auth().data().chat(chats->front().c_chat().vid.v);
}
| [&](not_null<ChatData*> chat) {
if (!image.isNull()) {
chat->session().api().uploadPeerPhoto(
chat,
std::move(image));
}
Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
};
if (!success) {
LOG(("API Error: chat not found in updates "
"(ContactsBox::creationDone)"));
}
Ui::hideLayer(); // Destroys 'this'.
ChatCreateDone(navigation, std::move(image), result);
}).fail([=](const RPCError &error) {
_creationRequestId = 0;
if (error.type() == qstr("NO_CHAT_TITLE")) {
auto weak = make_weak(this);
auto weak = Ui::MakeWeak(this);
selectUsersBox->closeBox();
if (weak) {
_title->showError();
@@ -615,7 +619,7 @@ void GroupInfoBox::submit() {
if (_type != Type::Group) {
createChannel(title, description);
} else {
auto initBox = [title, weak = make_weak(this)](
auto initBox = [title, weak = Ui::MakeWeak(this)](
not_null<PeerListBox*> box) {
auto create = [box, title, weak] {
if (weak) {
@@ -630,7 +634,7 @@ void GroupInfoBox::submit() {
};
Ui::show(
Box<PeerListBox>(
std::make_unique<AddParticipantsBoxController>(),
std::make_unique<AddParticipantsBoxController>(_navigation),
std::move(initBox)),
LayerOption::KeepOther);
}
@@ -647,15 +651,15 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
MTPInputGeoPoint(), // geo_point
MTPstring() // address
)).done([=](const MTPUpdates &result) {
Auth().api().applyUpdates(result);
_navigation->session().api().applyUpdates(result);
const auto success = base::make_optional(&result)
| [](auto updates) -> std::optional<const QVector<MTPChat>*> {
switch (updates->type()) {
case mtpc_updates:
return &updates->c_updates().vchats.v;
return &updates->c_updates().vchats().v;
case mtpc_updatesCombined:
return &updates->c_updatesCombined().vchats.v;
return &updates->c_updatesCombined().vchats().v;
}
LOG(("API Error: unexpected update cons %1 (GroupInfoBox::createChannel)").arg(updates->type()));
return std::nullopt;
@@ -665,8 +669,9 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
? base::make_optional(chats)
: std::nullopt;
}
| [](auto chats) {
return Auth().data().channel(chats->front().c_channel().vid.v);
| [&](auto chats) {
return _navigation->session().data().channel(
chats->front().c_channel().vid().v);
}
| [&](not_null<ChannelData*> channel) {
auto image = _photo->takeResultImage();
@@ -681,7 +686,7 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
)).done([=](const MTPExportedChatInvite &result) {
_creationRequestId = 0;
if (result.type() == mtpc_chatInviteExported) {
auto link = qs(result.c_chatInviteExported().vlink);
auto link = qs(result.c_chatInviteExported().vlink());
_createdChannel->setInviteLink(link);
}
if (_channelDone) {
@@ -690,7 +695,9 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
closeBox();
callback(argument);
} else {
Ui::show(Box<SetupChannelBox>(_createdChannel));
Ui::show(Box<SetupChannelBox>(
_navigation,
_createdChannel));
}
}).send();
};
@@ -730,10 +737,16 @@ void GroupInfoBox::updateMaxHeight() {
setDimensions(st::boxWideWidth, newHeight);
}
SetupChannelBox::SetupChannelBox(QWidget*, ChannelData *channel, bool existing)
: _channel(channel)
SetupChannelBox::SetupChannelBox(
QWidget*,
not_null<Window::SessionNavigation*> navigation,
not_null<ChannelData*> channel,
bool existing)
: _navigation(navigation)
, _channel(channel)
, _existing(existing)
, _privacyGroup(std::make_shared<Ui::RadioenumGroup<Privacy>>(Privacy::Public))
, _privacyGroup(
std::make_shared<Ui::RadioenumGroup<Privacy>>(Privacy::Public))
, _public(
this,
_privacyGroup,
@@ -750,7 +763,12 @@ SetupChannelBox::SetupChannelBox(QWidget*, ChannelData *channel, bool existing)
? tr::lng_create_private_group_title
: tr::lng_create_private_channel_title)(tr::now),
st::defaultBoxCheckbox)
, _aboutPublicWidth(st::boxWideWidth - st::boxPadding.left() - st::boxButtonPadding.right() - st::newGroupPadding.left() - st::defaultRadio.diameter - st::defaultBoxCheckbox.textPosition.x())
, _aboutPublicWidth(st::boxWideWidth
- st::boxPadding.left()
- st::boxButtonPadding.right()
- st::newGroupPadding.left()
- st::defaultRadio.diameter
- st::defaultBoxCheckbox.textPosition.x())
, _aboutPublic(
st::defaultTextStyle,
(channel->isMegagroup()
@@ -800,7 +818,7 @@ void SetupChannelBox::prepare() {
boxClosing() | rpl::start_with_next([=] {
if (!_existing) {
AddParticipantsBoxController::Start(_channel);
AddParticipantsBoxController::Start(_navigation, _channel);
}
}, lifetime());
@@ -1004,11 +1022,16 @@ void SetupChannelBox::privacyChanged(Privacy value) {
if (value == Privacy::Public) {
if (_tooMuchUsernames) {
_privacyGroup->setValue(Privacy::Private);
Ui::show(Box<RevokePublicLinkBox>(crl::guard(this, [this] {
const auto callback = crl::guard(this, [=] {
_tooMuchUsernames = false;
_privacyGroup->setValue(Privacy::Public);
check();
})), LayerOption::KeepOther);
});
Ui::show(
Box<RevokePublicLinkBox>(
&_channel->session(),
callback),
LayerOption::KeepOther);
return;
}
_link->show();
@@ -1097,23 +1120,31 @@ bool SetupChannelBox::onCheckFail(const RPCError &error) {
}
void SetupChannelBox::showRevokePublicLinkBoxForEdit() {
closeBox();
Ui::show(Box<RevokePublicLinkBox>([channel = _channel, existing = _existing]() {
const auto channel = _channel;
const auto existing = _existing;
const auto navigation = _navigation;
const auto callback = [=] {
Ui::show(
Box<SetupChannelBox>(channel, existing),
Box<SetupChannelBox>(navigation, channel, existing),
LayerOption::KeepOther);
}), LayerOption::KeepOther);
};
closeBox();
Ui::show(
Box<RevokePublicLinkBox>(
&channel->session(),
callback),
LayerOption::KeepOther);
}
bool SetupChannelBox::onFirstCheckFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
_checkRequestId = 0;
QString err(error.type());
if (err == qstr("CHANNEL_PUBLIC_GROUP_NA")) {
const auto &type = error.type();
if (type == qstr("CHANNEL_PUBLIC_GROUP_NA")) {
Ui::hideLayer();
return true;
} else if (err == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) {
} else if (type == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) {
if (_existing) {
showRevokePublicLinkBoxForEdit();
} else {
@@ -1148,8 +1179,8 @@ void EditNameBox::prepare() {
if (_invertOrder) {
setTabOrder(_last, _first);
}
_first->setMaxLength(kMaxGroupChannelTitle);
_last->setMaxLength(kMaxGroupChannelTitle);
_first->setMaxLength(kMaxUserFirstLastName);
_last->setMaxLength(kMaxUserFirstLastName);
connect(_first, &Ui::InputField::submitted, [=] { submit(); });
connect(_last, &Ui::InputField::submitted, [=] { submit(); });
@@ -1249,7 +1280,12 @@ bool EditNameBox::saveSelfFail(const RPCError &error) {
return true;
}
RevokePublicLinkBox::Inner::Inner(QWidget *parent, Fn<void()> revokeCallback) : TWidget(parent)
RevokePublicLinkBox::Inner::Inner(
QWidget *parent,
not_null<Main::Session*> session,
Fn<void()> revokeCallback)
: TWidget(parent)
, _session(session)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _revokeWidth(st::normalFont->width(tr::lng_channels_too_much_public_revoke(tr::now)))
, _revokeCallback(std::move(revokeCallback)) {
@@ -1261,10 +1297,10 @@ RevokePublicLinkBox::Inner::Inner(QWidget *parent, Fn<void()> revokeCallback) :
MTP_flags(0)
)).done([=](const MTPmessages_Chats &result) {
const auto &chats = result.match([](const auto &data) {
return data.vchats.v;
return data.vchats().v;
});
for (const auto &chat : chats) {
if (const auto peer = Auth().data().processChat(chat)) {
if (const auto peer = _session->data().processChat(chat)) {
if (!peer->isChannel() || peer->userName().isEmpty()) {
continue;
}
@@ -1290,8 +1326,10 @@ RevokePublicLinkBox::Inner::Inner(QWidget *parent, Fn<void()> revokeCallback) :
RevokePublicLinkBox::RevokePublicLinkBox(
QWidget*,
not_null<Main::Session*> session,
Fn<void()> revokeCallback)
: _aboutRevoke(
: _session(session)
, _aboutRevoke(
this,
tr::lng_channels_too_much_public_about(tr::now),
st::aboutRevokePublicLabel)
@@ -1300,7 +1338,7 @@ RevokePublicLinkBox::RevokePublicLinkBox(
void RevokePublicLinkBox::prepare() {
_innerTop = st::boxPadding.top() + _aboutRevoke->height() + st::boxPadding.top();
_inner = setInnerWidget(object_ptr<Inner>(this, [=] {
_inner = setInnerWidget(object_ptr<Inner>(this, _session, [=] {
const auto callback = _revokeCallback;
closeBox();
if (callback) {
@@ -1310,7 +1348,7 @@ void RevokePublicLinkBox::prepare() {
addButton(tr::lng_cancel(), [=] { closeBox(); });
subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
subscribe(_session->downloaderTaskFinished(), [=] { update(); });
_inner->resizeToWidth(st::boxWideWidth);
setDimensions(st::boxWideWidth, _innerTop + _inner->height());
@@ -1324,8 +1362,8 @@ void RevokePublicLinkBox::Inner::updateSelected() {
auto point = mapFromGlobal(QCursor::pos());
PeerData *selected = nullptr;
auto top = _rowsTop;
for_const (auto &row, _rows) {
auto revokeLink = rtlrect(width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - _revokeWidth, top + st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, _revokeWidth, st::normalFont->height, width());
for (const auto &row : _rows) {
auto revokeLink = style::rtlrect(width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - _revokeWidth, top + st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, _revokeWidth, st::normalFont->height, width());
if (revokeLink.contains(point)) {
selected = row.peer;
break;
@@ -1364,7 +1402,7 @@ void RevokePublicLinkBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
if (_revokeRequestId) return;
_revokeRequestId = request(MTPchannels_UpdateUsername(
pressed->asChannel()->inputChannel,
MTP_string("")
MTP_string()
)).done([=](const MTPBool &result) {
const auto callback = _revokeCallback;
if (_weakRevokeConfirmBox) {

View File

@@ -11,12 +11,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h"
#include "styles/style_widgets.h"
#include <QtCore/QTimer>
class ConfirmBox;
class PeerListBox;
constexpr auto kMaxBioLength = 70;
namespace Window {
class SessionNavigation;
} // namespace Window
style::InputField CreateBioFieldStyle();
namespace Main {
class Session;
} // namespace Main
namespace Ui {
class FlatLabel;
@@ -32,21 +38,31 @@ class LinkButton;
class UserpicButton;
} // namespace Ui
constexpr auto kMaxBioLength = 70;
enum class PeerFloodType {
Send,
InviteGroup,
InviteChannel,
};
style::InputField CreateBioFieldStyle();
QString PeerFloodErrorText(PeerFloodType type);
void ShowAddParticipantsError(
const QString &error,
not_null<PeerData*> chat,
const std::vector<not_null<UserData*>> &users);
class AddContactBox : public BoxContent, public RPCSender {
class AddContactBox : public BoxContent {
public:
AddContactBox(QWidget*, QString fname = QString(), QString lname = QString(), QString phone = QString());
AddContactBox(QWidget*, UserData *user);
AddContactBox(QWidget*, not_null<Main::Session*> session);
AddContactBox(
QWidget*,
not_null<Main::Session*> session,
QString fname,
QString lname,
QString phone);
protected:
void prepare() override;
@@ -61,12 +77,8 @@ private:
void retry();
void save();
void updateButtons();
void onImportDone(const MTPcontacts_ImportedContacts &res);
void onSaveUserDone(const MTPcontacts_ImportedContacts &res);
bool onSaveUserFail(const RPCError &e);
UserData *_user = nullptr;
const not_null<Main::Session*> _session;
object_ptr<Ui::InputField> _first;
object_ptr<Ui::InputField> _last;
@@ -91,6 +103,7 @@ public:
};
GroupInfoBox(
QWidget*,
not_null<Window::SessionNavigation*> navigation,
Type type,
const QString &title = QString(),
Fn<void(not_null<ChannelData*>)> channelDone = nullptr);
@@ -110,6 +123,8 @@ private:
void descriptionResized();
void updateMaxHeight();
const not_null<Window::SessionNavigation*> _navigation;
Type _type = Type::Group;
QString _initialTitle;
Fn<void(not_null<ChannelData*>)> _channelDone;
@@ -124,9 +139,16 @@ private:
};
class SetupChannelBox : public BoxContent, public RPCSender {
class SetupChannelBox
: public BoxContent
, public RPCSender
, private base::Subscriber {
public:
SetupChannelBox(QWidget*, ChannelData *channel, bool existing = false);
SetupChannelBox(
QWidget*,
not_null<Window::SessionNavigation*> navigation,
not_null<ChannelData*> channel,
bool existing = false);
void setInnerFocus() override;
@@ -162,7 +184,9 @@ private:
void showRevokePublicLinkBoxForEdit();
ChannelData *_channel = nullptr;
const not_null<Window::SessionNavigation*> _navigation;
const not_null<ChannelData*> _channel;
bool _existing = false;
std::shared_ptr<Ui::RadioenumGroup<Privacy>> _privacyGroup;
@@ -213,9 +237,15 @@ private:
};
class RevokePublicLinkBox : public BoxContent, public RPCSender {
class RevokePublicLinkBox
: public BoxContent
, public RPCSender
, private base::Subscriber {
public:
RevokePublicLinkBox(QWidget*, Fn<void()> revokeCallback);
RevokePublicLinkBox(
QWidget*,
not_null<Main::Session*> session,
Fn<void()> revokeCallback);
protected:
void prepare() override;
@@ -223,6 +253,8 @@ protected:
void resizeEvent(QResizeEvent *e) override;
private:
const not_null<Main::Session*> _session;
object_ptr<Ui::FlatLabel> _aboutRevoke;
class Inner;

View File

@@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/auto_download_box.h"
#include "lang/lang_keys.h"
#include "auth_session.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "info/profile/info_profile_button.h"
#include "ui/widgets/continuous_sliders.h"
@@ -29,8 +29,10 @@ constexpr auto kDefaultLimit = 10 * kMegabyte;
AutoDownloadBox::AutoDownloadBox(
QWidget*,
not_null<Main::Session*> session,
Data::AutoDownload::Source source)
: _source(source) {
: _session(session)
, _source(source) {
}
void AutoDownloadBox::prepare() {
@@ -45,7 +47,7 @@ void AutoDownloadBox::setupContent() {
setTitle(tr::lng_media_auto_title());
const auto settings = &Auth().settings().autoDownload();
const auto settings = &_session->settings().autoDownload();
const auto checked = [=](Source source, Type type) {
return (settings->bytesLimit(source, type) > 0);
};
@@ -166,11 +168,11 @@ void AutoDownloadBox::setupContent() {
Local::writeUserSettings();
}
if (allowMoreTypes.contains(Type::Photo)) {
Auth().data().photoLoadSettingsChanged();
_session->data().photoLoadSettingsChanged();
}
if (ranges::find_if(allowMoreTypes, _1 != Type::Photo)
!= allowMoreTypes.end()) {
Auth().data().documentLoadSettingsChanged();
_session->data().documentLoadSettingsChanged();
}
closeBox();
});

View File

@@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
namespace Main {
class Session;
} // namespace Main
namespace Data {
namespace AutoDownload {
enum class Source;
@@ -17,7 +21,10 @@ enum class Source;
class AutoDownloadBox : public BoxContent {
public:
AutoDownloadBox(QWidget*, Data::AutoDownload::Source source);
AutoDownloadBox(
QWidget*,
not_null<Main::Session*> session,
Data::AutoDownload::Source source);
protected:
void prepare() override;
@@ -25,6 +32,8 @@ protected:
private:
void setupContent();
const not_null<Main::Session*> _session;
Data::AutoDownload::Source _source;
};

View File

@@ -11,8 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localstorage.h"
#include "mainwindow.h"
#include "ui/widgets/checkbox.h"
#include "facades.h"
#include "styles/style_boxes.h"
AutoLockBox::AutoLockBox(QWidget*, not_null<Main::Session*> session)
: _session(session) {
}
void AutoLockBox::prepare() {
setTitle(tr::lng_passcode_autolock());
@@ -39,6 +44,6 @@ void AutoLockBox::durationChanged(int seconds) {
Local::writeUserSettings();
Global::RefLocalPasscodeChanged().notify();
Auth().checkAutoLock();
_session->checkAutoLock();
closeBox();
}

View File

@@ -9,14 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
namespace Main {
class Session;
} // namespace Main
namespace Ui {
class Radiobutton;
} // namespace Ui
class AutoLockBox : public BoxContent {
public:
AutoLockBox(QWidget*) {
}
AutoLockBox(QWidget*, not_null<Main::Session*> session);
protected:
void prepare() override;
@@ -24,6 +27,8 @@ protected:
private:
void durationChanged(int seconds);
const not_null<Main::Session*> _session;
std::vector<object_ptr<Ui::Radiobutton>> _options;
};

View File

@@ -10,12 +10,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "ui/effects/round_checkbox.h"
#include "ui/image/image.h"
#include "auth_session.h"
#include "ui/ui_utility.h"
#include "main/main_session.h"
#include "apiwrap.h"
#include "mtproto/sender.h"
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "boxes/background_preview_box.h"
#include "boxes/confirm_box.h"
#include "app.h"
#include "styles/style_overview.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h"
@@ -54,7 +57,9 @@ class BackgroundBox::Inner
, private MTP::Sender
, private base::Subscriber {
public:
Inner(QWidget *parent);
Inner(
QWidget *parent,
not_null<Main::Session*> session);
rpl::producer<Data::WallPaper> chooseEvents() const;
rpl::producer<Data::WallPaper> removeRequests() const;
@@ -107,6 +112,8 @@ private:
int row) const;
void validatePaperThumbnail(const Paper &paper) const;
const not_null<Main::Session*> _session;
std::vector<Paper> _papers;
Selection _over;
@@ -118,7 +125,8 @@ private:
};
BackgroundBox::BackgroundBox(QWidget*) {
BackgroundBox::BackgroundBox(QWidget*, not_null<Main::Session*> session)
: _session(session) {
}
void BackgroundBox::prepare() {
@@ -128,11 +136,15 @@ void BackgroundBox::prepare() {
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
_inner = setInnerWidget(object_ptr<Inner>(this), st::backgroundScroll);
_inner = setInnerWidget(
object_ptr<Inner>(this, _session),
st::backgroundScroll);
_inner->chooseEvents(
) | rpl::start_with_next([](const Data::WallPaper &paper) {
Ui::show(Box<BackgroundPreviewBox>(paper), LayerOption::KeepOther);
) | rpl::start_with_next([=](const Data::WallPaper &paper) {
Ui::show(
Box<BackgroundPreviewBox>(_session, paper),
LayerOption::KeepOther);
}, _inner->lifetime());
_inner->removeRequests(
@@ -143,15 +155,16 @@ void BackgroundBox::prepare() {
void BackgroundBox::removePaper(const Data::WallPaper &paper) {
const auto box = std::make_shared<QPointer<BoxContent>>();
const auto remove = [=, weak = make_weak(this)]{
const auto session = _session;
const auto remove = [=, weak = Ui::MakeWeak(this)]{
if (*box) {
(*box)->closeBox();
}
if (weak) {
weak->_inner->removePaper(paper);
}
Auth().data().removeWallpaper(paper);
Auth().api().request(MTPaccount_SaveWallPaper(
session->data().removeWallpaper(paper);
session->api().request(MTPaccount_SaveWallPaper(
paper.mtpInput(),
MTP_bool(true),
paper.mtpSettings()
@@ -166,17 +179,21 @@ void BackgroundBox::removePaper(const Data::WallPaper &paper) {
LayerOption::KeepOther);
}
BackgroundBox::Inner::Inner(QWidget *parent) : RpWidget(parent)
BackgroundBox::Inner::Inner(
QWidget *parent,
not_null<Main::Session*> session)
: RpWidget(parent)
, _session(session)
, _check(std::make_unique<Ui::RoundCheckbox>(st::overviewCheck, [=] { update(); })) {
_check->setChecked(true, Ui::RoundCheckbox::SetStyle::Fast);
if (Auth().data().wallpapers().empty()) {
if (_session->data().wallpapers().empty()) {
resize(st::boxWideWidth, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
} else {
updatePapers();
}
requestPapers();
subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
subscribe(_session->downloaderTaskFinished(), [=] { update(); });
using Update = Window::Theme::BackgroundUpdate;
subscribe(Window::Theme::Background(), [=](const Update &update) {
if (update.paletteChanged()) {
@@ -192,9 +209,9 @@ BackgroundBox::Inner::Inner(QWidget *parent) : RpWidget(parent)
void BackgroundBox::Inner::requestPapers() {
request(MTPaccount_GetWallPapers(
MTP_int(Auth().data().wallpapersHash())
MTP_int(_session->data().wallpapersHash())
)).done([=](const MTPaccount_WallPapers &result) {
if (Auth().data().updateWallpapers(result)) {
if (_session->data().updateWallpapers(result)) {
updatePapers();
}
}).send();
@@ -220,7 +237,7 @@ void BackgroundBox::Inner::sortPapers() {
void BackgroundBox::Inner::updatePapers() {
_over = _overDown = Selection();
_papers = Auth().data().wallpapers(
_papers = _session->data().wallpapers(
) | ranges::view::filter([](const Data::WallPaper &paper) {
return !paper.isPattern() || paper.backgroundColor().has_value();
}) | ranges::view::transform([](const Data::WallPaper &paper) {
@@ -315,7 +332,7 @@ void BackgroundBox::Inner::paintPaper(
if (paper.data.id() == Window::Theme::Background()->id()) {
const auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
const auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
_check->paint(p, crl::now(), checkLeft, checkTop, width());
_check->paint(p, checkLeft, checkTop, width());
} else if (Data::IsCloudWallPaper(paper.data)
&& !Data::IsDefaultWallPaper(paper.data)
&& over.has_value()

View File

@@ -9,13 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
namespace Main {
class Session;
} // namespace Main
namespace Data {
class WallPaper;
} // namespace Data
class BackgroundBox : public BoxContent {
public:
BackgroundBox(QWidget*);
BackgroundBox(QWidget*, not_null<Main::Session*> session);
protected:
void prepare() override;
@@ -25,6 +29,8 @@ private:
void removePaper(const Data::WallPaper &paper);
const not_null<Main::Session*> _session;
QPointer<Inner> _inner;
};

View File

@@ -13,19 +13,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h"
#include "ui/image/image.h"
#include "ui/widgets/checkbox.h"
#include "ui/ui_utility.h"
#include "history/history.h"
#include "history/history_message.h"
#include "history/view/history_view_message.h"
#include "auth_session.h"
#include "main/main_session.h"
#include "apiwrap.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "data/data_document.h"
#include "base/unixtime.h"
#include "boxes/confirm_box.h"
#include "boxes/background_preview_box.h"
#include "app.h"
#include "styles/style_history.h"
#include "styles/style_boxes.h"
#include <QtGui/QClipboard>
#include <QtGui/QGuiApplication>
namespace {
constexpr auto kMaxWallPaperSlugLength = 255;
@@ -220,7 +226,7 @@ void ServiceCheck::Generator::invalidate() {
ServiceCheck::Generator &ServiceCheck::Frames() {
static const auto Instance = Ui::CreateChild<Generator>(
QApplication::instance());
QCoreApplication::instance());
return *Instance;
}
@@ -283,15 +289,17 @@ AdminLog::OwnedItem GenerateTextItem(
const auto flags = Flag::f_entities
| Flag::f_from_id
| (out ? Flag::f_out : Flag(0));
const auto clientFlags = MTPDmessage_ClientFlag::f_fake_history_item;
const auto replyTo = 0;
const auto viaBotId = 0;
const auto item = history->owner().makeMessage(
history,
++id,
flags,
clientFlags,
replyTo,
viaBotId,
unixtime(),
base::unixtime::now(),
out ? history->session().userId() : peerToUser(history->peer->id),
QString(),
TextWithEntities{ TextUtilities::Clean(text) });
@@ -386,20 +394,24 @@ QImage PrepareScaledFromFull(
BackgroundPreviewBox::BackgroundPreviewBox(
QWidget*,
not_null<Main::Session*> session,
const Data::WallPaper &paper)
: _text1(GenerateTextItem(
: _session(session)
, _text1(GenerateTextItem(
delegate(),
Auth().data().history(peerFromUser(PeerData::kServiceNotificationsId)),
_session->data().history(
peerFromUser(PeerData::kServiceNotificationsId)),
tr::lng_background_text1(tr::now),
false))
, _text2(GenerateTextItem(
delegate(),
Auth().data().history(peerFromUser(PeerData::kServiceNotificationsId)),
_session->data().history(
peerFromUser(PeerData::kServiceNotificationsId)),
tr::lng_background_text2(tr::now),
true))
, _paper(paper)
, _radial([=](crl::time now) { radialAnimationCallback(now); }) {
subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
subscribe(_session->downloaderTaskFinished(), [=] { update(); });
}
not_null<HistoryView::ElementDelegate*> BackgroundPreviewBox::delegate() {
@@ -482,7 +494,7 @@ void BackgroundPreviewBox::apply() {
&& Data::IsCloudWallPaper(_paper);
App::main()->setChatBackground(_paper, std::move(_full));
if (install) {
Auth().api().request(MTPaccount_InstallWallPaper(
_session->api().request(MTPaccount_InstallWallPaper(
_paper.mtpInput(),
_paper.mtpSettings()
)).send();
@@ -491,7 +503,7 @@ void BackgroundPreviewBox::apply() {
}
void BackgroundPreviewBox::share() {
QApplication::clipboard()->setText(_paper.shareUrl());
QGuiApplication::clipboard()->setText(_paper.shareUrl());
Ui::Toast::Show(tr::lng_background_link_copied(tr::now));
}
@@ -735,18 +747,23 @@ void BackgroundPreviewBox::checkLoadedDocument() {
}
bool BackgroundPreviewBox::Start(
not_null<Main::Session*> session,
const QString &slug,
const QMap<QString, QString> &params) {
if (const auto paper = Data::WallPaper::FromColorSlug(slug)) {
Ui::show(Box<BackgroundPreviewBox>(paper->withUrlParams(params)));
Ui::show(Box<BackgroundPreviewBox>(
session,
paper->withUrlParams(params)));
return true;
}
if (!IsValidWallPaperSlug(slug)) {
Ui::show(Box<InformBox>(tr::lng_background_bad_link(tr::now)));
return false;
}
Auth().api().requestWallPaper(slug, [=](const Data::WallPaper &result) {
Ui::show(Box<BackgroundPreviewBox>(result.withUrlParams(params)));
session->api().requestWallPaper(slug, [=](const Data::WallPaper &result) {
Ui::show(Box<BackgroundPreviewBox>(
session,
result.withUrlParams(params)));
}, [](const RPCError &error) {
Ui::show(Box<InformBox>(tr::lng_background_bad_link(tr::now)));
});

View File

@@ -15,17 +15,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animations.h"
#include "ui/effects/radial_animation.h"
namespace Main {
class Session;
} // namespace Main
namespace Ui {
class Checkbox;
} // namespace Ui
class BackgroundPreviewBox
: public BoxContent
, private HistoryView::SimpleElementDelegate {
, private HistoryView::SimpleElementDelegate
, private base::Subscriber {
public:
BackgroundPreviewBox(QWidget*, const Data::WallPaper &paper);
BackgroundPreviewBox(
QWidget*,
not_null<Main::Session*> session,
const Data::WallPaper &paper);
static bool Start(
not_null<Main::Session*> session,
const QString &slug,
const QMap<QString, QString> &params);
@@ -58,6 +67,7 @@ private:
void startFadeInFrom(QPixmap previous);
void checkBlurAnimationStart();
const not_null<Main::Session*> _session;
AdminLog::OwnedItem _text1;
AdminLog::OwnedItem _text2;
Data::WallPaper _paper;

View File

@@ -161,6 +161,12 @@ boxPhotoCompressedSkip: 20px;
boxPhotoCaptionSkip: 8px;
boxPhotoTextFg: windowSubTextFg;
boxLoadingAnimation: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) {
color: windowSubTextFg;
thickness: 2px;
}
boxLoadingSize: 20px;
cropPointSize: 10px;
cropSkip: 13px;
cropMinSize: 20px;
@@ -321,12 +327,14 @@ contactsMultiSelect: MultiSelect {
}
contactsPhotoCheckIcon: icon {{
"default_checkbox_check",
windowFgActive,
overviewCheckFgActive,
point(3px, 6px)
}};
contactsPhotoCheck: RoundCheckbox(defaultRoundCheckbox) {
size: 20px;
sizeSmall: 0.3;
bgInactive: overviewCheckBg;
bgActive: overviewCheckBgActive;
check: contactsPhotoCheckIcon;
}
contactsPhotoCheckbox: RoundImageCheckbox {
@@ -761,6 +769,7 @@ rightsHeaderLabel: FlatLabel(boxLabel) {
textFg: windowActiveTextFg;
}
rightsUntilMargin: margins(0px, 8px, 0px, 20px);
rightsRankMargin: margins(0px, 16px, 0px, 20px);
mutePhotoButton: UserpicButton(defaultUserpicButton) {
size: size(40px, 40px);
@@ -876,6 +885,21 @@ themesScroll: ScrollArea(defaultScrollArea) {
bottomsh: 0px;
topsh: 0px;
}
themesMenuToggle: IconButton(defaultIconButton) {
width: 44px;
height: 44px;
icon: icon {{ "title_menu_dots", menuIconFg }};
iconOver: icon {{ "title_menu_dots", menuIconFgOver }};
iconPosition: point(-1px, -1px);
rippleAreaPosition: point(4px, 4px);
rippleAreaSize: 36px;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
}
themesMenuPosition: point(-2px, 25px);
createPollField: InputField(defaultInputField) {
font: boxTextFont;
@@ -988,3 +1012,21 @@ blockUserConfirmation: FlatLabel(boxLabel) {
}
transferCheckWidth: 300px;
slowmodeLabelsMargin: margins(0px, 5px, 0px, 0px);
slowmodeLabel: LabelSimple(defaultLabelSimple) {
textFg: windowSubTextFg;
}
customBadgeField: InputField(defaultInputField) {
textMargins: margins(2px, 7px, 2px, 0px);
placeholderFg: placeholderFg;
placeholderFgActive: placeholderFgActive;
placeholderFgError: placeholderFgActive;
placeholderMargins: margins(0px, 0px, 0px, 0px);
placeholderScale: 0.;
placeholderFont: normalFont;
heightMin: 32px;
}

View File

@@ -8,10 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/calendar_box.h"
#include "ui/widgets/buttons.h"
#include "styles/style_boxes.h"
#include "styles/style_dialogs.h"
#include "lang/lang_keys.h"
#include "ui/effects/ripple_animation.h"
#include "ui/ui_utility.h"
#include "styles/style_boxes.h"
#include "styles/style_dialogs.h"
namespace {
@@ -27,6 +28,11 @@ public:
_month.setForced(_month.value(), true);
}
void setBeginningButton(bool enabled);
bool hasBeginningButton() const {
return _beginningButton;
}
void setMinDate(QDate date);
void setMaxDate(QDate date);
@@ -55,6 +61,9 @@ public:
bool isEnabled(int index) const {
return (index >= _minDayIndex) && (index <= _maxDayIndex);
}
bool atBeginning() const {
return _highlighted == _min;
}
const base::Variable<QDate> &month() {
return _month;
@@ -69,6 +78,8 @@ private:
static int daysShiftForMonth(QDate month);
static int rowsCountForMonth(QDate month);
bool _beginningButton = false;
base::Variable<QDate> _month;
QDate _min, _max;
QDate _highlighted;
@@ -86,6 +97,10 @@ CalendarBox::Context::Context(QDate month, QDate highlighted) : _highlighted(hig
showMonth(month);
}
void CalendarBox::Context::setBeginningButton(bool enabled) {
_beginningButton = enabled;
}
void CalendarBox::Context::setMinDate(QDate date) {
_min = date;
applyMonth(_month.value(), true);
@@ -192,6 +207,7 @@ public:
int countHeight();
void setDateChosenCallback(Fn<void(QDate)> callback);
void selectBeginning();
~Inner();
@@ -243,7 +259,7 @@ void CalendarBox::Inner::monthChanged(QDate month) {
_ripples.clear();
resizeToCurrent();
update();
sendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton);
Ui::SendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton);
}
void CalendarBox::Inner::resizeToCurrent() {
@@ -389,7 +405,10 @@ void CalendarBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
auto pressed = _pressed;
setPressed(kEmptySelection);
if (pressed != kEmptySelection && pressed == _selected) {
_dateChosenCallback(_context->dateFromIndex(pressed));
crl::on_main(this, [=] {
const auto onstack = _dateChosenCallback;
onstack(_context->dateFromIndex(pressed));
});
}
}
@@ -417,6 +436,10 @@ void CalendarBox::Inner::setDateChosenCallback(Fn<void(QDate)> callback) {
_dateChosenCallback = std::move(callback);
}
void CalendarBox::Inner::selectBeginning() {
_dateChosenCallback(_context->dateFromIndex(_context->minDayIndex()));
}
CalendarBox::Inner::~Inner() = default;
class CalendarBox::Title : public TWidget, private base::Subscriber {
@@ -494,6 +517,14 @@ void CalendarBox::setMaxDate(QDate date) {
_context->setMaxDate(date);
}
bool CalendarBox::hasBeginningButton() const {
return _context->hasBeginningButton();
}
void CalendarBox::setBeginningButton(bool enabled) {
_context->setBeginningButton(enabled);
}
void CalendarBox::prepare() {
_previous->setClickedCallback([this] { goPreviousMonth(); });
_next->setClickedCallback([this] { goNextMonth(); });
@@ -510,6 +541,9 @@ void CalendarBox::prepare() {
if (_finalize) {
_finalize(this);
}
if (!_context->atBeginning() && hasBeginningButton()) {
addLeftButton(tr::lng_calendar_beginning(), [this] { _inner->selectBeginning(); });
}
}
bool CalendarBox::isPreviousEnabled() const {
@@ -555,6 +589,8 @@ void CalendarBox::resizeEvent(QResizeEvent *e) {
void CalendarBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
e->ignore();
} else if (e->key() == Qt::Key_Home) {
_inner->selectBeginning();
} else if (e->key() == Qt::Key_Left) {
goPreviousMonth();
} else if (e->key() == Qt::Key_Right) {

View File

@@ -17,7 +17,7 @@ namespace Ui {
class IconButton;
} // namespace Ui
class CalendarBox : public BoxContent {
class CalendarBox : public BoxContent, private base::Subscriber {
public:
CalendarBox(
QWidget*,
@@ -33,6 +33,8 @@ public:
FnMut<void(not_null<CalendarBox*>)> finalize,
const style::CalendarSizes &st);
void setBeginningButton(bool enabled);
bool hasBeginningButton() const;
void setMinDate(QDate date);
void setMaxDate(QDate date);

View File

@@ -13,10 +13,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/fade_wrap.h"
#include "ui/toast/toast.h"
#include "ui/text/text_utilities.h"
#include "ui/special_fields.h"
#include "boxes/confirm_phone_box.h"
#include "boxes/confirm_box.h"
#include "auth_session.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "app.h"
#include "styles/style_boxes.h"
namespace {
@@ -60,8 +63,7 @@ void createErrorLabel(
class ChangePhoneBox::EnterPhone : public BoxContent {
public:
EnterPhone(QWidget*) {
}
EnterPhone(QWidget*, not_null<Main::Session*> session);
void setInnerFocus() override {
_phone->setFocusFast();
@@ -79,6 +81,8 @@ private:
showError(QString());
}
const not_null<Main::Session*> _session;
object_ptr<Ui::PhoneInput> _phone = { nullptr };
object_ptr<Ui::FadeWrap<Ui::FlatLabel>> _error = { nullptr };
mtpRequestId _requestId = 0;
@@ -87,7 +91,13 @@ private:
class ChangePhoneBox::EnterCode : public BoxContent {
public:
EnterCode(QWidget*, const QString &phone, const QString &hash, int codeLength, int callTimeout);
EnterCode(
QWidget*,
not_null<Main::Session*> session,
const QString &phone,
const QString &hash,
int codeLength,
int callTimeout);
void setInnerFocus() override {
_code->setFocusFast();
@@ -107,6 +117,8 @@ private:
}
int countHeight();
const not_null<Main::Session*> _session;
QString _phone;
QString _hash;
int _codeLength = 0;
@@ -119,11 +131,22 @@ private:
};
ChangePhoneBox::EnterPhone::EnterPhone(
QWidget*,
not_null<Main::Session*> session)
: _session(session) {
}
void ChangePhoneBox::EnterPhone::prepare() {
setTitle(tr::lng_change_phone_title());
auto phoneValue = QString();
_phone.create(this, st::defaultInputField, tr::lng_change_phone_new_title(), phoneValue);
_phone.create(
this,
st::defaultInputField,
tr::lng_change_phone_new_title(),
ExtractPhonePrefix(_session->user()->phone()),
phoneValue);
_phone->resize(st::boxWidth - 2 * st::boxPadding.left(), _phone->height());
_phone->moveToLeft(st::boxPadding.left(), st::boxLittleSkip);
@@ -149,9 +172,7 @@ void ChangePhoneBox::EnterPhone::submit() {
_requestId = MTP::send(
MTPaccount_SendChangePhoneCode(
MTP_string(phoneNumber),
MTP_codeSettings(
MTP_flags(0),
MTPstring())),
MTP_codeSettings(MTP_flags(0))),
rpcDone(crl::guard(this, [=](
const MTPauth_SentCode &result) {
return sendPhoneDone(phoneNumber, result);
@@ -167,25 +188,28 @@ void ChangePhoneBox::EnterPhone::sendPhoneDone(const QString &phoneNumber, const
auto codeLength = 0;
auto &data = result.c_auth_sentCode();
switch (data.vtype.type()) {
switch (data.vtype().type()) {
case mtpc_auth_sentCodeTypeApp:
LOG(("Error: should not be in-app code!"));
showError(Lang::Hard::ServerError());
return;
case mtpc_auth_sentCodeTypeSms: codeLength = data.vtype.c_auth_sentCodeTypeSms().vlength.v; break;
case mtpc_auth_sentCodeTypeCall: codeLength = data.vtype.c_auth_sentCodeTypeCall().vlength.v; break;
case mtpc_auth_sentCodeTypeSms: codeLength = data.vtype().c_auth_sentCodeTypeSms().vlength().v; break;
case mtpc_auth_sentCodeTypeCall: codeLength = data.vtype().c_auth_sentCodeTypeCall().vlength().v; break;
case mtpc_auth_sentCodeTypeFlashCall:
LOG(("Error: should not be flashcall!"));
showError(Lang::Hard::ServerError());
return;
}
auto phoneCodeHash = qs(data.vphone_code_hash);
auto phoneCodeHash = qs(data.vphone_code_hash());
auto callTimeout = 0;
if (data.has_next_type() && data.vnext_type.type() == mtpc_auth_codeTypeCall) {
callTimeout = data.has_timeout() ? data.vtimeout.v : 60;
if (const auto nextType = data.vnext_type()) {
if (nextType->type() == mtpc_auth_codeTypeCall) {
callTimeout = data.vtimeout().value_or(60);
}
}
Ui::show(
Box<EnterCode>(
_session,
phoneNumber,
phoneCodeHash,
codeLength,
@@ -227,8 +251,15 @@ void ChangePhoneBox::EnterPhone::showError(const QString &text) {
}
}
ChangePhoneBox::EnterCode::EnterCode(QWidget*, const QString &phone, const QString &hash, int codeLength, int callTimeout)
: _phone(phone)
ChangePhoneBox::EnterCode::EnterCode(
QWidget*,
not_null<Main::Session*> session,
const QString &phone,
const QString &hash,
int codeLength,
int callTimeout)
: _session(session)
, _phone(phone)
, _hash(hash)
, _codeLength(codeLength)
, _callTimeout(callTimeout)
@@ -277,13 +308,15 @@ void ChangePhoneBox::EnterCode::submit() {
}
hideError();
const auto session = _session;
const auto code = _code->getDigitsOnly();
const auto weak = Ui::MakeWeak(this);
_requestId = MTP::send(MTPaccount_ChangePhone(
MTP_string(_phone),
MTP_string(_hash),
MTP_string(code)
), rpcDone([weak = make_weak(this)](const MTPUser &result) {
Auth().data().processUser(result);
), rpcDone([=](const MTPUser &result) {
session->data().processUser(result);
if (weak) {
Ui::hideLayer();
}
@@ -340,11 +373,17 @@ bool ChangePhoneBox::EnterCode::sendCodeFail(const RPCError &error) {
return true;
}
ChangePhoneBox::ChangePhoneBox(QWidget*, not_null<Main::Session*> session)
: _session(session) {
}
void ChangePhoneBox::prepare() {
const auto session = _session;
setTitle(tr::lng_change_phone_title());
addButton(tr::lng_change_phone_button(), [] {
Ui::show(Box<ConfirmBox>(tr::lng_change_phone_warning(tr::now), [] {
Ui::show(Box<EnterPhone>());
addButton(tr::lng_change_phone_button(), [=] {
Ui::show(Box<ConfirmBox>(tr::lng_change_phone_warning(tr::now), [=] {
Ui::show(Box<EnterPhone>(session));
}));
});
addButton(tr::lng_cancel(), [this] {

View File

@@ -9,10 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
namespace Main {
class Session;
} // namespace Main
class ChangePhoneBox : public BoxContent {
public:
ChangePhoneBox(QWidget*) {
}
ChangePhoneBox(QWidget*, not_null<Main::Session*> session);
protected:
void prepare() override;
@@ -23,5 +26,7 @@ private:
class EnterPhone;
class EnterCode;
const not_null<Main::Session*> _session;
};

View File

@@ -25,13 +25,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/click_handler_types.h"
#include "window/window_session_controller.h"
#include "storage/localstorage.h"
#include "data/data_scheduled_messages.h"
#include "data/data_session.h"
#include "data/data_photo.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "auth_session.h"
#include "base/unixtime.h"
#include "main/main_session.h"
#include "observer_peer.h"
#include "facades.h"
#include "app.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
TextParseOptions _confirmBoxTextOptions = {
TextParseLinks | TextParseMultiline | TextParseMarkdown | TextParseRichText, // flags
@@ -247,8 +254,9 @@ void ConfirmBox::mouseReleaseEvent(QMouseEvent *e) {
_lastMousePos = e->globalPos();
updateHover();
if (const auto activated = ClickHandler::unpressed()) {
const auto guard = window();
Ui::hideLayer();
App::activateClickHandler(activated, e->button());
ActivateClickHandler(guard, activated, e->button());
return;
}
BoxContent::mouseReleaseEvent(e);
@@ -458,7 +466,8 @@ DeleteMessagesBox::DeleteMessagesBox(
QWidget*,
not_null<HistoryItem*> item,
bool suggestModerateActions)
: _ids(1, item->fullId()) {
: _session(&item->history()->session())
, _ids(1, item->fullId()) {
if (suggestModerateActions) {
_moderateBan = item->suggestBanReport();
_moderateDeleteAll = item->suggestDeleteAllReport();
@@ -471,8 +480,10 @@ DeleteMessagesBox::DeleteMessagesBox(
DeleteMessagesBox::DeleteMessagesBox(
QWidget*,
not_null<Main::Session*> session,
MessageIdsList &&selected)
: _ids(std::move(selected)) {
: _session(session)
, _ids(std::move(selected)) {
Expects(!_ids.empty());
}
@@ -480,7 +491,8 @@ DeleteMessagesBox::DeleteMessagesBox(
QWidget*,
not_null<PeerData*> peer,
bool justClear)
: _wipeHistoryPeer(peer)
: _session(&peer->session())
, _wipeHistoryPeer(peer)
, _wipeHistoryJustClear(justClear) {
}
@@ -537,10 +549,11 @@ void DeleteMessagesBox::prepare() {
: tr::lng_selected_delete_sure(tr::now, lt_count, _ids.size());
if (const auto peer = checkFromSinglePeer()) {
auto count = int(_ids.size());
if (auto revoke = revokeText(peer)) {
if (hasScheduledMessages()) {
} else if (auto revoke = revokeText(peer)) {
_revoke.create(this, revoke->checkbox, false, st::defaultBoxCheckbox);
appendDetails(std::move(revoke->description));
} else if (peer && peer->isChannel()) {
} else if (peer->isChannel()) {
if (peer->isMegagroup()) {
appendDetails({ tr::lng_delete_for_everyone_hint(tr::now, lt_count, count) });
}
@@ -575,10 +588,21 @@ void DeleteMessagesBox::prepare() {
setDimensions(st::boxWidth, fullHeight);
}
bool DeleteMessagesBox::hasScheduledMessages() const {
for (const auto fullId : std::as_const(_ids)) {
if (const auto item = _session->data().message(fullId)) {
if (item->isScheduled()) {
return true;
}
}
}
return false;
}
PeerData *DeleteMessagesBox::checkFromSinglePeer() const {
auto result = (PeerData*)nullptr;
for (const auto fullId : std::as_const(_ids)) {
if (const auto item = Auth().data().message(fullId)) {
if (const auto item = _session->data().message(fullId)) {
const auto peer = item->history()->peer;
if (!result) {
result = peer;
@@ -620,7 +644,7 @@ auto DeleteMessagesBox::revokeText(not_null<PeerData*> peer) const
return std::nullopt;
}
const auto now = unixtime();
const auto now = base::unixtime::now();
const auto canRevoke = [&](HistoryItem * item) {
return item->canDeleteForEveryone(now);
};
@@ -760,9 +784,21 @@ void DeleteMessagesBox::deleteAndClear() {
}
base::flat_map<not_null<PeerData*>, QVector<MTPint>> idsByPeer;
base::flat_map<not_null<PeerData*>, QVector<MTPint>> scheduledIdsByPeer;
for (const auto itemId : _ids) {
if (const auto item = Auth().data().message(itemId)) {
if (const auto item = _session->data().message(itemId)) {
const auto history = item->history();
if (item->isScheduled()) {
const auto wasOnServer = !item->isSending()
&& !item->hasFailed();
if (wasOnServer) {
scheduledIdsByPeer[history->peer].push_back(MTP_int(
_session->data().scheduledMessages().lookupId(item)));
} else {
_session->data().scheduledMessages().removeSending(item);
}
continue;
}
const auto wasOnServer = IsServerMsgId(item->id);
const auto wasLast = (history->lastMessage() == item);
const auto wasInChats = (history->chatListMessage() == item);
@@ -779,21 +815,33 @@ void DeleteMessagesBox::deleteAndClear() {
for (const auto &[peer, ids] : idsByPeer) {
peer->session().api().deleteMessages(peer, ids, revoke);
}
for (const auto &[peer, ids] : scheduledIdsByPeer) {
peer->session().api().request(MTPmessages_DeleteScheduledMessages(
peer->input,
MTP_vector<MTPint>(ids)
)).done([=, peer=peer](const MTPUpdates &updates) {
peer->session().api().applyUpdates(updates);
}).send();
}
const auto session = _session;
Ui::hideLayer();
Auth().data().sendHistoryChangeNotifications();
session->data().sendHistoryChangeNotifications();
}
ConfirmInviteBox::ConfirmInviteBox(
QWidget*,
not_null<Main::Session*> session,
const MTPDchatInvite &data,
Fn<void()> submit)
: _submit(std::move(submit))
: _session(session)
, _submit(std::move(submit))
, _title(this, st::confirmInviteTitle)
, _status(this, st::confirmInviteStatus)
, _participants(GetParticipants(data))
, _participants(GetParticipants(_session, data))
, _isChannel(data.is_channel() && !data.is_megagroup()) {
const auto title = qs(data.vtitle);
const auto count = data.vparticipants_count.v;
const auto title = qs(data.vtitle());
const auto count = data.vparticipants_count().v;
const auto status = [&] {
return (!_participants.empty() && _participants.size() < count)
? tr::lng_group_invite_members(tr::now, lt_count, count)
@@ -806,11 +854,11 @@ ConfirmInviteBox::ConfirmInviteBox(
_title->setText(title);
_status->setText(status);
const auto photo = Auth().data().processPhoto(data.vphoto);
const auto photo = _session->data().processPhoto(data.vphoto());
if (!photo->isNull()) {
_photo = photo->thumbnail();
if (!_photo->loaded()) {
subscribe(Auth().downloaderTaskFinished(), [=] {
subscribe(_session->downloaderTaskFinished(), [=] {
update();
});
_photo->load(Data::FileOrigin());
@@ -823,15 +871,17 @@ ConfirmInviteBox::ConfirmInviteBox(
}
std::vector<not_null<UserData*>> ConfirmInviteBox::GetParticipants(
not_null<Main::Session*> session,
const MTPDchatInvite &data) {
if (!data.has_participants()) {
const auto participants = data.vparticipants();
if (!participants) {
return {};
}
const auto &v = data.vparticipants.v;
const auto &v = participants->v;
auto result = std::vector<not_null<UserData*>>();
result.reserve(v.size());
for (const auto &participant : v) {
if (const auto user = Auth().data().processUser(participant)) {
if (const auto user = session->data().processUser(participant)) {
result.push_back(user);
}
}
@@ -860,7 +910,9 @@ void ConfirmInviteBox::prepare() {
for (const auto user : _participants) {
auto name = new Ui::FlatLabel(this, st::confirmInviteUserName);
name->resizeToWidth(st::confirmInviteUserPhotoSize + padding);
name->setText(user->firstName.isEmpty() ? App::peerName(user) : user->firstName);
name->setText(user->firstName.isEmpty()
? user->name
: user->firstName);
name->moveToLeft(left + (padding / 2), st::confirmInviteUserNameTop);
left += _userWidth;
}

View File

@@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
namespace Main {
class Session;
} // namespace Main
namespace Ui {
class Checkbox;
class FlatLabel;
@@ -91,7 +95,7 @@ public:
};
class MaxInviteBox : public BoxContent {
class MaxInviteBox : public BoxContent, private base::Subscriber {
public:
MaxInviteBox(QWidget*, not_null<ChannelData*> channel);
@@ -150,7 +154,10 @@ public:
QWidget*,
not_null<HistoryItem*> item,
bool suggestModerateActions);
DeleteMessagesBox(QWidget*, MessageIdsList &&selected);
DeleteMessagesBox(
QWidget*,
not_null<Main::Session*> session,
MessageIdsList &&selected);
DeleteMessagesBox(QWidget*, not_null<PeerData*> peer, bool justClear);
void setDeleteConfirmedCallback(Fn<void()> callback) {
@@ -169,8 +176,12 @@ private:
TextWithEntities description;
};
void deleteAndClear();
PeerData *checkFromSinglePeer() const;
std::optional<RevokeConfig> revokeText(not_null<PeerData*> peer) const;
[[nodiscard]] PeerData *checkFromSinglePeer() const;
[[nodiscard]] bool hasScheduledMessages() const;
[[nodiscard]] std::optional<RevokeConfig> revokeText(
not_null<PeerData*> peer) const;
const not_null<Main::Session*> _session;
PeerData * const _wipeHistoryPeer = nullptr;
const bool _wipeHistoryJustClear = false;
@@ -190,10 +201,14 @@ private:
};
class ConfirmInviteBox : public BoxContent, public RPCSender {
class ConfirmInviteBox
: public BoxContent
, public RPCSender
, private base::Subscriber {
public:
ConfirmInviteBox(
QWidget*,
not_null<Main::Session*> session,
const MTPDchatInvite &data,
Fn<void()> submit);
~ConfirmInviteBox();
@@ -206,8 +221,11 @@ protected:
private:
static std::vector<not_null<UserData*>> GetParticipants(
not_null<Main::Session*> session,
const MTPDchatInvite &data);
const not_null<Main::Session*> _session;
Fn<void()> _submit;
object_ptr<Ui::FlatLabel> _title;
object_ptr<Ui::FlatLabel> _status;

View File

@@ -17,6 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/qthelp_url.h" // qthelp::url_encode
#include "platform/platform_info.h" // Platform::SystemVersionPretty
#include "mainwidget.h"
#include "numbers.h"
#include "app.h"
#include "lang/lang_keys.h"
namespace {
@@ -64,7 +66,14 @@ void ShowPhoneBannedError(const QString &phone) {
tr::lng_signin_banned_help(tr::now),
close,
[=] { SendToBannedHelp(phone); close(); }));
}
QString ExtractPhonePrefix(const QString &phone) {
const auto pattern = phoneNumberParse(phone);
if (!pattern.isEmpty()) {
return phone.mid(0, pattern[0]);
}
return QString();
}
SentCodeField::SentCodeField(
@@ -219,29 +228,33 @@ void ConfirmPhoneBox::checkPhoneAndHash() {
_sendCodeRequestId = MTP::send(
MTPaccount_SendConfirmPhoneCode(
MTP_string(_hash),
MTP_codeSettings(
MTP_flags(0),
MTPstring())),
MTP_codeSettings(MTP_flags(0))),
rpcDone(&ConfirmPhoneBox::sendCodeDone),
rpcFail(&ConfirmPhoneBox::sendCodeFail));
}
void ConfirmPhoneBox::sendCodeDone(const MTPauth_SentCode &result) {
Expects(result.type() == mtpc_auth_sentCode);
_sendCodeRequestId = 0;
auto &resultInner = result.c_auth_sentCode();
switch (resultInner.vtype.type()) {
case mtpc_auth_sentCodeTypeApp: LOG(("Error: should not be in-app code!")); break;
case mtpc_auth_sentCodeTypeSms: _sentCodeLength = resultInner.vtype.c_auth_sentCodeTypeSms().vlength.v; break;
case mtpc_auth_sentCodeTypeCall: _sentCodeLength = resultInner.vtype.c_auth_sentCodeTypeCall().vlength.v; break;
case mtpc_auth_sentCodeTypeFlashCall: LOG(("Error: should not be flashcall!")); break;
}
_phoneHash = qs(resultInner.vphone_code_hash);
if (resultInner.has_next_type() && resultInner.vnext_type.type() == mtpc_auth_codeTypeCall) {
_call.setStatus({ SentCodeCall::State::Waiting, resultInner.has_timeout() ? resultInner.vtimeout.v : 60 });
}
launch();
result.match([&](const MTPDauth_sentCode &data) {
_sendCodeRequestId = 0;
_sentCodeLength = data.vtype().match([&](const MTPDauth_sentCodeTypeApp &data) {
LOG(("Error: should not be in-app code!"));
return 0;
}, [&](const MTPDauth_sentCodeTypeSms &data) {
return data.vlength().v;
}, [&](const MTPDauth_sentCodeTypeCall &data) {
return data.vlength().v;
}, [&](const MTPDauth_sentCodeTypeFlashCall &data) {
LOG(("Error: should not be flashcall!"));
return 0;
});
_phoneHash = qs(data.vphone_code_hash());
if (const auto nextType = data.vnext_type()) {
if (nextType->type() == mtpc_auth_codeTypeCall) {
_call.setStatus({ SentCodeCall::State::Waiting, data.vtimeout().value_or(60) });
}
}
launch();
});
}
bool ConfirmPhoneBox::sendCodeFail(const RPCError &error) {

View File

@@ -17,6 +17,7 @@ class FlatLabel;
} // namespace Ui
void ShowPhoneBannedError(const QString &phone);
[[nodiscard]] QString ExtractPhonePrefix(const QString &phone);
class SentCodeField : public Ui::InputField {
public:

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localstorage.h"
#include "base/qthelp_url.h"
#include "core/application.h"
#include "main/main_account.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
@@ -23,14 +24,69 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animations.h"
#include "ui/effects/radial_animation.h"
#include "ui/text_options.h"
#include "facades.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_info.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
namespace {
constexpr auto kSaveSettingsDelayedTimeout = crl::time(1000);
class Base64UrlInput : public Ui::MaskedInputField {
public:
Base64UrlInput(
QWidget *parent,
const style::InputField &st,
rpl::producer<QString> placeholder,
const QString &val);
protected:
void correctValue(
const QString &was,
int wasCursor,
QString &now,
int &nowCursor) override;
};
Base64UrlInput::Base64UrlInput(
QWidget *parent,
const style::InputField &st,
rpl::producer<QString> placeholder,
const QString &val)
: MaskedInputField(parent, st, std::move(placeholder), val) {
if (!QRegularExpression("^[a-zA-Z0-9_\\-]+$").match(val).hasMatch()) {
setText(QString());
}
}
void Base64UrlInput::correctValue(
const QString &was,
int wasCursor,
QString &now,
int &nowCursor) {
QString newText;
newText.reserve(now.size());
auto newPos = nowCursor;
for (auto i = 0, l = now.size(); i < l; ++i) {
const auto ch = now[i];
if ((ch >= '0' && ch <= '9')
|| (ch >= 'a' && ch <= 'z')
|| (ch >= 'A' && ch <= 'Z')
|| (ch == '-')
|| (ch == '_')) {
newText.append(ch);
} else if (i < nowCursor) {
--newPos;
}
}
setCorrectedText(now, nowCursor, newText, newPos);
}
class ProxyRow : public Ui::RippleButton {
public:
using View = ProxiesBoxController::ItemView;
@@ -150,7 +206,7 @@ private:
QPointer<Ui::PortInput> _port;
QPointer<Ui::InputField> _user;
QPointer<Ui::PasswordInput> _password;
QPointer<Ui::HexInput> _secret;
QPointer<Base64UrlInput> _secret;
QPointer<Ui::SlideWrap<Ui::VerticalLayout>> _credentials;
QPointer<Ui::SlideWrap<Ui::VerticalLayout>> _mtprotoCredentials;
@@ -359,7 +415,7 @@ void ProxyRow::paintCheck(Painter &p) {
pen.setCapStyle(Qt::RoundCap);
p.setPen(pen);
p.setBrush(_st->bg);
const auto rect = rtlrect(QRectF(left, top, _st->diameter, _st->diameter).marginsRemoved(QMarginsF(_st->thickness / 2., _st->thickness / 2., _st->thickness / 2., _st->thickness / 2.)), outerWidth);
const auto rect = style::rtlrect(QRectF(left, top, _st->diameter, _st->diameter).marginsRemoved(QMarginsF(_st->thickness / 2., _st->thickness / 2., _st->thickness / 2., _st->thickness / 2.)), outerWidth);
if (_progress && loading.shown > 0 && anim::Disabled()) {
anim::DrawStaticLoading(
p,
@@ -378,7 +434,7 @@ void ProxyRow::paintCheck(Painter &p) {
p.setBrush(anim::brush(_st->untoggledFg, _st->toggledFg, toggled * set));
auto skip0 = _st->diameter / 2., skip1 = _st->skip / 10., checkSkip = skip0 * (1. - toggled) + skip1 * toggled;
p.drawEllipse(rtlrect(QRectF(left, top, _st->diameter, _st->diameter).marginsRemoved(QMarginsF(checkSkip, checkSkip, checkSkip, checkSkip)), outerWidth));
p.drawEllipse(style::rtlrect(QRectF(left, top, _st->diameter, _st->diameter).marginsRemoved(QMarginsF(checkSkip, checkSkip, checkSkip, checkSkip)), outerWidth));
}
}
@@ -852,12 +908,11 @@ void ProxyBox::setupMtprotoCredentials(const ProxyData &data) {
addLabel(mtproto, tr::lng_proxy_credentials(tr::now));
auto secretWrap = object_ptr<Ui::RpWidget>(mtproto);
_secret = Ui::CreateChild<Ui::HexInput>(
_secret = Ui::CreateChild<Base64UrlInput>(
secretWrap.data(),
st::connectionUserInputField,
tr::lng_connection_proxy_secret_ph(),
(data.type == Type::Mtproto) ? data.password : QString());
_secret->setMaxLength(ProxyData::MaxMtprotoPasswordLength());
_secret->move(0, 0);
_secret->heightValue(
) | rpl::start_with_next([=, wrap = secretWrap.data()](int height) {
@@ -975,6 +1030,11 @@ void ProxiesBoxController::ShowApplyConfirmation(
strong->closeBox();
}
}), LayerOption::KeepOther);
} else {
Ui::show(Box<InformBox>(
(proxy.status() == ProxyData::Status::Unsupported
? tr::lng_proxy_unsupported(tr::now)
: tr::lng_proxy_invalid(tr::now))));
}
}
@@ -990,58 +1050,55 @@ void ProxiesBoxController::refreshChecker(Item &item) {
const auto type = (item.data.type == Type::Http)
? Variants::Http
: Variants::Tcp;
const auto mtproto = Core::App().mtp();
const auto mtproto = Core::App().activeAccount().mtp();
const auto dcId = mtproto->mainDcId();
item.state = ItemState::Checking;
const auto setup = [&](Checker &checker) {
const auto setup = [&](Checker &checker, const bytes::vector &secret) {
checker = MTP::internal::AbstractConnection::Create(
mtproto,
type,
QThread::currentThread(),
secret,
item.data);
setupChecker(item.id, checker);
};
setup(item.checker);
if (item.data.type == Type::Mtproto) {
item.checkerv6 = nullptr;
const auto secret = item.data.secretFromMtprotoPassword();
setup(item.checker, secret);
item.checker->connectToServer(
item.data.host,
item.data.port,
item.data.secretFromMtprotoPassword(),
secret,
dcId);
item.checkerv6 = nullptr;
} else {
const auto options = mtproto->dcOptions()->lookup(
dcId,
MTP::DcType::Regular,
true);
const auto endpoint = options.data[Variants::IPv4][type];
const auto endpointv6 = options.data[Variants::IPv6][type];
if (endpoint.empty()) {
item.checker = nullptr;
}
if (Global::TryIPv6() && !endpointv6.empty()) {
setup(item.checkerv6);
} else {
item.checkerv6 = nullptr;
}
const auto connect = [&](
Checker &checker,
Variants::Address address) {
const auto &list = options.data[address][type];
if (list.empty()
|| (address == Variants::IPv6 && !Global::TryIPv6())) {
checker = nullptr;
return;
}
const auto &endpoint = list.front();
setup(checker, endpoint.secret);
checker->connectToServer(
QString::fromStdString(endpoint.ip),
endpoint.port,
endpoint.secret,
dcId);
};
connect(item.checker, Variants::IPv4);
connect(item.checkerv6, Variants::IPv6);
if (!item.checker && !item.checkerv6) {
item.state = ItemState::Unavailable;
return;
}
const auto connect = [&](
const Checker &checker,
const std::vector<MTP::DcOptions::Endpoint> &endpoints) {
if (checker) {
checker->connectToServer(
QString::fromStdString(endpoints.front().ip),
endpoints.front().port,
endpoints.front().secret,
dcId);
}
};
connect(item.checker, endpoint);
connect(item.checkerv6, endpointv6);
}
}
@@ -1373,7 +1430,7 @@ void ProxiesBoxController::share(const ProxyData &proxy) {
? "&pass=" + qthelp::url_encode(proxy.password) : "")
+ ((proxy.type == Type::Mtproto && !proxy.password.isEmpty())
? "&secret=" + proxy.password : "");
QApplication::clipboard()->setText(link);
QGuiApplication::clipboard()->setText(link);
Ui::Toast::Show(tr::lng_username_copied(tr::now));
}
@@ -1381,7 +1438,7 @@ ProxiesBoxController::~ProxiesBoxController() {
if (_saveTimer.isActive()) {
App::CallDelayed(
kSaveSettingsDelayedTimeout,
QApplication::instance(),
QCoreApplication::instance(),
[] { Local::writeSettings(); });
}
}

View File

@@ -16,10 +16,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/shadow.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "main/main_session.h"
#include "core/event_filter.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/message_field.h"
#include "history/view/history_view_schedule_box.h"
#include "settings/settings_common.h"
#include "base/unique_qptr.h"
#include "facades.h"
#include "styles/style_boxes.h"
#include "styles/style_settings.h"
@@ -36,7 +40,8 @@ class Options {
public:
Options(
not_null<QWidget*> outer,
not_null<Ui::VerticalLayout*> container);
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session);
[[nodiscard]] bool isValid() const;
[[nodiscard]] rpl::producer<bool> isValidChanged() const;
@@ -53,6 +58,7 @@ private:
static Option Create(
not_null<QWidget*> outer,
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session,
int position);
void toggleRemoveAlways(bool toggled);
@@ -122,6 +128,7 @@ private:
not_null<QWidget*> _outer;
not_null<Ui::VerticalLayout*> _container;
const not_null<Main::Session*> _session;
int _position = 0;
std::vector<Option> _list;
std::set<Option, std::less<>> _destroyed;
@@ -134,12 +141,18 @@ private:
void InitField(
not_null<QWidget*> container,
not_null<Ui::InputField*> field) {
not_null<Ui::InputField*> field,
not_null<Main::Session*> session) {
field->setInstantReplaces(Ui::InstantReplaces::Default());
field->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
field->setInstantReplacesEnabled(
session->settings().replaceEmojiValue());
auto options = Ui::Emoji::SuggestionsController::Options();
options.suggestExactFirstWord = false;
Ui::Emoji::SuggestionsController::Init(container, field, options);
Ui::Emoji::SuggestionsController::Init(
container,
field,
session,
options);
}
not_null<Ui::FlatLabel*> CreateWarningLabel(
@@ -176,6 +189,7 @@ void FocusAtEnd(not_null<Ui::InputField*> field) {
Options::Option Options::Option::Create(
not_null<QWidget*> outer,
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session,
int position) {
auto result = Option();
const auto field = container->insert(
@@ -187,7 +201,7 @@ Options::Option Options::Option::Create(
st::createPollOptionField,
Ui::InputField::Mode::NoNewlines,
tr::lng_polls_create_option_add())));
InitField(outer, field->entity());
InitField(outer, field->entity(), session);
field->entity()->setMaxLength(kOptionLimit + kErrorLimit);
result._field.reset(field);
@@ -341,9 +355,11 @@ rpl::producer<Qt::MouseButton> Options::Option::removeClicks() const {
Options::Options(
not_null<QWidget*> outer,
not_null<Ui::VerticalLayout*> container)
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session)
: _outer(outer)
, _container(container)
, _session(session)
, _position(_container->count()) {
checkLastOption();
}
@@ -488,6 +504,7 @@ void Options::addEmptyOption() {
_list.push_back(Option::Create(
_outer,
_container,
_session,
_position + _list.size() + _destroyed.size()));
const auto field = _list.back().field();
QObject::connect(field, &Ui::InputField::submitted, [=] {
@@ -507,11 +524,11 @@ void Options::addEmptyOption() {
Core::InstallEventFilter(field, [=](not_null<QEvent*> event) {
if (event->type() != QEvent::KeyPress
|| !field->getLastText().isEmpty()) {
return false;
return Core::EventFilter::Result::Continue;
}
const auto key = static_cast<QKeyEvent*>(event.get())->key();
if (key != Qt::Key_Backspace) {
return false;
return Core::EventFilter::Result::Continue;
}
const auto index = findField(field);
@@ -520,7 +537,7 @@ void Options::addEmptyOption() {
} else {
_backspaceInFront.fire({});
}
return true;
return Core::EventFilter::Result::Cancel;
});
_list.back().removeClicks(
@@ -578,10 +595,15 @@ void Options::checkLastOption() {
} // namespace
CreatePollBox::CreatePollBox(QWidget*) {
CreatePollBox::CreatePollBox(
QWidget*,
not_null<Main::Session*> session,
Api::SendType sendType)
: _session(session)
, _sendType(sendType) {
}
rpl::producer<PollData> CreatePollBox::submitRequests() const {
rpl::producer<CreatePollBox::Result> CreatePollBox::submitRequests() const {
return _submitRequests.events();
}
@@ -605,7 +627,7 @@ not_null<Ui::InputField*> CreatePollBox::setupQuestion(
Ui::InputField::Mode::MultiLine,
tr::lng_polls_create_question_placeholder()),
st::createPollFieldPadding);
InitField(getDelegate()->outerContainer(), question);
InitField(getDelegate()->outerContainer(), question, _session);
question->setMaxLength(kQuestionLimit + kErrorLimit);
const auto warning = CreateWarningLabel(
@@ -648,7 +670,8 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
AddSubsectionTitle(container, tr::lng_polls_create_options());
const auto options = lifetime().make_state<Options>(
getDelegate()->outerContainer(),
container);
container,
_session);
auto limit = options->usedCount() | rpl::after_next([=](int count) {
setCloseByEscape(!count);
setCloseByOutsideClick(!count);
@@ -687,6 +710,22 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
result.answers = options->toPollAnswers();
return result;
};
const auto send = [=](Api::SendOptions options) {
_submitRequests.fire({ collectResult(), options });
};
const auto sendSilent = [=] {
auto options = Api::SendOptions();
options.silent = true;
send(options);
};
const auto sendScheduled = [=] {
Ui::show(
HistoryView::PrepareScheduleBox(
this,
SendMenuType::Scheduled,
send),
LayerOption::KeepOther);
};
const auto updateValid = [=] {
valid->fire(isValidQuestion() && options->isValid());
};
@@ -699,9 +738,16 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
) | rpl::start_with_next([=](bool valid) {
clearButtons();
if (valid) {
addButton(
const auto submit = addButton(
tr::lng_polls_create_button(),
[=] { _submitRequests.fire(collectResult()); });
[=] { send({}); });
if (_sendType == Api::SendType::Normal) {
SetupSendMenu(
submit.data(),
[=] { return SendMenuType::Scheduled; },
sendSilent,
sendScheduled);
}
}
addButton(tr::lng_cancel(), [=] { closeBox(); });
}, lifetime());

View File

@@ -8,18 +8,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "boxes/abstract_box.h"
#include "api/api_common.h"
#include "data/data_poll.h"
struct PollData;
namespace Ui {
class VerticalLayout;
} // namespace Ui
struct PollData;
namespace Main {
class Session;
} // namespace Main
class CreatePollBox : public BoxContent {
public:
CreatePollBox(QWidget*);
struct Result {
PollData poll;
Api::SendOptions options;
};
rpl::producer<PollData> submitRequests() const;
CreatePollBox(
QWidget*,
not_null<Main::Session*> session,
Api::SendType sendType);
rpl::producer<Result> submitRequests() const;
void submitFailed(const QString &error);
void setInnerFocus() override;
@@ -32,8 +46,10 @@ private:
not_null<Ui::InputField*> setupQuestion(
not_null<Ui::VerticalLayout*> container);
const not_null<Main::Session*> _session;
const Api::SendType _sendType = Api::SendType();
Fn<void()> _setInnerFocus;
Fn<rpl::producer<bool>()> _dataIsValidValue;
rpl::event_stream<PollData> _submitRequests;
rpl::event_stream<Result> _submitRequests;
};

View File

@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "platform/platform_specific.h"
#include "facades.h"
#include "styles/style_boxes.h"
DownloadPathBox::DownloadPathBox(QWidget *parent)

View File

@@ -8,7 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/edit_caption_box.h"
#include "apiwrap.h"
#include "auth_session.h"
#include "api/api_text_entities.h"
#include "main/main_session.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/tabbed_panel.h"
@@ -21,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_photo.h"
#include "data/data_user.h"
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "history/history.h"
#include "history/history_item.h"
#include "lang/lang_keys.h"
@@ -31,12 +33,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_chat_helpers.h"
#include "styles/style_history.h"
#include "ui/image/image.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/checkbox.h"
#include "ui/special_buttons.h"
#include "ui/text_options.h"
#include "ui/widgets/input_fields.h"
#include "window/window_session_controller.h"
#include "ui/widgets/checkbox.h"
#include "confirm_box.h"
#include "facades.h"
#include "app.h"
#include <QtCore/QMimeData>
EditCaptionBox::EditCaptionBox(
QWidget*,
@@ -232,7 +239,7 @@ EditCaptionBox::EditCaptionBox(
_thumbnailImageLoaded = _thumbnailImage
? _thumbnailImage->loaded()
: true;
subscribe(Auth().downloaderTaskFinished(), [=] {
subscribe(_controller->session().downloaderTaskFinished(), [=] {
if (!_thumbnailImageLoaded
&& _thumbnailImage
&& _thumbnailImage->loaded()) {
@@ -254,9 +261,11 @@ EditCaptionBox::EditCaptionBox(
_field->setMaxLength(Global::CaptionLengthMax());
_field->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
_field->setInstantReplaces(Ui::InstantReplaces::Default());
_field->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
_field->setInstantReplacesEnabled(
_controller->session().settings().replaceEmojiValue());
_field->setMarkdownReplacesEnabled(rpl::single(true));
_field->setEditLinkCallback(DefaultEditLinkCallback(_field));
_field->setEditLinkCallback(
DefaultEditLinkCallback(&_controller->session(), _field));
auto r = object_ptr<Ui::SlideWrap<Ui::Checkbox>>(
this,
@@ -275,14 +284,13 @@ EditCaptionBox::EditCaptionBox(
}, _wayWrap->lifetime());
}
bool EditCaptionBox::emojiFilter(not_null<QEvent*> event) {
void EditCaptionBox::emojiFilterForGeometry(not_null<QEvent*> event) {
const auto type = event->type();
if (type == QEvent::Move || type == QEvent::Resize) {
// updateEmojiPanelGeometry uses not only container geometry, but
// also container children geometries that will be updated later.
crl::on_main(this, [=] { updateEmojiPanelGeometry(); });
}
return false;
}
void EditCaptionBox::updateEmojiPanelGeometry() {
@@ -627,7 +635,8 @@ void EditCaptionBox::prepare() {
});
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_field);
_field,
&_controller->session());
setupEmojiPanel();
@@ -708,14 +717,16 @@ void EditCaptionBox::setupEmojiPanel() {
st::emojiPanMinHeight / 2,
st::emojiPanMinHeight);
_emojiPanel->hide();
_emojiPanel->getSelector()->emojiChosen(
_emojiPanel->selector()->emojiChosen(
) | rpl::start_with_next([=](EmojiPtr emoji) {
Ui::InsertEmojiAtCursor(_field->textCursor(), emoji);
}, lifetime());
_emojiFilter.reset(Core::InstallEventFilter(
container,
[=](not_null<QEvent*> event) { return emojiFilter(event); }));
const auto filterCallback = [=](not_null<QEvent*> event) {
emojiFilterForGeometry(event);
return Core::EventFilter::Result::Continue;
};
_emojiFilter.reset(Core::InstallEventFilter(container, filterCallback));
_emojiToggle.create(this, st::boxAttachEmoji);
_emojiToggle->installEventFilter(_emojiPanel);
@@ -804,10 +815,10 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) {
// App::roundRect(p, x, y, w, h, st::msgInBg, MessageInCorners, &st::msgInShadow);
if (_thumbw) {
QRect rthumb(rtlrect(x + 0, y + 0, st::msgFileThumbSize, st::msgFileThumbSize, width()));
QRect rthumb(style::rtlrect(x + 0, y + 0, st::msgFileThumbSize, st::msgFileThumbSize, width()));
p.drawPixmap(rthumb.topLeft(), _thumb);
} else {
const QRect inner(rtlrect(x + 0, y + 0, st::msgFileSize, st::msgFileSize, width()));
const QRect inner(style::rtlrect(x + 0, y + 0, st::msgFileSize, st::msgFileSize, width()));
p.setPen(Qt::NoPen);
p.setBrush(st::msgFileInBg);
@@ -876,7 +887,7 @@ void EditCaptionBox::setInnerFocus() {
void EditCaptionBox::save() {
if (_saveRequestId) return;
const auto item = Auth().data().message(_msgId);
const auto item = _controller->session().data().message(_msgId);
if (!item) {
_error = tr::lng_edit_deleted(tr::now);
update();
@@ -890,17 +901,17 @@ void EditCaptionBox::save() {
const auto textWithTags = _field->getTextWithAppliedMarkdown();
auto sending = TextWithEntities{
textWithTags.text,
ConvertTextTagsToEntities(textWithTags.tags)
TextUtilities::ConvertTextTagsToEntities(textWithTags.tags)
};
const auto prepareFlags = Ui::ItemTextOptions(
item->history(),
Auth().user()).flags;
_controller->session().user()).flags;
TextUtilities::PrepareForSending(sending, prepareFlags);
TextUtilities::Trim(sending);
const auto sentEntities = TextUtilities::EntitiesToMTP(
const auto sentEntities = Api::EntitiesToMTP(
sending.entities,
TextUtilities::ConvertOption::SkipLocal);
Api::ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) {
flags |= MTPmessages_EditMessage::Flag::f_entities;
}
@@ -909,15 +920,15 @@ void EditCaptionBox::save() {
const auto textWithTags = _field->getTextWithAppliedMarkdown();
auto sending = TextWithEntities{
textWithTags.text,
ConvertTextTagsToEntities(textWithTags.tags)
TextUtilities::ConvertTextTagsToEntities(textWithTags.tags)
};
item->setText(sending);
Auth().api().editMedia(
_controller->session().api().editMedia(
std::move(_preparedList),
(!_asFile && _photo) ? SendMediaType::Photo : SendMediaType::File,
_field->getTextWithAppliedMarkdown(),
ApiWrap::SendOptions(item->history()),
Api::SendAction(item->history()),
item->fullId().msg);
closeBox();
return;
@@ -931,28 +942,32 @@ void EditCaptionBox::save() {
MTP_string(sending.text),
MTPInputMedia(),
MTPReplyMarkup(),
sentEntities),
sentEntities,
MTP_int(0)), // schedule_date
rpcDone(&EditCaptionBox::saveDone),
rpcFail(&EditCaptionBox::saveFail));
}
void EditCaptionBox::saveDone(const MTPUpdates &updates) {
_saveRequestId = 0;
const auto controller = _controller;
closeBox();
Auth().api().applyUpdates(updates);
controller->session().api().applyUpdates(updates);
}
bool EditCaptionBox::saveFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
_saveRequestId = 0;
QString err = error.type();
if (err == qstr("MESSAGE_ID_INVALID") || err == qstr("CHAT_ADMIN_REQUIRED") || err == qstr("MESSAGE_EDIT_TIME_EXPIRED")) {
const auto &type = error.type();
if (type == qstr("MESSAGE_ID_INVALID")
|| type == qstr("CHAT_ADMIN_REQUIRED")
|| type == qstr("MESSAGE_EDIT_TIME_EXPIRED")) {
_error = tr::lng_edit_error(tr::now);
} else if (err == qstr("MESSAGE_NOT_MODIFIED")) {
} else if (type == qstr("MESSAGE_NOT_MODIFIED")) {
closeBox();
return true;
} else if (err == qstr("MESSAGE_EMPTY")) {
} else if (type == qstr("MESSAGE_EMPTY")) {
_field->setFocus();
_field->showError();
} else {

View File

@@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
#include "storage/storage_media_prepare.h"
#include "ui/wrap/slide_wrap.h"
#include <rpl/event_stream.h>
#include "media/clip/media_clip_reader.h"
namespace ChatHelpers {
class TabbedPanel;
@@ -35,7 +35,10 @@ namespace Window {
class SessionController;
} // namespace Window
class EditCaptionBox : public BoxContent, public RPCSender {
class EditCaptionBox
: public BoxContent
, public RPCSender
, private base::Subscriber {
public:
EditCaptionBox(
QWidget*,
@@ -57,7 +60,7 @@ private:
void setupEmojiPanel();
void updateEmojiPanelGeometry();
bool emojiFilter(not_null<QEvent*> event);
void emojiFilterForGeometry(not_null<QEvent*> event);
void save();
void captionResized();

View File

@@ -8,15 +8,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/edit_color_box.h"
#include "lang/lang_keys.h"
#include "styles/style_boxes.h"
#include "ui/widgets/shadow.h"
#include "platform/platform_info.h"
#include "styles/style_mediaview.h"
#include "ui/widgets/input_fields.h"
#include "ui/ui_utility.h"
#include "platform/platform_info.h"
#include "app.h"
#include "styles/style_boxes.h"
#include "styles/style_mediaview.h"
class EditColorBox::Picker : public TWidget {
public:
Picker(QWidget *parent, QColor color);
Picker(QWidget *parent, Mode mode, QColor color);
float64 valueX() const {
return _x;
@@ -28,7 +30,7 @@ public:
base::Observable<void> &changed() {
return _changed;
}
void setHSV(int hue, int saturation, int brightness);
void setHSB(HSB hsb);
void setRGB(int red, int green, int blue);
protected:
@@ -43,8 +45,11 @@ private:
QCursor generateCursor();
void preparePalette();
void preparePaletteRGBA();
void preparePaletteHSL();
void updateCurrentPoint(QPoint localPosition);
Mode _mode;
QColor _topleft;
QColor _topright;
QColor _bottomleft;
@@ -61,8 +66,8 @@ private:
};
QCursor EditColorBox::Picker::generateCursor() {
auto diameter = ConvertScale(16);
auto line = ConvertScale(1);
auto diameter = style::ConvertScale(16);
auto line = style::ConvertScale(1);
auto size = ((diameter + 2 * line) >= 32) ? 64 : 32;
auto cursor = QImage(QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
cursor.setDevicePixelRatio(cRetinaFactor());
@@ -84,7 +89,9 @@ QCursor EditColorBox::Picker::generateCursor() {
return QCursor(QPixmap::fromImage(cursor));
}
EditColorBox::Picker::Picker(QWidget *parent, QColor color) : TWidget(parent) {
EditColorBox::Picker::Picker(QWidget *parent, Mode mode, QColor color)
: TWidget(parent)
, _mode(mode) {
setCursor(generateCursor());
auto size = QSize(st::colorPickerSize, st::colorPickerSize);
@@ -137,18 +144,27 @@ void EditColorBox::Picker::preparePalette() {
if (!_paletteInvalidated) return;
_paletteInvalidated = false;
auto size = _palette.width();
if (_mode == Mode::RGBA) {
preparePaletteRGBA();
} else {
preparePaletteHSL();
}
_palette.setDevicePixelRatio(cRetinaFactor());
}
void EditColorBox::Picker::preparePaletteRGBA() {
const auto size = _palette.width();
auto ints = reinterpret_cast<uint32*>(_palette.bits());
auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
const auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
constexpr auto Large = 1024 * 1024;
constexpr auto LargeBit = 20; // n / Large == (n >> LargeBit)
auto part = Large / size;
const auto part = Large / size;
auto topleft = anim::shifted(_topleft);
auto topright = anim::shifted(_topright);
auto bottomleft = anim::shifted(_bottomleft);
auto bottomright = anim::shifted(_bottomright);
const auto topleft = anim::shifted(_topleft);
const auto topright = anim::shifted(_topright);
const auto bottomleft = anim::shifted(_bottomleft);
const auto bottomright = anim::shifted(_bottomright);
auto y_accumulated = 0;
for (auto y = 0; y != size; ++y, y_accumulated += part) {
@@ -156,11 +172,11 @@ void EditColorBox::Picker::preparePalette() {
// 0 <= y_accumulated < Large
// 0 <= y_ratio < 256
auto top_ratio = 255 - y_ratio;
auto bottom_ratio = y_ratio;
const auto top_ratio = 256 - y_ratio;
const auto bottom_ratio = y_ratio;
auto left = anim::reshifted(bottomleft * bottom_ratio + topleft * top_ratio);
auto right = anim::reshifted(bottomright * bottom_ratio + topright * top_ratio);
const auto left = anim::reshifted(bottomleft * bottom_ratio + topleft * top_ratio);
const auto right = anim::reshifted(bottomright * bottom_ratio + topright * top_ratio);
auto x_accumulated = 0;
for (auto x = 0; x != size; ++x, x_accumulated += part) {
@@ -168,7 +184,7 @@ void EditColorBox::Picker::preparePalette() {
// 0 <= x_accumulated < Large
// 0 <= x_ratio < 256
auto left_ratio = 255 - x_ratio;
auto left_ratio = 256 - x_ratio;
auto right_ratio = x_ratio;
*ints++ = anim::unshifted(left * left_ratio + right * right_ratio);
@@ -177,6 +193,40 @@ void EditColorBox::Picker::preparePalette() {
}
}
void EditColorBox::Picker::preparePaletteHSL() {
const auto size = _palette.width();
const auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
auto ints = reinterpret_cast<uint32*>(_palette.bits());
constexpr auto Large = 1024 * 1024;
constexpr auto LargeBit = 20; // n / Large == (n >> LargeBit)
const auto part = Large / size;
const auto lightness = _topleft.lightness();
const auto right = anim::shifted(_bottomright);
for (auto y = 0; y != size; ++y) {
const auto hue = y * 360 / size;
const auto color = QColor::fromHsl(hue, 255, lightness).toRgb();
const auto left = anim::shifted(anim::getPremultiplied(color));
auto x_accumulated = 0;
for (auto x = 0; x != size; ++x, x_accumulated += part) {
auto x_ratio = x_accumulated >> (LargeBit - 8); // (x_accumulated * 256) / Large;
// 0 <= x_accumulated < Large
// 0 <= x_ratio < 256
auto left_ratio = 256 - x_ratio;
auto right_ratio = x_ratio;
*ints++ = anim::unshifted(left * left_ratio + right * right_ratio);
}
ints += intsAddPerLine;
}
_palette = std::move(_palette).transformed(
QTransform(0, 1, 1, 0, 0, 0));
}
void EditColorBox::Picker::updateCurrentPoint(QPoint localPosition) {
auto x = snap(localPosition.x(), 0, width()) / float64(width());
auto y = snap(localPosition.y(), 0, height()) / float64(height());
@@ -188,17 +238,25 @@ void EditColorBox::Picker::updateCurrentPoint(QPoint localPosition) {
}
}
void EditColorBox::Picker::setHSV(int hue, int saturation, int brightness) {
_topleft = QColor(255, 255, 255);
_topright.setHsv(qMax(0, hue), 255, 255);
_topright = _topright.toRgb();
_bottomleft = _bottomright = QColor(0, 0, 0);
void EditColorBox::Picker::setHSB(HSB hsb) {
if (_mode == Mode::RGBA) {
_topleft = QColor(255, 255, 255);
_topright.setHsv(qMax(0, hsb.hue), 255, 255);
_topright = _topright.toRgb();
_bottomleft = _bottomright = QColor(0, 0, 0);
_x = snap(hsb.saturation / 255., 0., 1.);
_y = 1. - snap(hsb.brightness / 255., 0., 1.);
} else {
_topleft = _topright = QColor::fromHsl(0, 255, hsb.brightness);
_bottomleft = _bottomright = QColor::fromHsl(0, 0, hsb.brightness);
_x = snap(hsb.hue / 360., 0., 1.);
_y = 1. - snap(hsb.saturation / 255., 0., 1.);
}
_paletteInvalidated = true;
update();
_x = snap(saturation / 255., 0., 1.);
_y = 1. - snap(brightness / 255., 0., 1.);
}
void EditColorBox::Picker::setRGB(int red, int green, int blue) {
@@ -206,7 +264,11 @@ void EditColorBox::Picker::setRGB(int red, int green, int blue) {
}
void EditColorBox::Picker::setFromColor(QColor color) {
setHSV(color.hsvHue(), color.hsvSaturation(), color.value());
if (_mode == Mode::RGBA) {
setHSB({ color.hsvHue(), color.hsvSaturation(), color.value() });
} else {
setHSB({ color.hslHue(), color.hslSaturation(), color.lightness() });
}
}
class EditColorBox::Slider : public TWidget {
@@ -218,6 +280,7 @@ public:
enum class Type {
Hue,
Opacity,
Lightness
};
Slider(QWidget *parent, Direction direction, Type type, QColor color);
@@ -231,10 +294,12 @@ public:
_value = snap(value, 0., 1.);
update();
}
void setHSV(int hue, int saturation, int brightness);
void setHSB(HSB hsb);
void setRGB(int red, int green, int blue);
void setAlpha(int alpha);
void setLightnessLimits(int min, int max);
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
@@ -254,10 +319,14 @@ private:
void generatePixmap();
void updatePixmapFromMask();
void updateCurrentPoint(QPoint localPosition);
[[nodiscard]] QColor applyLimits(QColor color) const;
Direction _direction = Direction::Horizontal;
Type _type = Type::Hue;
int _lightnessMin = 0;
int _lightnessMax = 255;
QColor _color;
float64 _value = 0;
@@ -270,12 +339,17 @@ private:
};
EditColorBox::Slider::Slider(QWidget *parent, Direction direction, Type type, QColor color) : TWidget(parent)
EditColorBox::Slider::Slider(
QWidget *parent,
Direction direction,
Type type,
QColor color)
: TWidget(parent)
, _direction(direction)
, _type(type)
, _color(color.red(), color.green(), color.blue())
, _value(valueFromColor(color))
, _transparent((_type == Type::Hue) ? QBrush() : style::transparentPlaceholderBrush()) {
, _transparent((_type == Type::Opacity) ? style::transparentPlaceholderBrush() : QBrush()) {
prepareMinSize();
}
@@ -336,10 +410,9 @@ void EditColorBox::Slider::generatePixmap() {
auto part = Large / size;
if (_type == Type::Hue) {
QColor color;
for (auto x = 0; x != size; ++x) {
color.setHsv(x * 360 / size, 255, 255);
auto value = anim::getPremultiplied(color.toRgb());
const auto color = QColor::fromHsv(x * 360 / size, 255, 255);
const auto value = anim::getPremultiplied(color.toRgb());
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
ints[y * intsPerLine] = value;
}
@@ -349,7 +422,7 @@ void EditColorBox::Slider::generatePixmap() {
image = std::move(image).transformed(QTransform(0, -1, 1, 0, 0, 0));
}
_pixmap = App::pixmapFromImageInPlace(std::move(image));
} else {
} else if (_type == Type::Opacity) {
auto color = anim::shifted(QColor(255, 255, 255, 255));
auto transparent = anim::shifted(QColor(255, 255, 255, 0));
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
@@ -359,7 +432,7 @@ void EditColorBox::Slider::generatePixmap() {
// 0 <= x_accumulated < Large
// 0 <= x_ratio < 256
*ints++ = anim::unshifted(color * x_ratio + transparent * (255 - x_ratio));
*ints++ = anim::unshifted(color * x_ratio + transparent * (256 - x_ratio));
}
ints += intsPerLineAdded;
}
@@ -368,22 +441,45 @@ void EditColorBox::Slider::generatePixmap() {
}
_mask = std::move(image);
updatePixmapFromMask();
} else {
const auto range = _lightnessMax - _lightnessMin;
for (auto x = 0; x != size; ++x) {
const auto color = QColor::fromHsl(
_color.hslHue(),
_color.hslSaturation(),
_lightnessMin + x * range / size);
const auto value = anim::getPremultiplied(color.toRgb());
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
ints[y * intsPerLine] = value;
}
++ints;
}
if (!isHorizontal()) {
image = std::move(image).transformed(QTransform(0, -1, 1, 0, 0, 0));
}
_pixmap = App::pixmapFromImageInPlace(std::move(image));
}
}
void EditColorBox::Slider::setHSV(int hue, int saturation, int brightness) {
void EditColorBox::Slider::setHSB(HSB hsb) {
if (_type == Type::Hue) {
// hue == 360 converts to 0 if done in general way
_value = valueFromHue(hue);
_value = valueFromHue(hsb.hue);
update();
} else if (_type == Type::Opacity) {
_color.setHsv(hsb.hue, hsb.saturation, hsb.brightness);
colorUpdated();
} else {
_color.setHsv(hue, saturation, brightness);
_color.setHsl(
hsb.hue,
hsb.saturation,
std::clamp(hsb.brightness, _lightnessMin, _lightnessMax));
colorUpdated();
}
}
void EditColorBox::Slider::setRGB(int red, int green, int blue) {
_color.setRgb(red, green, blue);
_color = applyLimits(QColor(red, green, blue));
colorUpdated();
}
@@ -392,12 +488,23 @@ void EditColorBox::Slider::colorUpdated() {
_value = valueFromColor(_color);
} else if (!_mask.isNull()) {
updatePixmapFromMask();
} else {
_value = valueFromColor(_color);
generatePixmap();
}
update();
}
float64 EditColorBox::Slider::valueFromColor(QColor color) const {
return (_type == Type::Hue) ? valueFromHue(color.hsvHue()) : color.alphaF();
return (_type == Type::Hue)
? valueFromHue(color.hsvHue())
: (_type == Type::Opacity)
? color.alphaF()
: std::clamp(
((color.lightness() - _lightnessMin)
/ float64(_lightnessMax - _lightnessMin)),
0.,
1.);
}
float64 EditColorBox::Slider::valueFromHue(int hue) const {
@@ -411,6 +518,15 @@ void EditColorBox::Slider::setAlpha(int alpha) {
}
}
void EditColorBox::Slider::setLightnessLimits(int min, int max) {
Expects(max > min);
_lightnessMin = min;
_lightnessMax = max;
_color = applyLimits(_color);
colorUpdated();
}
void EditColorBox::Slider::updatePixmapFromMask() {
_pixmap = App::pixmapFromImageInPlace(style::colorizeImage(_mask, _color));
}
@@ -426,6 +542,19 @@ void EditColorBox::Slider::updateCurrentPoint(QPoint localPosition) {
}
}
QColor EditColorBox::Slider::applyLimits(QColor color) const {
if (_type != Type::Lightness) {
return color;
}
const auto lightness = color.lightness();
const auto clamped = std::clamp(lightness, _lightnessMin, _lightnessMax);
if (clamped == lightness) {
return color;
}
return QColor::fromHsl(color.hslHue(), color.hslSaturation(), clamped);
}
class EditColorBox::Field : public Ui::MaskedInputField {
public:
Field(QWidget *parent, const style::InputField &st, const QString &placeholder, int limit, const QString &units = QString());
@@ -613,14 +742,18 @@ void EditColorBox::ResultField::paintAdditionalPlaceholder(Painter &p) {
p.drawText(QRect(_st.textMargins.right(), _st.textMargins.top(), width(), height() - _st.textMargins.top() - _st.textMargins.bottom()), "#", style::al_topleft);
}
EditColorBox::EditColorBox(QWidget*, const QString &title, QColor current) : BoxContent()
EditColorBox::EditColorBox(
QWidget*,
const QString &title,
Mode mode,
QColor current)
: BoxContent()
, _title(title)
, _picker(this, current)
, _hueSlider(this, Slider::Direction::Vertical, Slider::Type::Hue, current)
, _opacitySlider(this, Slider::Direction::Horizontal, Slider::Type::Opacity, current)
, _mode(mode)
, _picker(this, mode, current)
, _hueField(this, st::colorValueInput, "H", 360, QString() + QChar(176)) // degree character
, _saturationField(this, st::colorValueInput, "S", 100, "%")
, _brightnessField(this, st::colorValueInput, "B", 100, "%")
, _brightnessField(this, st::colorValueInput, (mode == Mode::RGBA) ? "B" : "L", 100, "%")
, _redField(this, st::colorValueInput, "R", 255)
, _greenField(this, st::colorValueInput, "G", 255)
, _blueField(this, st::colorValueInput, "B", 255)
@@ -628,16 +761,47 @@ EditColorBox::EditColorBox(QWidget*, const QString &title, QColor current) : Box
, _transparent(style::transparentPlaceholderBrush())
, _current(current)
, _new(current) {
if (_mode == Mode::RGBA) {
_hueSlider.create(
this,
Slider::Direction::Vertical,
Slider::Type::Hue,
current);
_opacitySlider.create(
this,
Slider::Direction::Horizontal,
Slider::Type::Opacity,
current);
} else if (_mode == Mode::HSL) {
_lightnessSlider.create(
this,
Slider::Direction::Horizontal,
Slider::Type::Lightness,
current);
}
}
void EditColorBox::setLightnessLimits(int min, int max) {
Expects(_mode == Mode::HSL);
_lightnessMin = min;
_lightnessMax = max;
_lightnessSlider->setLightnessLimits(min, max);
const auto adjusted = applyLimits(_new);
if (_new != adjusted) {
updateFromColor(adjusted);
}
}
void EditColorBox::prepare() {
setTitle(rpl::single(_title));
const auto hsvChanged = [=] { updateFromHSVFields(); };
const auto hsbChanged = [=] { updateFromHSBFields(); };
const auto rgbChanged = [=] { updateFromRGBFields(); };
connect(_hueField, &Ui::MaskedInputField::changed, hsvChanged);
connect(_saturationField, &Ui::MaskedInputField::changed, hsvChanged);
connect(_brightnessField, &Ui::MaskedInputField::changed, hsvChanged);
connect(_hueField, &Ui::MaskedInputField::changed, hsbChanged);
connect(_saturationField, &Ui::MaskedInputField::changed, hsbChanged);
connect(_brightnessField, &Ui::MaskedInputField::changed, hsbChanged);
connect(_redField, &Ui::MaskedInputField::changed, rgbChanged);
connect(_greenField, &Ui::MaskedInputField::changed, rgbChanged);
connect(_blueField, &Ui::MaskedInputField::changed, rgbChanged);
@@ -661,8 +825,15 @@ void EditColorBox::prepare() {
setDimensions(st::colorEditWidth, height);
subscribe(_picker->changed(), [=] { updateFromControls(); });
subscribe(_hueSlider->changed(), [=] { updateFromControls(); });
subscribe(_opacitySlider->changed(), [=] { updateFromControls(); });
if (_hueSlider) {
subscribe(_hueSlider->changed(), [=] { updateFromControls(); });
}
if (_opacitySlider) {
subscribe(_opacitySlider->changed(), [=] { updateFromControls(); });
}
if (_lightnessSlider) {
subscribe(_lightnessSlider->changed(), [=] { updateFromControls(); });
}
boxClosing() | rpl::start_with_next([=] {
if (_cancelCallback) {
@@ -671,7 +842,7 @@ void EditColorBox::prepare() {
}, lifetime());
updateRGBFields();
updateHSVFields();
updateHSBFields();
updateResultField();
update();
}
@@ -704,21 +875,21 @@ void EditColorBox::fieldSubmitted() {
}
void EditColorBox::saveColor() {
_cancelCallback = Fn<void()>();
const auto weak = Ui::MakeWeak(this);
_cancelCallback = nullptr;
if (_saveCallback) {
_saveCallback(_new.toRgb());
}
closeBox();
if (weak) {
closeBox();
}
}
void EditColorBox::updateHSVFields() {
auto hue = qRound((1. - _hueSlider->value()) * 360);
auto saturation = qRound(_picker->valueX() * 255);
auto brightness = qRound((1. - _picker->valueY()) * 255);
auto alpha = qRound(_opacitySlider->value() * 255);
_hueField->setTextWithFocus(QString::number(hue));
_saturationField->setTextWithFocus(QString::number(percentFromByte(saturation)));
_brightnessField->setTextWithFocus(QString::number(percentFromByte(brightness)));
void EditColorBox::updateHSBFields() {
const auto hsb = hsbFromControls();
_hueField->setTextWithFocus(QString::number(hsb.hue));
_saturationField->setTextWithFocus(QString::number(percentFromByte(hsb.saturation)));
_brightnessField->setTextWithFocus(QString::number(percentFromByte(hsb.brightness)));
}
void EditColorBox::updateRGBFields() {
@@ -750,14 +921,28 @@ void EditColorBox::updateResultField() {
}
void EditColorBox::resizeEvent(QResizeEvent *e) {
auto fullwidth = _picker->width() + 2 * (st::colorEditSkip - st::colorSliderSkip) + _hueSlider->width() + st::colorSampleSize.width();
const auto fullwidth = _picker->width()
+ ((_mode == Mode::RGBA)
? (2 * (st::colorEditSkip - st::colorSliderSkip) + _hueSlider->width())
: (2 * st::colorEditSkip))
+ st::colorSampleSize.width();
auto left = (width() - fullwidth) / 2;
_picker->moveToLeft(left, st::colorEditSkip);
_hueSlider->setGeometryToLeft(_picker->x() + _picker->width() + st::colorEditSkip - st::colorSliderSkip, st::colorEditSkip - st::colorSliderSkip, _hueSlider->width(), st::colorPickerSize + 2 * st::colorSliderSkip);
_opacitySlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _opacitySlider->height());
auto fieldLeft = _hueSlider->x() + _hueSlider->width() - st::colorSliderSkip + st::colorEditSkip;
auto fieldWidth = st::colorSampleSize.width();
auto fieldHeight = _hueField->height();
if (_hueSlider) {
_hueSlider->setGeometryToLeft(_picker->x() + _picker->width() + st::colorEditSkip - st::colorSliderSkip, st::colorEditSkip - st::colorSliderSkip, _hueSlider->width(), st::colorPickerSize + 2 * st::colorSliderSkip);
}
if (_opacitySlider) {
_opacitySlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _opacitySlider->height());
}
if (_lightnessSlider) {
_lightnessSlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _lightnessSlider->height());
}
const auto fieldLeft = (_mode == Mode::RGBA)
? (_hueSlider->x() + _hueSlider->width() + st::colorEditSkip - st::colorSliderSkip)
: (_picker->x() + _picker->width() + st::colorEditSkip);
const auto addWidth = (_mode == Mode::RGBA) ? 0 : st::colorEditSkip;
const auto fieldWidth = st::colorSampleSize.width() + addWidth;
const auto fieldHeight = _hueField->height();
_newRect = QRect(fieldLeft, st::colorEditSkip, fieldWidth, st::colorSampleSize.height());
_currentRect = _newRect.translated(0, st::colorSampleSize.height());
_hueField->setGeometryToLeft(fieldLeft, _currentRect.y() + _currentRect.height() + st::colorFieldSkip, fieldWidth, fieldHeight);
@@ -766,7 +951,13 @@ void EditColorBox::resizeEvent(QResizeEvent *e) {
_redField->setGeometryToLeft(fieldLeft, _brightnessField->y() + _brightnessField->height() + st::colorFieldSkip, fieldWidth, fieldHeight);
_greenField->setGeometryToLeft(fieldLeft, _redField->y() + _redField->height(), fieldWidth, fieldHeight);
_blueField->setGeometryToLeft(fieldLeft, _greenField->y() + _greenField->height(), fieldWidth, fieldHeight);
_result->setGeometryToLeft(fieldLeft - (st::colorEditSkip + st::colorSliderWidth), _opacitySlider->y() + _opacitySlider->height() - st::colorSliderSkip - _result->height(), fieldWidth + (st::colorEditSkip + st::colorSliderWidth), fieldHeight);
const auto resultDelta = (_mode == Mode::RGBA)
? (st::colorEditSkip + st::colorSliderWidth)
: 0;
const auto resultBottom = (_mode == Mode::RGBA)
? (_opacitySlider->y() + _opacitySlider->height())
: (_lightnessSlider->y() + _lightnessSlider->height());
_result->setGeometryToLeft(fieldLeft - resultDelta, resultBottom - st::colorSliderSkip - _result->height(), fieldWidth + resultDelta, fieldHeight);
}
void EditColorBox::paintEvent(QPaintEvent *e) {
@@ -792,39 +983,74 @@ void EditColorBox::mousePressEvent(QMouseEvent *e) {
}
}
EditColorBox::HSB EditColorBox::hsbFromControls() const {
const auto hue = (_mode == Mode::RGBA)
? qRound((1. - _hueSlider->value()) * 360)
: qRound(_picker->valueX() * 360);
const auto saturation = (_mode == Mode::RGBA)
? qRound(_picker->valueX() * 255)
: qRound((1. - _picker->valueY()) * 255);
const auto brightness = (_mode == Mode::RGBA)
? qRound((1. - _picker->valueY()) * 255)
: (_lightnessMin
+ qRound(_lightnessSlider->value()
* (_lightnessMax - _lightnessMin)));
return { hue, saturation, brightness };
}
QColor EditColorBox::applyLimits(QColor color) const {
if (_mode != Mode::HSL) {
return color;
}
const auto lightness = color.lightness();
const auto clamped = std::clamp(lightness, _lightnessMin, _lightnessMax);
if (clamped == lightness) {
return color;
}
return QColor::fromHsl(color.hslHue(), color.hslSaturation(), clamped);
}
void EditColorBox::updateFromColor(QColor color) {
_new = color;
_new = applyLimits(color);
updateControlsFromColor();
updateRGBFields();
updateHSVFields();
updateHSBFields();
updateResultField();
update();
}
void EditColorBox::updateFromControls() {
auto hue = qRound((1. - _hueSlider->value()) * 360);
auto saturation = qRound(_picker->valueX() * 255);
auto brightness = qRound((1. - _picker->valueY()) * 255);
auto alpha = qRound(_opacitySlider->value() * 255);
setHSV(hue, saturation, brightness, alpha);
updateHSVFields();
updateControlsFromHSV(hue, saturation, brightness);
const auto hsb = hsbFromControls();
const auto alpha = _opacitySlider
? qRound(_opacitySlider->value() * 255)
: 255;
setHSB(hsb, alpha);
updateHSBFields();
updateControlsFromHSB(hsb);
}
void EditColorBox::updateFromHSVFields() {
auto hue = _hueField->value();
auto saturation = percentToByte(_saturationField->value());
auto brightness = percentToByte(_brightnessField->value());
auto alpha = qRound(_opacitySlider->value() * 255);
setHSV(hue, saturation, brightness, alpha);
updateControlsFromHSV(hue, saturation, brightness);
void EditColorBox::updateFromHSBFields() {
const auto hue = _hueField->value();
const auto saturation = percentToByte(_saturationField->value());
const auto brightness = std::clamp(
percentToByte(_brightnessField->value()),
_lightnessMin,
_lightnessMax);
const auto alpha = _opacitySlider
? qRound(_opacitySlider->value() * 255)
: 255;
setHSB({ hue, saturation, brightness }, alpha);
updateControlsFromHSB({ hue, saturation, brightness });
}
void EditColorBox::updateFromRGBFields() {
auto red = _redField->value();
auto blue = _blueField->value();
auto green = _greenField->value();
auto alpha = qRound(_opacitySlider->value() * 255);
const auto red = _redField->value();
const auto blue = _blueField->value();
const auto green = _greenField->value();
const auto alpha = _opacitySlider
? qRound(_opacitySlider->value() * 255)
: 255;
setRGB(red, green, blue, alpha);
updateResultField();
}
@@ -855,10 +1081,17 @@ void EditColorBox::updateFromResultField() {
updateRGBFields();
}
void EditColorBox::updateControlsFromHSV(int hue, int saturation, int brightness) {
_picker->setHSV(hue, saturation, brightness);
_hueSlider->setHSV(hue, saturation, brightness);
_opacitySlider->setHSV(hue, saturation, brightness);
void EditColorBox::updateControlsFromHSB(HSB hsb) {
_picker->setHSB(hsb);
if (_hueSlider) {
_hueSlider->setHSB(hsb);
}
if (_opacitySlider) {
_opacitySlider->setHSB(hsb);
}
if (_lightnessSlider) {
_lightnessSlider->setHSB(hsb);
}
}
void EditColorBox::updateControlsFromColor() {
@@ -867,21 +1100,32 @@ void EditColorBox::updateControlsFromColor() {
auto blue = _new.blue();
auto alpha = _new.alpha();
_picker->setRGB(red, green, blue);
_hueSlider->setRGB(red, green, blue);
_opacitySlider->setRGB(red, green, blue);
_opacitySlider->setAlpha(alpha);
if (_hueSlider) {
_hueSlider->setRGB(red, green, blue);
}
if (_opacitySlider) {
_opacitySlider->setRGB(red, green, blue);
_opacitySlider->setAlpha(alpha);
}
if (_lightnessSlider) {
_lightnessSlider->setRGB(red, green, blue);
}
}
void EditColorBox::setHSV(int hue, int saturation, int value, int alpha) {
_new.setHsv(hue, saturation, value, alpha);
void EditColorBox::setHSB(HSB hsb, int alpha) {
if (_mode == Mode::RGBA) {
_new.setHsv(hsb.hue, hsb.saturation, hsb.brightness, alpha);
} else {
_new.setHsl(hsb.hue, hsb.saturation, hsb.brightness, alpha);
}
updateRGBFields();
updateResultField();
update();
}
void EditColorBox::setRGB(int red, int green, int blue, int alpha) {
_new.setRgb(red, green, blue, alpha);
_new = applyLimits(QColor(red, green, blue, alpha));
updateControlsFromColor();
updateHSVFields();
updateHSBFields();
update();
}
}

View File

@@ -9,9 +9,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
class EditColorBox : public BoxContent {
class EditColorBox : public BoxContent, private base::Subscriber {
public:
EditColorBox(QWidget*, const QString &title, QColor current = QColor(255, 255, 255));
enum class Mode {
RGBA,
HSL,
};
EditColorBox(QWidget*, const QString &title, Mode mode, QColor current);
void setLightnessLimits(int min, int max);
void setSaveCallback(Fn<void(QColor)> callback) {
_saveCallback = std::move(callback);
@@ -36,21 +42,28 @@ protected:
void setInnerFocus() override;
private:
struct HSB { // HSV or HSL depending on Mode.
int hue = 0;
int saturation = 0;
int brightness = 0;
};
void saveColor();
void fieldSubmitted();
[[nodiscard]] HSB hsbFromControls() const;
void updateFromColor(QColor color);
void updateControlsFromColor();
void updateControlsFromHSV(int hue, int saturation, int brightness);
void updateHSVFields();
void updateControlsFromHSB(HSB hsb);
void updateHSBFields();
void updateRGBFields();
void updateResultField();
void updateFromControls();
void updateFromHSVFields();
void updateFromHSBFields();
void updateFromRGBFields();
void updateFromResultField();
void setHSV(int hue, int saturation, int brightness, int alpha);
void setHSB(HSB hsb, int alpha);
void setRGB(int red, int green, int blue, int alpha);
[[nodiscard]] QColor applyLimits(QColor color) const;
int percentFromByte(int byte) {
return snap(qRound(byte * 100 / 255.), 0, 100);
@@ -65,10 +78,12 @@ private:
class ResultField;
QString _title;
Mode _mode = Mode();
object_ptr<Picker> _picker;
object_ptr<Slider> _hueSlider;
object_ptr<Slider> _opacitySlider;
object_ptr<Slider> _hueSlider = { nullptr };
object_ptr<Slider> _opacitySlider = { nullptr };
object_ptr<Slider> _lightnessSlider = { nullptr };
object_ptr<Field> _hueField;
object_ptr<Field> _saturationField;
@@ -85,6 +100,9 @@ private:
QRect _currentRect;
QRect _newRect;
int _lightnessMin = 0;
int _lightnessMax = 255;
Fn<void(QColor)> _saveCallback;
Fn<void()> _cancelCallback;

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