Compare commits

...

219 Commits

Author SHA1 Message Date
John Preston
c47781c25a Version 1.5.11.
- Bug fixes and other minor improvements.
2019-02-01 15:51:46 +03:00
John Preston
f0c4868b3e Ignore default banned rights for channel admins.
Fixes #5640.
2019-02-01 15:50:57 +03:00
John Preston
35e5c2329b Fix wall paper preview on Retina. 2019-02-01 15:41:43 +03:00
John Preston
74fc5524ab Fix dock and menu hiding on macOS.
Regression was introduced in 0681d10c51.
2019-02-01 15:41:43 +03:00
John Preston
2d4c99a6f7 Fix build for old OS X versions. 2019-02-01 13:23:35 +03:00
John Preston
69c73d0a2c Version 1.5.10.
- Bug fixes and other minor improvements.
2019-02-01 12:51:02 +03:00
John Preston
58510e0208 Improve chat list bot icon layout. 2019-02-01 12:50:30 +03:00
Jiachen YANG
0681d10c51 fixing mediaview fullscreen on X11 2019-02-01 12:38:32 +03:00
John Zimmermann
add2356c8a Generalize Unity Integration to not depend on libunity 2019-02-01 12:34:55 +03:00
John Preston
c66b2b2291 Fix setting offline on quit. 2019-02-01 12:23:37 +03:00
23rd
769923c6cc Fixed a keeping online status after application quit.
Fixes https://github.com/telegramdesktop/tdesktop/issues/5528.
2019-02-01 11:52:25 +03:00
23rd
27528d084f Added shortcuts to skip months in CalendarBox. 2019-02-01 11:49:14 +03:00
23rd
299aa69058 Added an icon for bots in the dialog list.
Fixes https://github.com/telegramdesktop/tdesktop/issues/1894 .
2019-02-01 11:48:45 +03:00
John Preston
d1cc09f40e Destroy layers in reverse order. 2019-02-01 11:47:30 +03:00
John Preston
a133b43eed Fix crash in dual background box show. 2019-02-01 11:32:34 +03:00
John Preston
9b57725b8c Fix bad function call in data export cancel. 2019-02-01 11:17:23 +03:00
John Preston
34b0f6f014 Fix crash in group migration. 2019-02-01 11:09:14 +03:00
John Preston
f5cc93ec64 Decouple MTP::Instance from Core::App. 2019-02-01 10:48:31 +03:00
John Preston
4f3263d979 Fix crash in MediaView with thumbless GIFs. 2019-02-01 10:28:04 +03:00
John Preston
b28e374e06 Fix Media::Audio::Instance destruction. 2019-02-01 10:09:55 +03:00
John Preston
918d58ef0a Better check unique results in support mode. 2019-02-01 09:53:15 +03:00
John Preston
5a388d9dde Improve Poland number formatting. 2019-02-01 09:18:31 +03:00
John Preston
0f4909621b Version 1.5.9.
- Bug fixes and other minor improvements.
2019-01-31 20:34:19 +03:00
John Preston
55d3d8adc3 Allow to delete users from exceptions. 2019-01-31 20:31:44 +03:00
John Preston
d2d6a6daa4 Fix proxy sponsor messages search display. 2019-01-31 15:56:57 +03:00
John Preston
a7f4ac2797 Search by hashtag inside groups. 2019-01-31 15:51:19 +03:00
John Preston
25b5027dc7 Only creator can restrict in old groups. 2019-01-31 15:20:15 +03:00
John Preston
93a967dc74 Add random_padding to dns requests. 2019-01-31 13:48:10 +03:00
John Preston
3cfc3dcecf Mark even more extensions as executables. 2019-01-30 17:59:55 +03:00
John Preston
e09510ea9f Fix crash in update checker destruction. 2019-01-30 17:58:23 +03:00
John Preston
4c289fc8fb Fix add members button in channels. 2019-01-30 13:49:36 +03:00
John Preston
58cf0fa2b1 Display date in background preview. 2019-01-29 20:03:51 +03:00
John Preston
5ca12a73c3 Use new HistoryWallPaper media type for wallpaper. 2019-01-29 19:26:19 +03:00
John Preston
fd8e9dad92 Fix pattern wallpapers with themes. 2019-01-29 14:52:33 +03:00
John Preston
df0fe0a460 Closed alpha version 1.5.8.2. 2019-01-29 14:52:33 +03:00
John Preston
646d15b257 Add LSFileQuarantineEnabled to the .plist 2019-01-29 14:52:33 +03:00
John Preston
2eb6848eb8 Fix background preview on retina screens. 2019-01-29 14:52:33 +03:00
John Preston
9b3c103f16 Backport patch for Mojave accessibility access request. 2019-01-29 14:52:33 +03:00
John Preston
6a2a13d346 Closed alpha version 1.5.8.1. 2019-01-29 14:52:33 +03:00
John Preston
b6edf4561d Add support for pattern wallpapers. 2019-01-29 14:52:33 +03:00
John Preston
c2744700c0 Mark more extensions as executables. 2019-01-29 14:52:33 +03:00
John Preston
f506a5ea6c Save wallpaper settings locally. 2019-01-29 14:52:33 +03:00
John Preston
2f702148e3 Fix manage channel actions visibility.
Fixes #5601.
2019-01-29 14:52:33 +03:00
John Preston
e10c928207 Update API scheme to layer 95. 2019-01-29 14:52:33 +03:00
John Preston
a1baa23a52 Use both thumbnails in photos and documents.
Fixes #5602.
2019-01-28 13:10:45 +03:00
John Preston
a70e72f75d Application->Sandbox, Messenger->Application. 2019-01-23 12:51:58 +04:00
John Preston
4111da1dd0 Move some calls from App namespace. 2019-01-22 17:10:01 +04:00
John Preston
e7804d014d Share background from preview. 2019-01-22 17:10:00 +04:00
John Preston
bf87de3706 Fix removing chats from dialogs. 2019-01-22 17:05:06 +04:00
John Preston
ebc2043055 Improve empty group display. 2019-01-22 11:50:21 +04:00
John Preston
69b1f6c4e1 Version 1.5.8.
- Global permissions for groups. Restrict all members in any group
from posting certain types of content.
- Unified group settings. Make groups public,
set admins with granular permissions and toggle persistent history
in just a few clicks in any group.
- Choose the emoji set you would like to use in Chat Settings.
- Choose input and output devices for Telegram Calls
in Settings > Advanced > Call Settings.
- Support for automatically downloading files and music.
2019-01-21 21:28:45 +04:00
John Preston
2708777167 Closed alpha version 1.5.7.2. 2019-01-21 18:06:20 +04:00
John Preston
6e0d62bb65 Re-apply document thumb only for stickers. 2019-01-21 17:43:24 +04:00
John Preston
6d706fd222 Improve unacessible permissions design. 2019-01-21 17:43:24 +04:00
John Preston
6066265717 Fix emoji suggestions in monospace. 2019-01-21 11:02:20 +04:00
John Preston
805e4d01e7 Allow re-downloading failed media. 2019-01-21 10:49:44 +04:00
John Preston
e209299af4 Fix invite link block visibility. 2019-01-21 10:40:25 +04:00
John Preston
b3f0a3c9f5 Make audio device selection box wider. 2019-01-21 10:37:31 +04:00
John Preston
1da8841ac7 fixup use Text 2019-01-21 10:37:20 +04:00
John Preston
7df5df6351 Improve phrases for removed users. 2019-01-21 10:26:19 +04:00
John Preston
bf85b0c109 Closed alpha version 1.5.7.1. 2019-01-18 16:41:08 +04:00
John Preston
314e30272b Replace SingleTimer with base::Timer. 2019-01-18 16:41:08 +04:00
John Preston
61b9a32504 Add 'Contact joined' notifications setting. 2019-01-18 16:41:08 +04:00
John Preston
01b7d4ffba Use Text to display empty group state. 2019-01-18 16:41:07 +04:00
John Preston
8643972f8c Send thumbs only for big files. 2019-01-18 16:41:07 +04:00
John Preston
1894b8fcf7 Handle t.me/bg links with wallpapers / colors. 2019-01-18 16:41:07 +04:00
John Preston
e59a68cd68 Refresh wallpaper file references. 2019-01-17 12:21:31 +04:00
John Preston
466c6da5e3 Save wallpaper information. 2019-01-17 12:21:31 +04:00
John Preston
b8cb792831 Read background image async. 2019-01-17 12:21:31 +04:00
John Preston
0f9c2a62fe Load fullres images of new wallpapers. 2019-01-17 12:21:31 +04:00
John Preston
04350af96f Use one place for saving restrictions. 2019-01-17 12:21:31 +04:00
John Preston
287b3509ab Optimize getPeerDialog requests. 2019-01-17 12:21:31 +04:00
John Preston
2a5bcd3eec Support empty group placeholder for creators. 2019-01-17 12:21:31 +04:00
John Preston
2a0b9a44dd Remove supergroup migrate messages. 2019-01-17 12:21:31 +04:00
John Preston
c552db04d7 Use 'exception' and 'remove user' phrases. 2019-01-17 12:21:30 +04:00
John Preston
22f1ffc72b Use different phrases for default restrictions. 2019-01-17 12:21:30 +04:00
John Preston
4f33be20d4 Add user to group on appointing of admin. 2019-01-17 12:21:30 +04:00
John Preston
9728ddeaf9 Handle migration to supergroups in boxes. 2019-01-17 12:21:30 +04:00
John Preston
3c44bdb6b7 Support auto-migrate to supergroups. 2019-01-17 12:21:30 +04:00
John Preston
b236844c94 Fix crash in admin log events. 2019-01-17 12:21:30 +04:00
John Preston
67d12fa6d2 Improve update handling for legacy chats. 2019-01-17 12:21:30 +04:00
John Preston
215856adc3 Improve AddSpecial box for legacy groups. 2019-01-17 12:21:30 +04:00
John Preston
07e010dfb5 Save admins in legacy groups. 2019-01-17 12:21:30 +04:00
John Preston
dba9ca2084 Replace TLHelp helpers with .match() 2019-01-17 12:21:30 +04:00
John Preston
9a60e744d3 Unify permissions checks in participants boxes. 2019-01-17 12:21:30 +04:00
John Preston
e1a2ab0d7e Log only source base name in assertions. 2019-01-17 12:21:30 +04:00
John Preston
a605c110a8 Support legacy groups in participant boxes. 2019-01-17 12:21:29 +04:00
John Preston
18c6be0d3b Unify legacy and megagroups information edit. 2019-01-17 12:21:29 +04:00
John Preston
ff728e2fc1 Add default permissions changes to admin log. 2019-01-17 12:21:29 +04:00
John Preston
eff90395b6 Add EditPeerPermissionsBox. 2019-01-17 12:21:29 +04:00
John Preston
f4d52b82b4 Allow reusing permissions edit controls. 2019-01-17 12:21:29 +04:00
John Preston
61419b57c8 Prepare legacy group restrictions checking. 2019-01-17 12:21:29 +04:00
John Preston
441989a8e1 Remove EditChannelBox. 2019-01-17 12:21:29 +04:00
John Preston
c5a41e1f55 Divide data/data_peer and remove from PCH. 2019-01-17 12:21:29 +04:00
John Preston
0ce4d66601 Rename restrictionReason to unavailableReason. 2019-01-17 12:21:28 +04:00
John Preston
d2d6b319b6 Update API scheme to layer 93. Broken. 2019-01-17 12:21:28 +04:00
John Preston
47edb71a68 Update API scheme to layer 92. 2019-01-17 12:21:28 +04:00
John Preston
1f8626b383 Move App::histories to Data::Session. 2019-01-17 12:21:28 +04:00
John Preston
0d6a36e187 Fix volume widget position.
Fixes #5467.
2019-01-17 12:20:50 +04:00
John Preston
548a0c8517 Fix MTP logging. 2019-01-17 12:20:50 +04:00
John Preston
09d85e25c1 Fix possible crash in SendFilesBox. 2019-01-17 12:20:50 +04:00
John Preston
51b5b14dea Beta version 1.5.7.
- Choose the emoji set you would like to use
in Settings > Chat Settings.
- Choose input and output devices for Telegram Calls
in Settings > Adavanced > Call Settings.
2019-01-11 17:31:36 +04:00
John Preston
0d290a2c28 Fix emoji icon in theme preview.
Fixes #5561.
2019-01-11 17:30:57 +04:00
John Preston
c1b3d589c7 Fix elision in document extension in MediaView.
Fixes #5555.
2019-01-11 17:30:57 +04:00
John Preston
65430d92ea Refactor calls settings panel. 2019-01-11 17:30:57 +04:00
John Preston
8711830f66 Move calls settings to Settings > Advanced.
Also add calls settings button to the calls list box.
2019-01-11 17:30:57 +04:00
John Preston
c4d919d46b Improve emoji quality while creating cache. 2019-01-11 17:30:57 +04:00
John Preston
3e7a688811 Fix tray icon counter on Linux.
Fixes #5525.
2019-01-11 17:30:57 +04:00
John Preston
2773a675d0 Improve sticker set download phrase. 2019-01-11 17:30:57 +04:00
John Preston
9bbdccc111 Postpone sticker send from StickerSetBox.
Fixes #5539.
2019-01-11 17:30:57 +04:00
John Preston
bb8defeb42 Improve change emoji set box design. 2019-01-11 17:30:57 +04:00
John Preston
545dbd0791 Return sticker-related context menu actions.
Regression was introduced in 3e22ada889.
2019-01-11 17:30:57 +04:00
John Preston
c7469075ab Closed beta version 1.5.6.1: Fix build in GCC. 2019-01-11 17:30:57 +04:00
John Preston
54fa974789 Closed beta version 1.5.6.1. 2019-01-11 17:30:57 +04:00
John Preston
2cd3cec478 Go through open history in support mode. 2019-01-11 17:30:00 +04:00
John Preston
c11b977f1d Fix lagging chat list update in support. 2019-01-11 17:30:00 +04:00
John Preston
30e8f17b37 [all] / [one from chat] in support search results. 2019-01-11 17:30:00 +04:00
John Preston
2c3190ce2a Hide LoadMore button in search mode. 2019-01-11 17:30:00 +04:00
John Preston
ea7796dccc Ctrl+[1-5] jump to the relevant pinned chat. 2019-01-11 17:30:00 +04:00
John Preston
a11ca58f36 Fix Ctrl+Insert in hashtag search results. 2019-01-11 17:30:00 +04:00
John Preston
496d711684 Improve support shortcuts handling. 2019-01-11 17:30:00 +04:00
John Preston
219b824338 Refresh filtered results on more dialogs. 2019-01-11 17:30:00 +04:00
John Preston
df389a365c Display emoji set loading radial animation. 2019-01-11 17:30:00 +04:00
John Preston
aa2c52c1f8 Unpack and prepare emoji asynchronously. 2019-01-11 17:30:00 +04:00
John Preston
f48ae29f22 Load emoji sets from the cloud. 2019-01-11 17:30:00 +04:00
John Preston
de00e0e15c Add simple switch emoji set box. 2019-01-11 17:30:00 +04:00
John Preston
79fea49272 Add updating emoji on the run. 2019-01-11 17:30:00 +04:00
John Preston
8190b10680 Support custom emoji sets loading. 2019-01-11 17:30:00 +04:00
Omkar Nath Singh
1ebd9562a2 Update copyright year (#5559) 2019-01-07 11:36:54 +04:00
John Preston
ebaf63393f Fix build for old OS X and MacAppStore. 2019-01-06 15:22:18 +04:00
John Preston
7c168740d9 Update instructions for Xcode 10.1. 2019-01-05 16:34:34 +04:00
Gregory K
11b991cddc Add call settings (#5540) 2019-01-05 15:08:02 +04:00
John Preston
8306e58b75 Fix build in Xcode / GCC. 2018-12-28 19:41:06 +04:00
John Preston
4002739682 Suggest adding bots to channels as admins. 2018-12-28 15:42:58 +04:00
John Preston
c259921269 Use qualified names for App::main and App::wnd. 2018-12-28 11:14:00 +04:00
John Preston
2eb3041c1f Beta version 1.5.6.
- Fix crash on macOS.
2018-12-27 19:55:36 +04:00
John Preston
d539d9b5d2 Fix postponed calls from crl::on_main() on macOS.
In libdispatch crl::on_main() implementation we bypass Application::notify() frame.
So we handle event loop nesting control manually by wrapping all invokations done
through crl::on_main() with the same methods we do from Application::notify().
2018-12-27 13:34:12 +04:00
John Preston
d17c985bcb Beta version 1.5.5.
- Support for auto-download of files and music.
- Improved auto-download settings.
2018-12-27 09:26:19 +04:00
John Preston
e2668e7cfa Default auto-download 8 MB files. 2018-12-27 09:24:46 +04:00
Omkar Nath Singh
18c2c61bee Update Ubuntu support version (#5372) 2018-12-26 15:09:20 +03:00
23rd
be0b0c1984 Fixed an editing of unsupported messages. 2018-12-26 15:10:56 +04:00
John Preston
b00ca217b3 Don't autoread mentions with voice/video messages.
Voice/video message unread flag is the same that mention unread flag.
If we mark such mentions as read together with all others we mark
media as watched/listened instantly when they appear on the screen.

So now we mark as read only simple mentions, without "unread" media.
2018-12-26 14:30:55 +04:00
John Preston
874d76b16b Ignore activations from ~PopupMenu in file dialog event loops. 2018-12-26 13:54:49 +04:00
John Preston
cfac261516 Create Application before ConcurrentTimerEnvironment.
Fixes #5498.
2018-12-26 13:02:43 +04:00
John Preston
7b5e5c2587 Move caption to the next album item on cancel.
After #4869 albums are sent with captions in the first media.
In case we cancel the first media leaving the rest of the album
the caption will be lost unless we move it to the new "first" media.
2018-12-26 11:24:12 +04:00
John Preston
db064381d9 Re-update libtgvoip.
The submodule was rolled back in 0b87db8b45.
2018-12-26 11:20:01 +04:00
John Preston
096c310e0e Display consistent caption/comment placeholder.
After latest PRs regarding sticker sending with comment (#5500)
and album sending with caption (#4869) the input field placeholder
in SendFilesBox was inconsistent with the sending behaviour. Fix it.
2018-12-26 10:53:21 +04:00
udf
0b87db8b45 Ignore only empty drafts while sending with clear_draft (#5424) 2018-12-26 10:52:48 +04:00
John Preston
71cf4a4885 Change default autodownload settings. 2018-12-26 09:58:27 +04:00
John Preston
a0c6104fae Redesign auto download box. 2018-12-25 22:15:22 +04:00
John Preston
8e54ac4dcf Rename autolock_box module to auto_lock_box. 2018-12-25 22:15:22 +04:00
John Preston
97b0288c7d Add automatic loading of videos/files. 2018-12-25 22:15:22 +04:00
John Preston
e3cc8652e4 New structs for media autodownload settings. 2018-12-25 22:15:22 +04:00
John Preston
8708a001c7 Always display nice percent values.
Sum of percent values should never exceed 100%. If any two answers
received same amount of votes, they should show same percent values.
This way sum could be less than 100% (three answers, one vote each),
but this looks better than giving extra vote to some random answer.
2018-12-25 22:12:35 +04:00
John Preston
6fc4facddf Improve updates sending script. 2018-12-25 19:42:02 +04:00
John Preston
e5536880fb Improve mouse/keyboard selection interactions.
Fixes #5458.
2018-12-25 16:41:40 +04:00
John Preston
44ff8f92ac Fix crash with invalid custom langpacks. 2018-12-25 15:22:25 +04:00
John Preston
95208267de Fix applying langpacks from file. 2018-12-25 15:22:01 +04:00
John Preston
9c579e0d5b Fix crash in wrong confirm email button. 2018-12-25 14:53:47 +04:00
John Preston
d1be4c6d96 Fix crash with event loop nesting. 2018-12-25 14:11:10 +04:00
John Preston
a65afdac95 Fix crash in RevokePublicLinkBox. 2018-12-25 12:48:12 +04:00
John Preston
22b47925d4 Fix assertion violation in calls. 2018-12-25 12:21:00 +04:00
John Preston
f291e365e5 Add slide animations in CreatePollBox. 2018-12-25 11:41:22 +04:00
John Preston
6f176803d4 Reload poll data each 30 seconds without update. 2018-12-24 21:03:53 +04:00
John Preston
b6a3bb4080 Allow closing CreatePollBox by escape. 2018-12-24 17:13:44 +04:00
23rd
c7c1deab81 Add caption to first item of album istead of sending separated message. 2018-12-24 17:12:18 +04:00
RMPR
2ded5870b5 Update supported Fedora version (#5502) 2018-12-24 17:03:40 +04:00
John Preston
57f2ae098f Show exact option votes count in a tooltip.
Fixes #5505.
2018-12-24 14:24:16 +04:00
John Preston
76c06923d5 Add ripple animation to poll voting. 2018-12-24 14:03:13 +04:00
John Preston
fb7ac874f0 Show poll question in pinned bar. 2018-12-24 13:25:02 +04:00
John Preston
12905f0dcb Version 1.5.4: Fix build in Xcode. 2018-12-24 12:16:05 +04:00
John Preston
68a313a58f Version 1.5.4.
- Bug fixes and other minor improvements.
2018-12-23 21:37:26 +04:00
John Preston
152115bf2e Disable multiline poll options. 2018-12-23 21:28:12 +04:00
John Preston
8bd3051224 Fix crop photo box title. 2018-12-23 21:23:36 +04:00
John Preston
aa94ca6619 Fix stuck inline mode.
Regression was introduced in 017ec87d60.

Fixes #5503.
2018-12-23 20:57:34 +04:00
John Preston
e397f72eb2 Lock poll creating.
Fixes #5504.
2018-12-23 20:47:00 +04:00
John Preston
4d495b8d7c Fix votes count in RTL languages. 2018-12-23 20:42:32 +04:00
John Preston
a142a2717c Fix no votes label. 2018-12-23 20:37:32 +04:00
John Preston
de4a477686 Mark event loop nesting more carefully.
Fixes #5506. I hope fixes #5508.
2018-12-23 16:08:48 +04:00
John Preston
4e692e2c1e Version 1.5.3: Fix build for GCC. 2018-12-23 01:31:12 +04:00
John Preston
8237e6f7a3 Version 1.5.3.
- Create polls in groups and channels - right from the chat menu.
2018-12-23 00:30:55 +04:00
23rd
d1a9f1feac Fixed caption when sending single WEBP as file. 2018-12-23 00:27:36 +04:00
John Preston
ef1d38462f Simplest polls data export. 2018-12-23 00:23:19 +04:00
John Preston
93c8e9aa1f Add poll vote sending animation. 2018-12-22 23:32:04 +04:00
John Preston
ac2dce4bb1 Fix polls percent display. 2018-12-22 22:48:25 +04:00
John Preston
f2e1d90c74 Add send vote / retract vote animation. 2018-12-22 22:36:00 +04:00
John Preston
8e28a229f2 Improve polls design. 2018-12-22 14:23:22 +04:00
John Preston
363f6cb329 Add question / options length warnings. 2018-12-22 10:29:03 +04:00
John Preston
b6f7832745 Add create poll box from groups three-dot menu. 2018-12-22 10:29:03 +04:00
John Preston
74c1db740d Implement polls voting and actions. 2018-12-21 17:09:33 +04:00
John Preston
4bb5dcf50c Simplest poll layout. 2018-12-21 17:09:32 +04:00
John Preston
b0d01389c6 Add .terminal extension as executable on macOS. 2018-12-21 17:09:32 +04:00
John Preston
3e22ada889 Remove HistoryMediaType type-tag. 2018-12-21 17:09:32 +04:00
John Preston
6d08394adc Divide history_media_types to several modules. 2018-12-21 17:09:31 +04:00
John Preston
47bdeeef9a Poll data and updates handling added. 2018-12-21 17:09:31 +04:00
John Preston
099440d008 Add test poll sending code. 2018-12-21 17:09:30 +04:00
John Preston
eb7201a55b Strictly match message / media types. 2018-12-21 17:09:30 +04:00
John Preston
6d9f40db30 Update API scheme to layer 91. 2018-12-21 17:09:30 +04:00
John Preston
7960706e60 Update API scheme to layer 90. 2018-12-17 11:01:30 +04:00
John Preston
8b0e54a95c Fix macOS main menu items. 2018-12-17 10:45:48 +04:00
John Preston
89e8f0ccc3 Fix pt-br FAQ link. 2018-12-17 10:12:44 +04:00
John Preston
0f67f75bed Version 1.5.2: Fix unread mentions in workmode. 2018-12-13 16:31:03 +04:00
John Preston
4836173fe6 Version 1.5.2: Fix video message duration. 2018-12-13 15:33:38 +04:00
John Preston
7378ad00b0 Version 1.5.2.
- Bug fixes and other minor improvements.
2018-12-13 12:59:48 +04:00
John Preston
e876c9b6a6 Disable support shortcuts when not support mode.
Fixes #5453.
2018-12-13 12:48:31 +04:00
John Preston
980d20473a Fix building Qt and openal-soft for OS X 10.6/7. 2018-12-12 21:44:49 +04:00
John Preston
22511270dd Fix building openal-soft on Xcode. 2018-12-12 17:59:26 +04:00
John Preston
89cf733d24 Add Ui::PostponeCall() on return to event loop. 2018-12-12 14:14:28 +04:00
John Preston
a167a8587b Fix crash in main menu right button click.
Fixes #5462.
2018-12-12 09:47:24 +04:00
John Preston
ffbd488e93 Clone nimf plugin in Travis Qt build. 2018-12-11 17:39:24 +04:00
John Preston
fa3117e41e Version 1.5.1: Fix build for Xcode. 2018-12-11 17:21:47 +04:00
John Preston
4be335b8ac Version 1.5.1.
- Bug fixes and other minor improvements.
2018-12-11 17:13:47 +04:00
John Preston
3904a9f9a0 Add guard in layers destruction. 2018-12-11 17:12:35 +04:00
John Preston
b683d84df1 Report memory usage in MB. 2018-12-11 16:26:33 +04:00
John Preston
3ad29f6eb7 Fix crash in media preview. 2018-12-11 16:22:27 +04:00
John Preston
cf227490d7 Fix using of base langpack for custom languages. 2018-12-11 16:00:24 +04:00
John Preston
ffd6f7b326 Fix some more missing emoji display.
Fixes #5388.
2018-12-11 15:59:45 +04:00
John Preston
ec2a1b3ddb Fix some missing emoji display. 2018-12-11 11:17:19 +04:00
John Preston
60711471fc Return VB script for UWP version preparing. 2018-12-10 21:41:44 +04:00
512 changed files with 34719 additions and 22901 deletions

View File

@@ -72,10 +72,6 @@ GOTO:EOF
set TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES%,TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION
)
echo %BUILD_VERSION% | findstr /C:"disable_unity_integration">nul && (
set TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES%,TDESKTOP_DISABLE_UNITY_INTEGRATION
)
echo %BUILD_VERSION% | findstr /C:"disable_gtk_integration">nul && (
set TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES%,TDESKTOP_DISABLE_GTK_INTEGRATION
)

View File

@@ -14,7 +14,6 @@ env:
- BUILD_VERSION="disable_crash_reports"
- BUILD_VERSION="disable_network_proxy"
- BUILD_VERSION="disable_desktop_file_generation"
- BUILD_VERSION="disable_unity_integration"
- BUILD_VERSION="disable_gtk_integration"
matrix:
@@ -45,7 +44,7 @@ addons:
- libopus-dev
- libpulse-dev
- libssl-dev
- libunity-dev
- libdee-dev
- libva-dev
- libvdpau-dev
- libxcb-xkb-dev

View File

@@ -17,7 +17,7 @@ XKB_PATH="$BUILD/libxkbcommon"
XKB_CACHE_VERSION="3"
QT_PATH="$BUILD/qt"
QT_CACHE_VERSION="3"
QT_CACHE_VERSION="4"
QT_PATCH="$UPSTREAM/Telegram/Patches/qtbase_${QT_VERSION//\./_}.diff"
BREAKPAD_PATH="$BUILD/breakpad"
@@ -116,10 +116,6 @@ build() {
GYP_DEFINES+=",TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION"
fi
if [[ $BUILD_VERSION == *"disable_unity_integration"* ]]; then
GYP_DEFINES+=",TDESKTOP_DISABLE_UNITY_INTEGRATION"
fi
if [[ $BUILD_VERSION == *"disable_gtk_integration"* ]]; then
GYP_DEFINES+=",TDESKTOP_DISABLE_GTK_INTEGRATION"
fi
@@ -604,6 +600,7 @@ buildCustomQt() {
cd "$EXTERNAL/qt${QT_VERSION}/qtbase/src/plugins/platforminputcontexts"
git clone https://github.com/telegramdesktop/fcitx.git
git clone https://github.com/telegramdesktop/hime.git
git clone https://github.com/telegramdesktop/nimf.git
cd ../../../..
./configure -prefix $QT_PATH -release -opensource -confirm-license -qt-zlib \

2
LEGAL
View File

@@ -1,7 +1,7 @@
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
Copyright (c) 2014-2018 John Preston, https://desktop.telegram.org
Copyright (c) 2014-2019 John Preston, https://desktop.telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -15,8 +15,8 @@ The source code is published under GPLv3 with OpenSSL exception, the license is
* Windows XP - Windows 10 (**not** RT)
* Mac OS X 10.8 - Mac OS X 10.11
* Mac OS X 10.6 - Mac OS X 10.7 (separate build)
* Ubuntu 12.04 - Ubuntu 18.04
* Fedora 22 - Fedora 28
* Ubuntu 12.04 - Ubuntu 18.10
* Fedora 22 - Fedora 29
* [Snappy](https://snapcraft.io/telegram-desktop)
## Third-party
@@ -45,7 +45,7 @@ The source code is published under GPLv3 with OpenSSL exception, the license is
## Build instructions
* [Visual Studio 2017][msvc]
* [Xcode 9][xcode]
* [Xcode 10][xcode]
* [GYP/CMake on GNU/Linux][cmake]
[//]: # (LINKS)

View File

@@ -1,5 +1,5 @@
diff --git a/src/build/common.gypi b/src/build/common.gypi
index 29990c6..53e99d4 100644
index 29990c65..53e99d44 100644
--- a/src/build/common.gypi
+++ b/src/build/common.gypi
@@ -330,6 +330,7 @@
@@ -11,7 +11,7 @@ index 29990c6..53e99d4 100644
},
}],
diff --git a/src/client/mac/Breakpad.xcodeproj/project.pbxproj b/src/client/mac/Breakpad.xcodeproj/project.pbxproj
index 1a93ce6..1c1d643 100644
index 1a93ce6d..b5986e33 100644
--- a/src/client/mac/Breakpad.xcodeproj/project.pbxproj
+++ b/src/client/mac/Breakpad.xcodeproj/project.pbxproj
@@ -35,6 +35,19 @@
@@ -34,7 +34,15 @@ index 1a93ce6..1c1d643 100644
162F64F2161C577500CD68D5 /* arch_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 162F64F0161C577500CD68D5 /* arch_utilities.cc */; };
162F64F3161C577500CD68D5 /* arch_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 162F64F1161C577500CD68D5 /* arch_utilities.h */; };
162F64F4161C579B00CD68D5 /* arch_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 162F64F0161C577500CD68D5 /* arch_utilities.cc */; };
@@ -170,11 +183,8 @@
@@ -67,6 +80,7 @@
4DBE49A7134A4F280072546A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBE4769134A4F080072546A /* CoreServices.framework */; };
4DBE49A8134A4F380072546A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBE4769134A4F080072546A /* CoreServices.framework */; };
4DBE49A9134A4F460072546A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBE4769134A4F080072546A /* CoreServices.framework */; };
+ 5A8B220921E0C5740045F83C /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; };
8B3101C611F0CD9F00FCF3E4 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D6A5FE840307C02AAC07 /* AppKit.framework */; };
8B3101C711F0CD9F00FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
8B3101CA11F0CDB000FCF3E4 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D6A5FE840307C02AAC07 /* AppKit.framework */; };
@@ -170,11 +184,8 @@
F92C564A0ECD10CA009BE4BA /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; };
F92C564C0ECD10DD009BE4BA /* breakpadUtilities.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; };
F92C56570ECD113E009BE4BA /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C554A0ECCF530009BE4BA /* Carbon.framework */; };
@@ -46,7 +54,7 @@ index 1a93ce6..1c1d643 100644
F92C56A90ECE04C5009BE4BA /* crash_report_sender.m in Sources */ = {isa = PBXBuildFile; fileRef = F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */; };
F93803CD0F8083B7004D428B /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; };
F93803CE0F8083B7004D428B /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; };
@@ -213,7 +223,6 @@
@@ -213,7 +224,6 @@
F9C44DBD0EF072A0003AEBAA /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DBA0EF072A0003AEBAA /* MainMenu.xib */; };
F9C44E000EF077CD003AEBAA /* Breakpad.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; };
F9C44E3C0EF08B12003AEBAA /* Breakpad.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; };
@@ -54,7 +62,7 @@ index 1a93ce6..1c1d643 100644
F9C44EA20EF09F93003AEBAA /* HTTPMultipartUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */; };
F9C44EE50EF0A006003AEBAA /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */; };
F9C44EE90EF0A3C1003AEBAA /* GTMLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44EE80EF0A3C1003AEBAA /* GTMLogger.m */; };
@@ -410,20 +419,6 @@
@@ -410,20 +420,6 @@
remoteGlobalIDString = F92C563B0ECD10B3009BE4BA;
remoteInfo = breakpadUtilities;
};
@@ -75,7 +83,7 @@ index 1a93ce6..1c1d643 100644
F93DE2FB0F82C3C600608B94 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
@@ -536,13 +531,6 @@
@@ -536,13 +532,6 @@
remoteGlobalIDString = 8DC2EF4F0486A6940098B216;
remoteInfo = Breakpad;
};
@@ -89,7 +97,7 @@ index 1a93ce6..1c1d643 100644
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -714,7 +702,6 @@
@@ -714,7 +703,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -97,7 +105,7 @@ index 1a93ce6..1c1d643 100644
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1181,18 +1168,13 @@
@@ -1181,18 +1169,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */;
buildPhases = (
@@ -116,7 +124,7 @@ index 1a93ce6..1c1d643 100644
);
name = Breakpad;
productInstallPath = "$(HOME)/Library/Frameworks";
@@ -1399,6 +1381,8 @@
@@ -1399,6 +1382,8 @@
/* Begin PBXProject section */
0867D690FE84028FC02AAC07 /* Project object */ = {
isa = PBXProject;
@@ -125,7 +133,7 @@ index 1a93ce6..1c1d643 100644
buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Breakpad" */;
compatibilityVersion = "Xcode 3.1";
developmentRegion = English;
@@ -1583,16 +1567,6 @@
@@ -1583,16 +1568,6 @@
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
@@ -142,7 +150,7 @@ index 1a93ce6..1c1d643 100644
F92C569C0ECE04A7009BE4BA /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -1640,20 +1614,6 @@
@@ -1640,20 +1615,6 @@
shellPath = /bin/sh;
shellScript = "install_name_tool -id \"@executable_path/../Resources/breakpadUtilities.dylib\" \"${BUILT_PRODUCTS_DIR}/breakpadUtilities.dylib\"\n";
};
@@ -163,10 +171,11 @@ index 1a93ce6..1c1d643 100644
F9C77DD80F7DD5CF0045F7DB /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -1674,6 +1634,19 @@
@@ -1674,6 +1635,20 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 5A8B220921E0C5740045F83C /* breakpad_nlist_64.cc in Sources */,
+ 0748C0501C63C52D004489BF /* bootstrap_compat.cc in Sources */,
+ 0748C04F1C63C523004489BF /* macho_walker.cc in Sources */,
+ 0748C04E1C63C51C004489BF /* md5.cc in Sources */,
@@ -183,7 +192,7 @@ index 1a93ce6..1c1d643 100644
F92C565F0ECD116B009BE4BA /* protected_memory_allocator.cc in Sources */,
F92C56630ECD1179009BE4BA /* exception_handler.cc in Sources */,
F92C55D10ECD0064009BE4BA /* Breakpad.mm in Sources */,
@@ -1955,16 +1928,6 @@
@@ -1955,16 +1930,6 @@
target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */;
targetProxy = F92C564D0ECD10E5009BE4BA /* PBXContainerItemProxy */;
};
@@ -200,7 +209,7 @@ index 1a93ce6..1c1d643 100644
F93DE2FC0F82C3C600608B94 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F93803BD0F80820F004D428B /* generator_test */;
@@ -2025,11 +1988,6 @@
@@ -2025,11 +1990,6 @@
target = 8DC2EF4F0486A6940098B216 /* Breakpad */;
targetProxy = F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */;
};
@@ -212,7 +221,7 @@ index 1a93ce6..1c1d643 100644
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@@ -2126,8 +2084,12 @@
@@ -2126,8 +2086,12 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */;
buildSettings = {
@@ -226,7 +235,7 @@ index 1a93ce6..1c1d643 100644
};
name = Debug;
};
@@ -2135,7 +2097,12 @@
@@ -2135,7 +2099,12 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 8B31027811F0D3AF00FCF3E4 /* BreakpadRelease.xcconfig */;
buildSettings = {
@@ -239,7 +248,7 @@ index 1a93ce6..1c1d643 100644
};
name = Release;
};
@@ -2454,7 +2421,12 @@
@@ -2454,7 +2423,12 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */;
buildSettings = {
@@ -253,7 +262,7 @@ index 1a93ce6..1c1d643 100644
name = "Debug With Code Coverage";
};
diff --git a/src/client/mac/Framework/Breakpad.mm b/src/client/mac/Framework/Breakpad.mm
index 1d2e519..943310f 100644
index 1d2e519b..943310fa 100644
--- a/src/client/mac/Framework/Breakpad.mm
+++ b/src/client/mac/Framework/Breakpad.mm
@@ -355,10 +355,10 @@ bool Breakpad::Initialize(NSDictionary *parameters) {
@@ -480,7 +489,7 @@ index 1d2e519..943310f 100644
}
diff --git a/src/common/language.cc b/src/common/language.cc
index 978fb85..a95ae5f 100644
index 978fb855..a95ae5f7 100644
--- a/src/common/language.cc
+++ b/src/common/language.cc
@@ -46,8 +46,27 @@
@@ -552,7 +561,7 @@ index 978fb85..a95ae5f 100644
#endif
}
diff --git a/src/common/linux/elf_symbols_to_module.cc b/src/common/linux/elf_symbols_to_module.cc
index 562875e..4367851 100644
index 562875e1..43678510 100644
--- a/src/common/linux/elf_symbols_to_module.cc
+++ b/src/common/linux/elf_symbols_to_module.cc
@@ -39,6 +39,29 @@
@@ -615,7 +624,7 @@ index 562875e..4367851 100644
module->AddExtern(ext);
}
diff --git a/src/tools/linux/tools_linux.gypi b/src/tools/linux/tools_linux.gypi
index 1c15992..020e4c1 100644
index 1c15992e..020e4c1c 100644
--- a/src/tools/linux/tools_linux.gypi
+++ b/src/tools/linux/tools_linux.gypi
@@ -58,7 +58,7 @@
@@ -627,3 +636,16 @@ index 1c15992..020e4c1 100644
],
'dependencies': [
'../common/common.gyp:common',
diff --git a/src/tools/mac/dump_syms/macho_dump.cc b/src/tools/mac/dump_syms/macho_dump.cc
index d882bbe8..3432bb45 100644
--- a/src/tools/mac/dump_syms/macho_dump.cc
+++ b/src/tools/mac/dump_syms/macho_dump.cc
@@ -140,7 +140,7 @@ void DumpFile(const char *filename) {
size_t object_files_size;
const SuperFatArch* super_fat_object_files =
fat_reader.object_files(&object_files_size);
- struct fat_arch *object_files;
+ struct fat_arch *object_files = 0;
if (!super_fat_object_files->ConvertToFatArch(object_files)) {
exit(1);
}

View File

@@ -1,8 +1,8 @@
diff --git a/build/common.gypi b/build/common.gypi
index 1affc70..c0d2f6a 100644
index 1affc70..0677e4b 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -66,6 +66,11 @@
@@ -66,6 +66,13 @@
'conditions': [
['clang!=0', {
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11', # -std=c++11
@@ -10,11 +10,13 @@ index 1affc70..c0d2f6a 100644
+ 'OTHER_LDFLAGS': [
+ '/usr/local/macold/lib/libc++.a',
+ '/usr/local/macold/lib/libc++abi.a',
+ '-isysroot/',
+ '-L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib/',
+ ],
# Don't link in libarclite_macosx.a, see http://crbug.com/156530.
'CLANG_LINK_OBJC_RUNTIME': 'NO', # -fno-objc-link-runtime
@@ -116,6 +121,9 @@
@@ -116,6 +123,9 @@
],
},

View File

@@ -1,5 +1,5 @@
diff --git a/configure b/configure
index cb8d78fd3cb..cadb3f0a880 100755
index cb8d78fd3c..cadb3f0a88 100755
--- a/configure
+++ b/configure
@@ -511,7 +511,8 @@ if [ "$BUILD_ON_MAC" = "yes" ]; then
@@ -13,7 +13,7 @@ index cb8d78fd3cb..cadb3f0a880 100755
echo " Xcode not set up properly. You may need to confirm the license" >&2
echo " agreement by running /usr/bin/xcodebuild without arguments." >&2
diff --git a/mkspecs/common/g++-macx.conf b/mkspecs/common/g++-macx.conf
index 086510dd963..c485967863d 100644
index 086510dd96..078a5ed1dd 100644
--- a/mkspecs/common/g++-macx.conf
+++ b/mkspecs/common/g++-macx.conf
@@ -14,7 +14,13 @@ QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += -gdwarf-2
@@ -27,12 +27,12 @@ index 086510dd963..c485967863d 100644
+# Patch: Use C++14 with custom libc++ build.
+QMAKE_CXXFLAGS_CXX11 = -std=c++1y
+QMAKE_CXXFLAGS += -nostdinc++ -I/usr/local/macold/include/c++/v1
+QMAKE_LFLAGS += /usr/local/macold/lib/libc++.a /usr/local/macold/lib/libc++abi.a
+QMAKE_LFLAGS += /usr/local/macold/lib/libc++.a /usr/local/macold/lib/libc++abi.a -isysroot /
QMAKE_XCODE_GCC_VERSION = com.apple.compilers.llvmgcc42
diff --git a/mkspecs/features/mac/default_pre.prf b/mkspecs/features/mac/default_pre.prf
index 0cc8cd6dfdd..ca9725b7791 100644
index 0cc8cd6dfd..ca9725b779 100644
--- a/mkspecs/features/mac/default_pre.prf
+++ b/mkspecs/features/mac/default_pre.prf
@@ -12,7 +12,9 @@ isEmpty(QMAKE_XCODE_DEVELOPER_PATH) {
@@ -47,7 +47,7 @@ index 0cc8cd6dfdd..ca9725b7791 100644
}
diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp
index bb79a139b3c..5d595bc3b34 100644
index bb79a139b3..5d595bc3b3 100644
--- a/src/gui/image/qbmphandler.cpp
+++ b/src/gui/image/qbmphandler.cpp
@@ -220,6 +220,10 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int
@@ -74,7 +74,7 @@ index bb79a139b3c..5d595bc3b34 100644
if (ncols > 0) { // read color table
uchar rgb[4];
diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h
index ebff9509ab2..4300ca4c0f0 100644
index ebff9509ab..4300ca4c0f 100644
--- a/src/gui/painting/qpaintengine_p.h
+++ b/src/gui/painting/qpaintengine_p.h
@@ -87,8 +87,18 @@ public:
@@ -98,7 +98,7 @@ index ebff9509ab2..4300ca4c0f0 100644
// Make sure we're inside the viewport.
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
index 4879ae51d7d..56cdcbaf01c 100644
index 4879ae51d7..56cdcbaf01 100644
--- a/src/gui/text/qtextlayout.cpp
+++ b/src/gui/text/qtextlayout.cpp
@@ -654,6 +654,9 @@ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
@@ -175,7 +175,7 @@ index 4879ae51d7d..56cdcbaf01c 100644
inline void resetRightBearing()
diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h
index cbe42c38444..b273db7e78c 100644
index cbe42c3844..b273db7e78 100644
--- a/src/gui/text/qtextlayout.h
+++ b/src/gui/text/qtextlayout.h
@@ -194,6 +194,9 @@ private:
@@ -189,7 +189,7 @@ index cbe42c38444..b273db7e78c 100644
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 360f9722c70..f28f289ef6a 100644
index 360f9722c7..f28f289ef6 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -118,6 +118,8 @@ QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
@@ -202,7 +202,7 @@ index 360f9722c70..f28f289ef6a 100644
delete channels[i].socket;
}
diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm
index ca7afb7d1b9..25ae50008db 100644
index ca7afb7d1b..25ae50008d 100644
--- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm
+++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm
@@ -256,6 +256,13 @@ static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd)
@@ -220,7 +220,7 @@ index ca7afb7d1b9..25ae50008db 100644
fd->weight = QFont::Normal;
fd->style = QFont::StyleNormal;
diff --git a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
index 6e2c8a2a9af..3cace8abcbc 100644
index 6e2c8a2a9a..3cace8abcb 100644
--- a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
+++ b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
@@ -717,7 +717,8 @@ void QCoreTextFontEngine::getUnscaledGlyph(glyph_t, QPainterPath *, glyph_metric
@@ -234,7 +234,7 @@ index 6e2c8a2a9af..3cace8abcbc 100644
QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
index 92358ecc745..694fee73507 100644
index 92358ecc74..694fee7350 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
@@ -213,7 +213,8 @@ static void cleanupCocoaApplicationDelegate()
@@ -271,7 +271,7 @@ index 92358ecc745..694fee73507 100644
- (void)appleEventQuit:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm
index b81b9a0b1c2..4e59e833b1d 100644
index b81b9a0b1c..4e59e833b1 100644
--- a/src/plugins/platforms/cocoa/qcocoacursor.mm
+++ b/src/plugins/platforms/cocoa/qcocoacursor.mm
@@ -81,7 +81,7 @@ void QCocoaCursor::setPos(const QPoint &position)
@@ -284,7 +284,7 @@ index b81b9a0b1c2..4e59e833b1d 100644
CFRelease(e);
}
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
index 9850f83dea8..b2e1d3dfda7 100644
index 9850f83dea..b2e1d3dfda 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
@@ -649,9 +649,10 @@ OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGIm
@@ -317,7 +317,7 @@ index 9850f83dea8..b2e1d3dfda7 100644
}
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index 9fd05a65ee9..dea60720e78 100644
index 9fd05a65ee..dea60720e7 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
@@ -402,14 +402,24 @@ void QCocoaIntegration::updateScreens()
@@ -348,7 +348,7 @@ index 9fd05a65ee9..dea60720e78 100644
QCocoaScreen *QCocoaIntegration::screenAtIndex(int index)
diff --git a/src/plugins/platforms/cocoa/qcocoakeymapper.mm b/src/plugins/platforms/cocoa/qcocoakeymapper.mm
index e46eaff6be3..c62db534a2d 100644
index e46eaff6be..c62db534a2 100644
--- a/src/plugins/platforms/cocoa/qcocoakeymapper.mm
+++ b/src/plugins/platforms/cocoa/qcocoakeymapper.mm
@@ -382,6 +382,12 @@ bool QCocoaKeyMapper::updateKeyboard()
@@ -375,7 +375,7 @@ index e46eaff6be3..c62db534a2d 100644
}
return ret;
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
index 83c960d9317..03ae9696afe 100755
index 83c960d931..03ae9696af 100755
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
@@ -102,7 +102,10 @@ QT_USE_NAMESPACE
@@ -543,7 +543,7 @@ index 83c960d9317..03ae9696afe 100755
}
@end
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 4d0458a4aa2..3357a5ef817 100644
index 4d0458a4aa..3357a5ef81 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -167,7 +167,8 @@ static bool isMouseEvent(NSEvent *ev)
@@ -606,7 +606,7 @@ index 4d0458a4aa2..3357a5ef817 100644
[iconButton setImage:image];
[image release];
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm
index a18ee7ff71d..1f91feb0ae8 100644
index a18ee7ff71..1f91feb0ae 100644
--- a/src/plugins/platforms/cocoa/qnsview.mm
+++ b/src/plugins/platforms/cocoa/qnsview.mm
@@ -393,7 +393,9 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil;
@@ -675,7 +675,7 @@ index a18ee7ff71d..1f91feb0ae8 100644
}
return [super performKeyEquivalent:nsevent];
diff --git a/src/tools/qlalr/lalr.cpp b/src/tools/qlalr/lalr.cpp
index c68076477f3..e2a7aafa586 100644
index c68076477f..e2a7aafa58 100644
--- a/src/tools/qlalr/lalr.cpp
+++ b/src/tools/qlalr/lalr.cpp
@@ -246,11 +246,13 @@ void Grammar::buildExtendedGrammar ()
@@ -715,7 +715,7 @@ index c68076477f3..e2a7aafa586 100644
continue;
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index 7396808442e..7178aecf800 100644
index 7396808442..7178aecf80 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -4722,6 +4722,17 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
@@ -768,7 +768,7 @@ index 7396808442e..7178aecf800 100644
|| (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier)))
res = focusNextPrevChild(false);
diff --git a/src/widgets/styles/qmacstyle_mac.mm b/src/widgets/styles/qmacstyle_mac.mm
index 0845a5eb02f..5735cb6b396 100644
index 0845a5eb02..5735cb6b39 100644
--- a/src/widgets/styles/qmacstyle_mac.mm
+++ b/src/widgets/styles/qmacstyle_mac.mm
@@ -3667,9 +3667,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
@@ -786,7 +786,7 @@ index 0845a5eb02f..5735cb6b396 100644
}
diff --git a/src/widgets/util/qsystemtrayicon_qpa.cpp b/src/widgets/util/qsystemtrayicon_qpa.cpp
index f98aeaf6782..00c0734129e 100644
index f98aeaf678..00c0734129 100644
--- a/src/widgets/util/qsystemtrayicon_qpa.cpp
+++ b/src/widgets/util/qsystemtrayicon_qpa.cpp
@@ -99,13 +99,18 @@ void QSystemTrayIconPrivate::updateIcon_sys()
@@ -815,7 +815,7 @@ index f98aeaf6782..00c0734129e 100644
}
diff --git a/src/widgets/widgets/qwidgetlinecontrol.cpp b/src/widgets/widgets/qwidgetlinecontrol.cpp
index 75f30599be4..980f2be1e93 100644
index 75f30599be..980f2be1e9 100644
--- a/src/widgets/widgets/qwidgetlinecontrol.cpp
+++ b/src/widgets/widgets/qwidgetlinecontrol.cpp
@@ -1867,7 +1867,8 @@ void QWidgetLineControl::processKeyEvent(QKeyEvent* event)
@@ -829,7 +829,7 @@ index 75f30599be4..980f2be1e93 100644
#ifndef QT_NO_COMPLETER
complete(event->key());
diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp
index 96438a0bdf7..b0b72064056 100644
index 96438a0bdf..b0b7206405 100644
--- a/src/widgets/widgets/qwidgettextcontrol.cpp
+++ b/src/widgets/widgets/qwidgettextcontrol.cpp
@@ -1342,7 +1342,8 @@ void QWidgetTextControlPrivate::keyPressEvent(QKeyEvent *e)

View File

@@ -1,5 +1,5 @@
diff --git a/mkspecs/common/msvc-desktop.conf b/mkspecs/common/msvc-desktop.conf
index eec9e1f..7ae53c7 100644
index eec9e1f688..7ae53c7a1e 100644
--- a/mkspecs/common/msvc-desktop.conf
+++ b/mkspecs/common/msvc-desktop.conf
@@ -30,9 +30,10 @@ QMAKE_YACCFLAGS = -d
@@ -17,7 +17,7 @@ index eec9e1f..7ae53c7 100644
QMAKE_CFLAGS_LTCG = -GL
QMAKE_CFLAGS_SSE2 = -arch:SSE2
diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp
index 391fbcc..d07802b 100644
index 391fbcc519..d07802bb7a 100644
--- a/src/corelib/io/qfsfileengine_win.cpp
+++ b/src/corelib/io/qfsfileengine_win.cpp
@@ -427,11 +427,12 @@ qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
@@ -36,7 +36,7 @@ index 391fbcc..d07802b 100644
// Note: Only return error if the first WriteFile failed.
q->setError(QFile::WriteError, qt_error_string());
diff --git a/src/corelib/tools/qunicodetables.cpp b/src/corelib/tools/qunicodetables.cpp
index 14e4fd1..0619a17 100644
index 14e4fd10aa..0619a176a7 100644
--- a/src/corelib/tools/qunicodetables.cpp
+++ b/src/corelib/tools/qunicodetables.cpp
@@ -6227,7 +6227,8 @@ static const Properties uc_properties[] = {
@@ -50,7 +50,7 @@ index 14e4fd1..0619a17 100644
{ 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 },
{ 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 },
diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp
index 2d00b9d..eeba86e 100644
index 2d00b9dce9..eeba86e936 100644
--- a/src/gui/kernel/qhighdpiscaling.cpp
+++ b/src/gui/kernel/qhighdpiscaling.cpp
@@ -51,6 +51,9 @@ static const char screenFactorsEnvVar[] = "QT_SCREEN_SCALE_FACTORS";
@@ -64,7 +64,7 @@ index 2d00b9d..eeba86e 100644
qreal result = 1;
if (qEnvironmentVariableIsSet(scaleFactorEnvVar)) {
diff --git a/src/gui/kernel/qplatformdialoghelper.h b/src/gui/kernel/qplatformdialoghelper.h
index 5b2f4ec..790db46 100644
index 5b2f4ece77..790db46d25 100644
--- a/src/gui/kernel/qplatformdialoghelper.h
+++ b/src/gui/kernel/qplatformdialoghelper.h
@@ -386,6 +386,10 @@ public:
@@ -79,7 +79,7 @@ index 5b2f4ec..790db46 100644
virtual void selectNameFilter(const QString &filter) = 0;
virtual QString selectedNameFilter() const = 0;
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
index bcd29b6..bcb0672 100644
index bcd29b6fe1..bcb0672f69 100644
--- a/src/gui/kernel/qwindow.cpp
+++ b/src/gui/kernel/qwindow.cpp
@@ -2525,7 +2525,8 @@ void QWindowPrivate::setCursor(const QCursor *newCursor)
@@ -93,7 +93,7 @@ index bcd29b6..bcb0672 100644
QCursor *c = QGuiApplication::overrideCursor();
if (!c && hasCursor)
diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h
index 918c989..4158259 100644
index 918c98997b..4158259743 100644
--- a/src/gui/painting/qpaintengine_p.h
+++ b/src/gui/painting/qpaintengine_p.h
@@ -80,8 +80,18 @@ public:
@@ -117,7 +117,7 @@ index 918c989..4158259 100644
// Make sure we're inside the viewport.
diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h
index 7e507bb..936e7a9 100644
index 7e507bba2d..936e7a92cb 100644
--- a/src/gui/text/qtextengine_p.h
+++ b/src/gui/text/qtextengine_p.h
@@ -283,7 +283,8 @@ private:
@@ -131,7 +131,7 @@ index 7e507bb..936e7a9 100644
public:
inline QTextItemInt()
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
index aca475a..5fa0be2 100644
index aca475a581..5fa0be2c45 100644
--- a/src/gui/text/qtextlayout.cpp
+++ b/src/gui/text/qtextlayout.cpp
@@ -694,6 +694,9 @@ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
@@ -208,7 +208,7 @@ index aca475a..5fa0be2 100644
static const QFixed RightBearingNotCalculated;
diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h
index f74d4d4..8ad672c 100644
index f74d4d4229..8ad672c9fe 100644
--- a/src/gui/text/qtextlayout.h
+++ b/src/gui/text/qtextlayout.h
@@ -196,6 +196,9 @@ private:
@@ -222,7 +222,7 @@ index f74d4d4..8ad672c 100644
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index c4cb8e6..45793e3 100644
index c4cb8e65c0..45793e364f 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -110,6 +110,8 @@ QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
@@ -235,7 +235,7 @@ index c4cb8e6..45793e3 100644
delete channels[i].socket;
}
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index 41834b2..8cdf4ab 100644
index 41834b21ae..8cdf4ab145 100644
--- a/src/network/socket/qnativesocketengine_win.cpp
+++ b/src/network/socket/qnativesocketengine_win.cpp
@@ -675,6 +675,13 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin
@@ -253,7 +253,7 @@ index 41834b2..8cdf4ab 100644
setError(QAbstractSocket::NetworkError, AddressNotAvailableErrorString);
socketState = QAbstractSocket::UnconnectedState;
diff --git a/src/platformsupport/dbustray/qdbustrayicon.cpp b/src/platformsupport/dbustray/qdbustrayicon.cpp
index 4d6e707..9bdb0be 100644
index 4d6e70720d..9bdb0beb67 100644
--- a/src/platformsupport/dbustray/qdbustrayicon.cpp
+++ b/src/platformsupport/dbustray/qdbustrayicon.cpp
@@ -58,9 +58,18 @@ QT_BEGIN_NAMESPACE
@@ -290,7 +290,7 @@ index 4d6e707..9bdb0be 100644
}
if (!necessary)
diff --git a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp
index 728b166..1dc6459 100644
index 728b166b71..1dc64593e1 100644
--- a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp
+++ b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp
@@ -172,6 +172,79 @@ void QBasicFontDatabase::releaseHandle(void *handle)
@@ -388,7 +388,7 @@ index 728b166..1dc6459 100644
if (error != FT_Err_Ok) {
qDebug() << "FT_New_Face failed with index" << index << ':' << hex << error;
diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp
index 8ebabf3..7bb8abd 100644
index 8ebabf3419..7bb8abd0d0 100644
--- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp
+++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp
@@ -375,6 +375,17 @@ static void populateFromPattern(FcPattern *pattern)
@@ -450,7 +450,7 @@ index 8ebabf3..7bb8abd 100644
}
populateFromPattern(pattern);
diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm
index 566abf2..5c5fde9 100644
index 566abf2126..5c5fde9813 100644
--- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm
+++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm
@@ -265,6 +265,13 @@ static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd)
@@ -482,7 +482,7 @@ index 566abf2..5c5fde9 100644
if (CFNumberRef italic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSlantTrait)) {
double d;
diff --git a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
index 7b45958..2ed2fd9 100644
index 7b459584ea..2ed2fd9b3b 100644
--- a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
+++ b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
@@ -764,7 +764,8 @@ void QCoreTextFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, gl
@@ -496,7 +496,7 @@ index 7b45958..2ed2fd9 100644
QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const
diff --git a/src/plugins/platforminputcontexts/compose/compose.pro b/src/plugins/platforminputcontexts/compose/compose.pro
index 86bdd47..9b9c8de 100644
index 86bdd4729b..9b9c8ded08 100644
--- a/src/plugins/platforminputcontexts/compose/compose.pro
+++ b/src/plugins/platforminputcontexts/compose/compose.pro
@@ -15,7 +15,8 @@ HEADERS += $$PWD/qcomposeplatforminputcontext.h \
@@ -510,7 +510,7 @@ index 86bdd47..9b9c8de 100644
LIBS += $$QMAKE_LIBS_XKBCOMMON
QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XKBCOMMON
diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
index d1bea9a..36a15a6 100644
index d1bea9af23..36a15a6473 100644
--- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
+++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
@@ -232,6 +232,12 @@ bool QComposeInputContext::checkComposeTable()
@@ -527,7 +527,7 @@ index d1bea9a..36a15a6 100644
event.setCommitString(QChar(character));
QCoreApplication::sendEvent(m_focusObject, &event);
diff --git a/src/plugins/platforminputcontexts/platforminputcontexts.pro b/src/plugins/platforminputcontexts/platforminputcontexts.pro
index faea54b..fe4a837 100644
index faea54b874..fe4a837511 100644
--- a/src/plugins/platforminputcontexts/platforminputcontexts.pro
+++ b/src/plugins/platforminputcontexts/platforminputcontexts.pro
@@ -1,7 +1,8 @@
@@ -541,7 +541,7 @@ index faea54b..fe4a837 100644
contains(QT_CONFIG, xcb-plugin): SUBDIRS += compose
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
index caa8884..9dc3bc1 100644
index caa8884661..9dc3bc1661 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
@@ -210,7 +210,8 @@ QT_END_NAMESPACE
@@ -572,7 +572,7 @@ index caa8884..9dc3bc1 100644
}
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h
index 934f68a..3ece698 100644
index 934f68ad18..3ece6984ac 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.h
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h
@@ -64,6 +64,9 @@ public:
@@ -586,7 +586,7 @@ index 934f68a..3ece698 100644
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
index ca92103..f27ea15 100644
index ca92103826..f27ea15bad 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
@@ -38,7 +38,8 @@
@@ -624,7 +624,7 @@ index ca92103..f27ea15 100644
p.setCompositionMode(QPainter::CompositionMode_Source);
const QVector<QRect> rects = region.rects();
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
index 058209d..6af61e7 100644
index 058209da7e..6af61e7dab 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
@@ -546,9 +546,9 @@ OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGIm
@@ -654,7 +654,7 @@ index 058209d..6af61e7 100644
}
diff --git a/src/plugins/platforms/cocoa/qcocoakeymapper.mm b/src/plugins/platforms/cocoa/qcocoakeymapper.mm
index c2d206f..9b97398 100644
index c2d206fb45..9b9739862d 100644
--- a/src/plugins/platforms/cocoa/qcocoakeymapper.mm
+++ b/src/plugins/platforms/cocoa/qcocoakeymapper.mm
@@ -384,6 +384,12 @@ bool QCocoaKeyMapper::updateKeyboard()
@@ -681,7 +681,7 @@ index c2d206f..9b97398 100644
}
return ret;
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
index 8152c57..87ba2f3 100644
index 8152c57ffd..87ba2f3f72 100644
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
@@ -94,6 +94,8 @@ QT_USE_NAMESPACE
@@ -821,7 +821,7 @@ index 8152c57..87ba2f3 100644
}
}
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index c0d5904..f3c2047 100644
index c0d5904367..f3c2047196 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -141,7 +141,8 @@ static bool isMouseEvent(NSEvent *ev)
@@ -883,7 +883,7 @@ index c0d5904..f3c2047 100644
[iconButton setImage:image];
[image release];
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm
index c67bcfd..2616f42 100644
index c67bcfd23b..6a60670aee 100644
--- a/src/plugins/platforms/cocoa/qnsview.mm
+++ b/src/plugins/platforms/cocoa/qnsview.mm
@@ -647,6 +647,12 @@ QT_WARNING_POP
@@ -957,8 +957,19 @@ index c67bcfd..2616f42 100644
- (void)cancelOperation:(id)sender
{
Q_UNUSED(sender);
@@ -1981,6 +2006,10 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
// change the cursor
[nativeCursor set];
+ // Patch: Backport a fix from cd08753d3e. Starting with macOS Mojave this requires accessibility access.
+ if (QSysInfo::macVersion() >= Q_MV_OSX(10, 14))
+ return;
+
// Make sure the cursor is updated correctly if the mouse does not move and window is under cursor
// by creating a fake move event
if (m_updatingDrag)
diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
index 94bb71e..16ab51e 100644
index 94bb71e429..16ab51e166 100644
--- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
+++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
@@ -716,12 +716,20 @@ public:
@@ -1163,7 +1174,7 @@ index 94bb71e..16ab51e 100644
{
m_data.setSelectedNameFilter(f); // Dialog cannot be updated at run-time.
diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp
index 1e58b9b..1741c21 100644
index 1e58b9b3d4..1741c21a1c 100644
--- a/src/plugins/platforms/windows/qwindowskeymapper.cpp
+++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp
@@ -1268,6 +1268,10 @@ QList<int> QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const
@@ -1178,7 +1189,7 @@ index 1e58b9b..1741c21 100644
if (!kbItem.exists)
return result;
diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp
index 1d23a9d..640cd42 100644
index 1d23a9d9b9..640cd426ed 100644
--- a/src/plugins/platforms/windows/qwindowsservices.cpp
+++ b/src/plugins/platforms/windows/qwindowsservices.cpp
@@ -127,6 +127,10 @@ static inline bool launchMail(const QUrl &url)
@@ -1193,7 +1204,7 @@ index 1d23a9d..640cd42 100644
// but that cannot handle a Windows command line [yet].
command.replace(QStringLiteral("%1"), url.toString(QUrl::FullyEncoded));
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index b38d7c2..34f19c4 100644
index b38d7c29ae..34f19c4efa 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -1020,7 +1020,8 @@ void QWindowsWindow::destroyWindow()
@@ -1247,7 +1258,7 @@ index b38d7c2..34f19c4 100644
break;
}
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
index 6fffa1e..cb1c9c1 100644
index 6fffa1e6e9..cb1c9c1161 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -265,6 +265,10 @@ private:
@@ -1262,7 +1273,7 @@ index 6fffa1e..cb1c9c1 100644
inline bool isDropSiteEnabled() const { return m_dropTarget != 0; }
void setDropSiteEnabled(bool enabled);
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
index 09e7ecf..c0f15a4 100644
index 09e7ecf3a3..c0f15a4242 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
@@ -79,7 +79,10 @@ static int resourceType(const QByteArray &key)
@@ -1292,7 +1303,7 @@ index 09e7ecf..c0f15a4 100644
break;
}
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h
index f88b710..6f818a5 100644
index f88b710864..6f818a5a72 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.h
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h
@@ -68,7 +68,10 @@ public:
@@ -1308,7 +1319,7 @@ index f88b710..6f818a5 100644
QXcbNativeInterface();
diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp
index bc2de89..aa8f8df 100644
index bc2de899f5..aa8f8df4ad 100644
--- a/src/widgets/dialogs/qfiledialog.cpp
+++ b/src/widgets/dialogs/qfiledialog.cpp
@@ -1200,6 +1200,15 @@ QList<QUrl> QFileDialogPrivate::userSelectedFiles() const
@@ -1343,7 +1354,7 @@ index bc2de89..aa8f8df 100644
Returns a list of urls containing the selected files in the dialog.
If no files are selected, or the mode is not ExistingFiles or
diff --git a/src/widgets/dialogs/qfiledialog.h b/src/widgets/dialogs/qfiledialog.h
index ffe49a2..42dc563 100644
index ffe49a2dd2..42dc563c8a 100644
--- a/src/widgets/dialogs/qfiledialog.h
+++ b/src/widgets/dialogs/qfiledialog.h
@@ -108,6 +108,9 @@ public:
@@ -1357,7 +1368,7 @@ index ffe49a2..42dc563 100644
QList<QUrl> selectedUrls() const;
diff --git a/src/widgets/dialogs/qfiledialog_p.h b/src/widgets/dialogs/qfiledialog_p.h
index f610e46..547a646 100644
index f610e46f83..547a64695a 100644
--- a/src/widgets/dialogs/qfiledialog_p.h
+++ b/src/widgets/dialogs/qfiledialog_p.h
@@ -123,6 +123,10 @@ public:
@@ -1398,7 +1409,7 @@ index f610e46..547a646 100644
{
if (QPlatformFileDialogHelper *helper = platformFileDialogHelper())
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index b1d80d7..42e32fd 100644
index b1d80d7b8f..42e32fd404 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -5138,6 +5138,17 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
@@ -1451,7 +1462,7 @@ index b1d80d7..42e32fd 100644
|| (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier)))
res = focusNextPrevChild(false);
diff --git a/src/widgets/util/qsystemtrayicon.cpp b/src/widgets/util/qsystemtrayicon.cpp
index 704142f..7c4340e 100644
index 704142fe5c..7c4340e459 100644
--- a/src/widgets/util/qsystemtrayicon.cpp
+++ b/src/widgets/util/qsystemtrayicon.cpp
@@ -709,6 +709,10 @@ void QSystemTrayIconPrivate::updateMenu_sys_qpa()
@@ -1466,7 +1477,7 @@ index 704142f..7c4340e 100644
}
diff --git a/src/widgets/widgets/qabstractscrollarea.cpp b/src/widgets/widgets/qabstractscrollarea.cpp
index 2e2a042..472e377 100644
index 2e2a042bf1..472e37722b 100644
--- a/src/widgets/widgets/qabstractscrollarea.cpp
+++ b/src/widgets/widgets/qabstractscrollarea.cpp
@@ -640,15 +640,22 @@ scrolling range.
@@ -1497,7 +1508,7 @@ index 2e2a042..472e377 100644
}
diff --git a/src/widgets/widgets/qwidgetlinecontrol.cpp b/src/widgets/widgets/qwidgetlinecontrol.cpp
index daf9f00..57499dc 100644
index daf9f00c46..57499dc4a4 100644
--- a/src/widgets/widgets/qwidgetlinecontrol.cpp
+++ b/src/widgets/widgets/qwidgetlinecontrol.cpp
@@ -40,6 +40,11 @@
@@ -1539,7 +1550,7 @@ index daf9f00..57499dc 100644
#ifndef QT_NO_COMPLETER
complete(event->key());
diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp
index deca002..8a2023f 100644
index deca002bf5..8a2023f503 100644
--- a/src/widgets/widgets/qwidgettextcontrol.cpp
+++ b/src/widgets/widgets/qwidgettextcontrol.cpp
@@ -71,6 +71,11 @@

View File

@@ -92,7 +92,7 @@ msgServiceFont: semiboldFont;
msgServiceNameFont: semiboldFont;
msgServicePhotoWidth: 100px;
msgDateFont: font(13px);
msgMinWidth: 190px;
msgMinWidth: 160px;
msgPhotoSize: 33px;
msgPhotoSkip: 40px;
msgPadding: margins(13px, 7px, 13px, 8px);
@@ -258,19 +258,6 @@ videoIcon: icon {
};
locationSize: size(320px, 240px);
webPageLeft: 10px;
webPageBar: 2px;
webPageTitleFont: semiboldFont;
webPageTitleStyle: semiboldTextStyle;
webPageTitleOutFg: historyTextOutFg;
webPageTitleInFg: historyTextInFg;
webPageDescriptionOutFg: historyTextOutFg;
webPageDescriptionInFg: historyTextInFg;
webPageDescriptionFont: normalFont;
webPageDescriptionStyle: defaultTextStyle;
webPagePhotoSize: 100px;
webPagePhotoDelta: 8px;
mediaPlayerSuppressDuration: 150;
botDescSkip: 8px;

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -311,7 +311,8 @@ div.selected {
background-position: 12px 12px;
background-size: 24px 24px;
}
.default .media .title {
.default .media .title,
.default .media_poll .question {
padding-top: 4px;
font-size: 14px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 737 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -128,6 +128,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gif_error" = "An error has occurred while reading GIF animation :(";
"lng_edit_error" = "You cannot edit this message";
"lng_join_channel_error" = "Sorry, you have joined too many channels and supergroups. Please leave some before joining this one.";
"lng_migrate_error" = "This action will convert the group to a supergroup. Unfortunately, you are a member of too many supergroups and channels. Please leave some of the channels or groups you don't need before proceeding.";
"lng_error_phone_flood" = "Sorry, you have deleted and re-created your account too many times recently. Please wait for a few days before signing up again.";
"lng_error_start_minimized_passcoded" = "You have set a local passcode, so Telegram Desktop can't be launched minimised; it will ask you to enter your passcode before it can start working.";
"lng_error_pinned_max#one" = "Sorry, you can pin no more than {count} chat to the top.";
@@ -142,10 +143,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_error_admin_limit_channel" = "Sorry, you've reached the maximum number of admins for this channel.";
"lng_sure_add_admin_invite" = "This user is not a member of this group. Add them to the group and promote them to admin?";
"lng_sure_add_admin_invite_channel" = "This user is not a subscriber of this channel. Add them to the channel and promote them to admin?";
"lng_sure_add_admin_unban" = "This user is currently restricted or banned. Are you sure you want to unban and promote them?";
"lng_sure_add_admin_unremove" = "This user is currently restricted or removed. Are you sure you want to promote them?";
"lng_sure_ban_admin" = "This user is an admin. Are you sure you want to go ahead and restrict them?";
"lng_sure_ban_user_group" = "Ban {user} in the group?";
"lng_sure_ban_user_channel" = "Ban {user} in the channel?";
"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";
@@ -156,6 +155,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_deleted" = "Deleted Account";
"lng_deleted_message" = "Deleted message";
"lng_pinned_message" = "Pinned message";
"lng_pinned_poll" = "Pinned poll";
"lng_pinned_unpin_sure" = "Would you like to unpin this message?";
"lng_pinned_pin_sure" = "Would you like to pin this message?";
"lng_pinned_pin" = "Pin";
@@ -274,16 +274,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_empty_bio" = "None";
"lng_settings_section_notify" = "Notifications";
"lng_settings_notify_title" = "Notifications for chats";
"lng_settings_desktop_notify" = "Desktop notifications";
"lng_settings_show_name" = "Show sender's name";
"lng_settings_show_preview" = "Show message preview";
"lng_settings_native_title" = "Native notifications";
"lng_settings_use_windows" = "Use Windows notifications";
"lng_settings_use_native_notifications" = "Use native notifications";
"lng_settings_notifications_position" = "Location on the screen";
"lng_settings_notifications_count" = "Notifications count";
"lng_settings_sound_notify" = "Play sound";
"lng_settings_badge_title" = "Badge counter";
"lng_settings_include_muted" = "Include muted chats in unread count";
"lng_settings_count_unread" = "Count unread messages";
"lng_settings_events_title" = "Events";
"lng_settings_events_joined" = "Contact joined Telegram";
"lng_notification_preview" = "You have a new message";
"lng_notification_reply" = "Reply";
@@ -330,6 +335,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_bg_tile" = "Tile background";
"lng_settings_adaptive_wide" = "Adaptive layout for wide screens";
"lng_settings_section_call_settings" = "Calls Settings";
"lng_settings_call_section_output" = "Speakers and headphones";
"lng_settings_call_section_input" = "Microphone";
"lng_settings_call_input_device" = "Input device";
"lng_settings_call_output_device" = "Output device";
"lng_settings_call_input_volume" = "Input volume: {percent}%";
"lng_settings_call_output_volume" = "Output volume: {percent}%";
"lng_settings_call_test_mic" = "Test microphone";
"lng_settings_call_stop_mic_test" = "Stop test";
"lng_settings_call_section_other" = "Other settings";
"lng_settings_call_open_system_prefs" = "Open system sound preferences";
"lng_settings_call_device_default" = "Default";
"lng_settings_call_audio_ducking" = "Mute other sounds during calls";
"lng_settings_language" = "Language";
"lng_settings_default_scale" = "Default interface scale";
"lng_settings_connection_type" = "Connection type";
@@ -376,6 +395,13 @@ 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_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.";
"lng_background_bad_link" = "This background link appears to be invalid.";
"lng_background_apply" = "Apply";
"lng_background_share" = "Share";
"lng_background_link_copied" = "Link copied to clipboard";
"lng_download_path_ask" = "Ask download path for each file";
"lng_download_path" = "Download path";
@@ -647,10 +673,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_create_public_link" = "Create public link";
"lng_profile_edit_public_link" = "Edit public link";
"lng_profile_search_members" = "Search members";
"lng_profile_manage_admins" = "Manage administrators";
"lng_profile_manage_blocklist" = "Manage banned users";
"lng_profile_manage_restrictedlist" = "Manage restricted users";
"lng_profile_recent_actions" = "Recent actions";
"lng_profile_common_groups#one" = "{count} group in common";
"lng_profile_common_groups#other" = "{count} groups in common";
"lng_profile_participants_section" = "Members";
@@ -678,6 +700,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_join_group" = "Join Group";
"lng_profile_delete_and_exit" = "Leave";
"lng_profile_kick" = "Remove";
"lng_profile_delete_removed" = "Delete";
"lng_profile_sure_kick" = "Remove {user} from the group?";
"lng_profile_sure_kick_channel" = "Remove {user} from the channel?";
"lng_profile_sure_remove_admin" = "Remove {user} from admins?";
@@ -773,13 +796,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_manage_peer_recent_actions" = "Recent Actions";
"lng_manage_peer_members" = "Members";
"lng_manage_peer_administrators" = "Administrators";
"lng_manage_peer_banned_users" = "Banned users";
"lng_manage_peer_restricted_users" = "Restricted users";
"lng_manage_peer_exceptions" = "Exceptions";
"lng_manage_peer_removed_users" = "Removed users";
"lng_manage_peer_permissions" = "Permissions";
"lng_manage_history_visibility_title" = "Chat history for new members";
"lng_manage_history_visibility_shown" = "Visible";
"lng_manage_history_visibility_shown_about" = "New members will see messages that were sent before they joined.";
"lng_manage_history_visibility_hidden" = "Hidden";
"lng_manage_history_visibility_hidden_about" = "New members won't see earlier messages.";
"lng_manage_history_visibility_hidden_legacy" = "New members won't see more than 100 previous messages.";
"lng_report_title" = "Report channel";
"lng_report_group_title" = "Report group";
@@ -794,17 +819,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_report_thanks" = "Thank you! Your report will be reviewed by our team very soon.";
"lng_channel_add_members" = "Add members";
"lng_channel_add_banned" = "Ban user";
"lng_channel_add_restricted" = "Restrict user";
"lng_channel_add_users" = "Add users";
"lng_channel_add_removed" = "Remove user";
"lng_channel_add_exception" = "Add exception";
"lng_channel_admins" = "Administrators";
"lng_channel_add_admin" = "Add Administrator";
"lng_channel_admin_status_creator" = "Creator";
"lng_channel_admin_status_promoted_by" = "Promoted by {user}";
"lng_channel_admin_status_not_admin" = "Not administrator";
"lng_channel_banned_status_restricted_by" = "Restricted by {user}";
"lng_channel_banned_status_removed_by" = "Removed by {user}";
"lng_group_blocked_list_about" = "Banned users are removed from the group and can only come back if invited by an admin.\nInvite links don't work for them.";
"lng_channel_blocked_list_about" = "Banned users are removed from the channel.\nInvite links don't work for them.";
"lng_channel_removed_list_about" = "Users removed from the channel by admins cannot rejoin it via invite links.";
"lng_group_removed_list_about" = "Users removed from the group by admins cannot rejoin it via invite links.";
"lng_chat_all_members_admins" = "All Members Are Admins";
"lng_chat_about_all_admins" = "Group members can add new members, edit name and photo of the group.";
@@ -884,7 +911,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_changed_title_channel" = "Channel name was changed to «{title}»";
"lng_action_created_chat" = "{from} created group «{title}»";
"lng_action_created_channel" = "Channel created";
"lng_action_group_migrate" = "The group was upgraded to a supergroup";
"lng_action_pinned_message" = "{from} pinned «{text}»";
"lng_action_pinned_media" = "{from} pinned {media}";
"lng_action_pinned_media_photo" = "a photo";
@@ -939,8 +965,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_convert_feature4" = "— Creator can set a public link for the group";
"lng_profile_convert_warning" = "{bold_start}Note:{bold_end} This action can not be undone";
"lng_profile_convert_confirm" = "Convert";
"lng_profile_add_more_after_upgrade#one" = "You will be able to add up to {count} member after you upgrade your group to a supergroup.";
"lng_profile_add_more_after_upgrade#other" = "You will be able to add up to {count} members after you upgrade your group to a supergroup.";
"lng_profile_add_more_after_create" = "You will be able to add more members after you create the group.";
"lng_channel_not_accessible" = "Sorry, this channel is not accessible.";
"lng_group_not_accessible" = "Sorry, this group is not accessible.";
@@ -997,13 +1022,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_media_audio" = "Voice message";
"lng_media_auto_settings" = "Automatic media download";
"lng_media_auto_in_private" = "In private chats";
"lng_media_auto_in_groups" = "In groups";
"lng_media_auto_in_channels" = "In channels";
"lng_media_auto_title" = "Automatically download";
"lng_media_photo_title" = "Photos";
"lng_media_video_title" = "Video files";
"lng_media_audio_title" = "Voice messages";
"lng_media_gif_title" = "GIFs and animations";
"lng_media_auto_private_chats" = "Private chats";
"lng_media_auto_groups" = "Groups and channels";
"lng_media_auto_play" = "Autoplay";
"lng_media_video_messages_title" = "Round video messages";
"lng_media_file_title" = "Files";
"lng_media_music_title" = "Music";
"lng_media_animation_title" = "Animated GIFs";
"lng_media_size_limit" = "Limit by size";
"lng_media_size_up_to" = "up to {size}";
"lng_media_chat_background" = "Chat background";
"lng_emoji_category1" = "People";
"lng_emoji_category2" = "Nature";
@@ -1013,6 +1045,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_emoji_category6" = "Objects";
"lng_emoji_category7" = "Symbols & Flags";
"lng_emoji_hide_panel" = "Click here to hide the emoji sidebar";
"lng_emoji_manage_sets" = "Choose emoji set";
"lng_emoji_set_ready" = "Downloaded";
"lng_emoji_set_active" = "Current set";
"lng_emoji_set_download" = "Download {size}";
"lng_emoji_set_loading" = "{percent}, {progress}";
"lng_recent_stickers" = "Frequently used";
"lng_faved_stickers_add" = "Add to Favorites";
@@ -1021,6 +1058,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_stickers_description" = "You can choose a sticker set which will be available for every member while in the group chat.";
"lng_group_stickers_add" = "Choose sticker set";
"lng_group_about_header" = "You have created a group.";
"lng_group_about_text" = "Groups can have:";
"lng_group_about1" = "Up to 100,000 members";
"lng_group_about2" = "Persistent chat history";
"lng_group_about3" = "Public links such as t.me/title";
"lng_group_about4" = "Admins with different rights";
"lng_switch_stickers" = "Stickers";
"lng_switch_emoji" = "Emoji";
"lng_switch_gifs" = "GIFs";
@@ -1066,6 +1110,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_in_dlg_file" = "File";
"lng_in_dlg_sticker" = "Sticker";
"lng_in_dlg_sticker_emoji" = "{emoji} Sticker";
"lng_in_dlg_poll" = "Poll";
"lng_ban_user" = "Ban User";
"lng_delete_all_from" = "Delete all from this user";
@@ -1082,7 +1127,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_cant_invite_banned" = "Sorry, only admin can add this user.";
"lng_cant_invite_privacy" = "Sorry, you cannot add this user to groups because of their privacy settings.";
"lng_cant_invite_privacy_channel" = "Sorry, you cannot add this user to channels because of their privacy settings.";
"lng_cant_invite_bot_to_channel" = "Sorry, bots can only be added to channels as administrators.";
"lng_cant_do_this" = "Sorry, this action is unavailable.";
"lng_cant_invite_offer_admin" = "Bots can only be added as administrators.";
"lng_cant_invite_make_admin" = "Make admin";
"lng_send_button" = "Send";
"lng_message_ph" = "Write a message...";
@@ -1184,6 +1232,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_edit_permissions" = "Edit permissions";
"lng_context_restrict_user" = "Restrict user";
"lng_context_remove_from_group" = "Remove from group";
"lng_context_add_to_group" = "Add to group";
"lng_context_copy_link" = "Copy Link";
"lng_context_copy_post_link" = "Copy Post Link";
@@ -1267,9 +1316,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_contact_title" = "Edit contact name";
"lng_edit_channel_title" = "Edit channel";
"lng_edit_sign_messages" = "Sign messages";
"lng_edit_group_who_invites" = "Who can add members";
"lng_edit_group_invites_everybody" = "All members";
"lng_edit_group_invites_only_admins" = "Only admins";
"lng_edit_group" = "Edit group";
"lng_edit_self_title" = "Edit your name";
"lng_confirm_contact_data" = "New Contact";
@@ -1444,9 +1490,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_rights_edit_admin_header" = "What can this admin do?";
"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.";
"lng_rights_about_admin_cant_edit" = "You cannot edit rights of this admin.";
"lng_rights_about_admin_cant_edit" = "You are not allowed to edit the rights of this admin.";
"lng_rights_about_restriction_cant_edit" = "You cannot change the restrictions for this user.";
"lng_rights_restriction_for_all" = "This option is disabled for all members in Group Permissions. You can either enable the permission for everyone or make this user an admin.";
"lng_rights_permission_for_all" = "This option is enabled for all members in Group Permissions.";
"lng_rights_permission_unavailable" = "This permission is not available in public groups.";
"lng_rights_permission_cant_edit" = "You cannot edit this permission.";
"lng_rights_user_restrictions" = "User restrictions";
"lng_rights_user_restrictions_header" = "What can this user do?";
"lng_rights_user_restrictions_header" = "What can this member do?";
"lng_rights_default_restrictions_header" = "What can members of this group do?";
"lng_rights_channel_info" = "Change channel info";
"lng_rights_channel_post" = "Post messages";
@@ -1464,6 +1517,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_rights_chat_send_media" = "Send media";
"lng_rights_chat_send_stickers" = "Send stickers & GIFs";
"lng_rights_chat_send_links" = "Embed links";
"lng_rights_chat_send_polls" = "Send polls";
"lng_rights_chat_add_members" = "Add members";
"lng_rights_chat_banned_until_header" = "Restricted until";
"lng_rights_chat_banned_forever" = "Forever";
"lng_rights_chat_banned_day#one" = "For {count} day";
@@ -1478,9 +1533,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_restricted_send_stickers" = "The admins of this group restricted you from posting stickers here.";
"lng_restricted_send_gifs" = "The admins of this group restricted you from posting GIFs here.";
"lng_restricted_send_inline" = "The admins of this group restricted you from posting inline content here.";
"lng_restricted_send_polls" = "The admins of this group restricted you from posting polls here.";
"lng_restricted_list_title" = "Restricted users";
"lng_banned_list_title" = "Banned users";
"lng_restricted_send_message_all" = "Writing messages isn't allowed in this group.";
"lng_restricted_send_media_all" = "Posting media content isn't allowed in this group.";
"lng_restricted_send_stickers_all" = "Posting stickers isn't allowed in this group.";
"lng_restricted_send_gifs_all" = "Posting GIFs isn't allowed in this group.";
"lng_restricted_send_inline_all" = "Posting inline content isn't allowed in this group.";
"lng_restricted_send_polls_all" = "Posting polls isn't allowed in this group.";
"lng_exceptions_list_title" = "Exceptions";
"lng_removed_list_title" = "Removed users";
"lng_admin_log_title_all" = "All actions";
"lng_admin_log_title_selected" = "Selected actions";
@@ -1540,10 +1603,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_admin_log_participant_joined_channel" = "{from} joined the channel";
"lng_admin_log_participant_left" = "{from} left the group";
"lng_admin_log_participant_left_channel" = "{from} left the channel";
"lng_admin_log_stopped_poll" = "{from} stopped poll:";
"lng_admin_log_invited" = "invited {user}";
"lng_admin_log_banned" = "banned {user}";
"lng_admin_log_restricted" = "changed restrictions for {user} {until}";
"lng_admin_log_promoted" = "changed privileges for {user}";
"lng_admin_log_changed_default_permissions" = "changed default permissions";
"lng_admin_log_changed_stickers_group" = "{from} changed the group's {sticker_set}";
"lng_admin_log_changed_stickers_set" = "sticker set";
"lng_admin_log_removed_stickers_group" = "{from} removed the group's sticker set";
@@ -1555,12 +1620,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_admin_log_banned_send_media" = "Send media";
"lng_admin_log_banned_send_stickers" = "Send stickers & GIFs";
"lng_admin_log_banned_embed_links" = "Embed links";
"lng_admin_log_banned_send_polls" = "Send polls";
"lng_admin_log_admin_change_info" = "Change info";
"lng_admin_log_admin_post_messages" = "Post messages";
"lng_admin_log_admin_edit_messages" = "Edit messages";
"lng_admin_log_admin_delete_messages" = "Delete messages";
"lng_admin_log_admin_ban_users" = "Ban users";
"lng_admin_log_admin_invite_users" = "Add users";
"lng_admin_log_admin_invite_users" = "Add members";
"lng_admin_log_admin_invite_link" = "Invite users via link";
"lng_admin_log_admin_pin_messages" = "Pin messages";
"lng_admin_log_admin_add_admins" = "Add new admins";
@@ -1829,6 +1895,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_launch_exe_sure" = "Run";
"lng_launch_exe_dont_ask" = "Don't ask me again";
"lng_polls_anonymous" = "Anonymous Poll";
"lng_polls_closed" = "Final results";
"lng_polls_votes_count#one" = "{count} vote";
"lng_polls_votes_count#other" = "{count} votes";
"lng_polls_votes_none" = "No votes";
"lng_polls_retract" = "Retract vote";
"lng_polls_stop" = "Stop poll";
"lng_polls_stop_warning" = "If you stop this poll now, nobody will be able to vote in it anymore. This action cannot be undone.";
"lng_polls_stop_sure" = "Stop";
"lng_polls_create" = "Create poll";
"lng_polls_create_title" = "New poll";
"lng_polls_create_question" = "Question";
"lng_polls_create_question_placeholder" = "Ask a question";
"lng_polls_create_options" = "Poll options";
"lng_polls_create_option_add" = "Add an option...";
"lng_polls_create_limit#one" = "You can add {count} more option.";
"lng_polls_create_limit#other" = "You can add {count} more options.";
"lng_polls_create_maximum" = "You have added the maximum number of options.";
"lng_polls_create_button" = "Create";
// Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program...";
@@ -1841,6 +1927,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_linux_menu_undo" = "Undo";
"lng_linux_menu_redo" = "Redo";
"lng_linux_no_audio_prefs" = "You don't have any audio configuration applications installed.";
// Mac specific
"lng_mac_choose_program_menu" = "Other...";

View File

@@ -207,7 +207,7 @@
52;MX;Mexico;
51;PE;Peru;51 XXX XXX XXX;11;
49;DE;Germany;49 XXX XXXXXXXX;13;
48;PL;Poland;48 XX XXX XXXX;11;
48;PL;Poland;48 XXX XXX XXX;11;
47;NO;Norway;47 XXXX XXXX;10;
46;SE;Sweden;46 XX XXX XXXX;11;
45;DK;Denmark;45 XXXX XXXX;10;

View File

@@ -0,0 +1,8 @@
<RCC>
<qresource prefix="/gui">
<file alias="emoji/set0_preview.webp">../emoji/set0_preview.webp</file>
<file alias="emoji/set1_preview.webp">../emoji/set1_preview.webp</file>
<file alias="emoji/set2_preview.webp">../emoji/set2_preview.webp</file>
<file alias="emoji/set3_preview.webp">../emoji/set3_preview.webp</file>
</qresource>
</RCC>

View File

@@ -176,7 +176,8 @@ inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = In
inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia;
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia;
inputMediaGeoLive#7b1a118f geo_point:InputGeoPoint period:int = InputMedia;
inputMediaGeoLive#ce4e82fd flags:# stopped:flags.0?true geo_point:InputGeoPoint period:flags.1?int = InputMedia;
inputMediaPoll#6b3765b poll:Poll = InputMedia;
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto;
@@ -194,8 +195,6 @@ inputDocumentFileLocation#196683d9 id:long access_hash:long file_reference:bytes
inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation;
inputTakeoutFileLocation#29be5899 = InputFileLocation;
inputAppEvent#770656a8 time:double type:string peer:long data:string = InputAppEvent;
peerUser#9db1bc6d user_id:int = Peer;
peerChat#bad0e5bb chat_id:int = Peer;
peerChannel#bddde532 channel_id:int = Peer;
@@ -228,13 +227,13 @@ userStatusLastWeek#7bf09fc = UserStatus;
userStatusLastMonth#77ebc742 = UserStatus;
chatEmpty#9ba2d800 id:int = Chat;
chat#d91cdd54 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true admins_enabled:flags.3?true admin:flags.4?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel = 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#c88974ac flags:# creator:flags.0?true left:flags.2?true editor:flags.3?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?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?ChannelAdminRights banned_rights:flags.15?ChannelBannedRights participants_count:flags.17?int = 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 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;
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#edd2a791 flags:# id:int 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 = ChatFull;
channelFull#76af5481 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?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 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 = ChatFull;
chatFull#22a235da 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 = ChatFull;
channelFull#1c87a71a 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 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 = ChatFull;
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
@@ -261,6 +260,7 @@ messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:str
messageMediaGame#fdb19008 game:Game = MessageMedia;
messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia;
messageMediaGeoLive#7c3c2609 geo:GeoPoint period:int = MessageMedia;
messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
messageActionEmpty#b6aef7b0 = MessageAction;
messageActionChatCreate#a6638b9a title:string users:Vector<int> = MessageAction;
@@ -284,6 +284,7 @@ messageActionCustomAction#fae69f56 message:string = MessageAction;
messageActionBotAllowed#abe9affe domain:string = MessageAction;
messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction;
messageActionSecureValuesSent#d95c6154 types:Vector<SecureValueType> = MessageAction;
messageActionContactSignUp#f3f25f76 = MessageAction;
dialog#e4def5db flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
@@ -293,6 +294,7 @@ photo#9c477dd8 flags:# has_stickers:flags.0?true id:long access_hash:long file_r
photoSizeEmpty#e17e23c type:string = PhotoSize;
photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize;
geoPointEmpty#1117dd5f = GeoPoint;
geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint;
@@ -316,12 +318,12 @@ peerNotifySettings#af509d20 flags:# show_previews:flags.0?Bool silent:flags.1?Bo
peerSettings#818426cd flags:# report_spam:flags.0?true = PeerSettings;
wallPaper#ccb03657 id:int title:string sizes:Vector<PhotoSize> color:int = WallPaper;
wallPaperSolid#63117f24 id:int title:string bg_color:int color:int = WallPaper;
wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper;
inputReportReasonSpam#58dbcab8 = ReportReason;
inputReportReasonViolence#1e22c78d = ReportReason;
inputReportReasonPornography#2e59d922 = ReportReason;
inputReportReasonChildAbuse#adf44ee3 = ReportReason;
inputReportReasonOther#e1746d0a text:string = ReportReason;
inputReportReasonCopyright#9b89f93a = ReportReason;
@@ -350,8 +352,8 @@ messages.dialogsSlice#71e094f3 count:int dialogs:Vector<Dialog> messages:Vector<
messages.dialogsNotModified#f0e3e596 count:int = messages.Dialogs;
messages.messages#8c718e87 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
messages.messagesSlice#b446ae3 count:int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
messages.channelMessages#99262e37 flags:# pts:int count:int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
messages.messagesSlice#a6c47aaa flags:# inexact:flags.1?true count:int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
messages.channelMessages#99262e37 flags:# inexact:flags.1?true pts:int count:int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
messages.messagesNotModified#74535f21 count:int = messages.Messages;
messages.chats#64ff9fd5 chats:Vector<Chat> = messages.Chats;
@@ -387,7 +389,6 @@ updateChatParticipants#7761198 participants:ChatParticipants = Update;
updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update;
updateUserName#a7332b73 user_id:int first_name:string last_name:string username:string = Update;
updateUserPhoto#95313b0c user_id:int date:int photo:UserProfilePhoto previous:Bool = Update;
updateContactRegistered#2575bbb9 user_id:int date:int = Update;
updateContactLink#9d2e67c5 user_id:int my_link:ContactLink foreign_link:ContactLink = Update;
updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update;
updateEncryptedChatTyping#1710f156 chat_id:int = Update;
@@ -411,7 +412,6 @@ updateNewChannelMessage#62ba04d9 message:Message pts:int pts_count:int = Update;
updateReadChannelInbox#4214f37f channel_id:int max_id:int = Update;
updateDeleteChannelMessages#c37521c9 channel_id:int messages:Vector<int> pts:int pts_count:int = Update;
updateChannelMessageViews#98a12b4b channel_id:int id:int views:int = Update;
updateChatAdmins#6e947941 chat_id:int enabled:Bool version:int = Update;
updateChatParticipantAdmin#b6901959 chat_id:int user_id:int is_admin:Bool version:int = Update;
updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update;
updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true order:Vector<long> = Update;
@@ -447,6 +447,8 @@ updateChannelAvailableMessages#70db6837 channel_id:int available_min_id:int = Up
updateDialogUnreadMark#e16459c3 flags:# unread:flags.0?true peer:DialogPeer = Update;
updateUserPinnedMessage#4c43da18 user_id:int id:int = Update;
updateChatPinnedMessage#22893b26 chat_id:int id:int = Update;
updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update;
updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBannedRights version:int = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@@ -473,7 +475,7 @@ upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes
dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption;
config#e6ca25f6 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config;
config#e6ca25f6 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config;
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
@@ -511,7 +513,7 @@ inputDocumentEmpty#72f0eaae = InputDocument;
inputDocument#1abfb575 id:long access_hash:long file_reference:bytes = InputDocument;
documentEmpty#36f8c871 id:long = Document;
document#59534e4c id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumb:PhotoSize dc_id:int attributes:Vector<DocumentAttribute> = Document;
document#9ba29cc1 flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumbs:flags.0?Vector<PhotoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;
help.support#17c6b5f6 phone_number:string user:User = help.Support;
@@ -673,8 +675,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#a82fa898 flags:# can_edit:flags.0?true user_id:int inviter_id:int promoted_by:int date:int admin_rights:ChannelAdminRights = ChannelParticipant;
channelParticipantBanned#222c1886 flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChannelBannedRights = 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;
channelParticipantBanned#1c0facaf flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChatBannedRights = ChannelParticipant;
channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter;
channelParticipantsAdmins#b4608969 = ChannelParticipantsFilter;
@@ -682,6 +684,7 @@ channelParticipantsKicked#a3b54985 q:string = ChannelParticipantsFilter;
channelParticipantsBots#b0d1865b = ChannelParticipantsFilter;
channelParticipantsBanned#1427a5e1 q:string = ChannelParticipantsFilter;
channelParticipantsSearch#656ac4b q:string = ChannelParticipantsFilter;
channelParticipantsContacts#bb6ae88d q:string = ChannelParticipantsFilter;
channels.channelParticipants#f56ee2a8 count:int participants:Vector<ChannelParticipant> users:Vector<User> = channels.ChannelParticipants;
channels.channelParticipantsNotModified#f0173fe9 = channels.ChannelParticipants;
@@ -916,11 +919,7 @@ langPackStringDeleted#2979eeb2 key:string = LangPackString;
langPackDifference#f385c1f6 lang_code:string from_version:int version:int strings:Vector<LangPackString> = LangPackDifference;
langPackLanguage#eeca5ce3 flags:# official:flags.0?true rtl:flags.2?true name:string native_name:string lang_code:string base_lang_code:flags.1?string plural_code:string strings_count:int translated_count:int translations_url:string = LangPackLanguage;
channelAdminRights#5d7ceba5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true invite_link:flags.6?true pin_messages:flags.7?true add_admins:flags.9?true manage_call:flags.10?true = ChannelAdminRights;
channelBannedRights#58cf4249 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true until_date:int = ChannelBannedRights;
langPackLanguage#eeca5ce3 flags:# official:flags.0?true rtl:flags.2?true beta:flags.3?true name:string native_name:string lang_code:string base_lang_code:flags.1?string plural_code:string strings_count:int translated_count:int translations_url:string = LangPackLanguage;
channelAdminLogEventActionChangeTitle#e6dfb825 prev_value:string new_value:string = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeAbout#55188a2e prev_value:string new_value:string = ChannelAdminLogEventAction;
@@ -938,6 +937,8 @@ channelAdminLogEventActionParticipantToggleBan#e6d83d7e prev_participant:Channel
channelAdminLogEventActionParticipantToggleAdmin#d5676710 prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeStickerSet#b1c3caa7 prev_stickerset:InputStickerSet new_stickerset:InputStickerSet = ChannelAdminLogEventAction;
channelAdminLogEventActionTogglePreHistoryHidden#5f5c95f1 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionDefaultBannedRights#2df5fc0a prev_banned_rights:ChatBannedRights new_banned_rights:ChatBannedRights = ChannelAdminLogEventAction;
channelAdminLogEventActionStopPoll#8f079643 message:Message = ChannelAdminLogEventAction;
channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
@@ -1057,6 +1058,17 @@ secureRequiredTypeOneOf#27477b4 types:Vector<SecureRequiredType> = SecureRequire
help.passportConfigNotModified#bfb9f457 = help.PassportConfig;
help.passportConfig#a098d6af hash:int countries_langs:DataJSON = help.PassportConfig;
inputAppEvent#1d1b1245 time:double type:string peer:long data:JSONValue = InputAppEvent;
jsonObjectValue#c0de1bd9 key:string value:JSONValue = JSONObjectValue;
jsonNull#3f6d7b68 = JSONValue;
jsonBool#c7345e6a value:Bool = JSONValue;
jsonNumber#2be0dfa4 value:double = JSONValue;
jsonString#b71e767a value:string = JSONValue;
jsonArray#f7444763 value:Vector<JSONValue> = JSONValue;
jsonObject#99c1d49d value:Vector<JSONObjectValue> = JSONValue;
pageTableCell#34566b6a flags:# header:flags.0?true align_center:flags.3?true align_right:flags.4?true valign_middle:flags.5?true valign_bottom:flags.6?true text:flags.7?RichText colspan:flags.1?int rowspan:flags.2?int = PageTableCell;
pageTableRow#e0c0c5e5 cells:Vector<PageTableCell> = PageTableRow;
@@ -1071,13 +1083,39 @@ pageListOrderedItemBlocks#98dd8936 num:string blocks:Vector<PageBlock> = PageLis
pageRelatedArticle#b390dc08 flags:# url:string webpage_id:long title:flags.0?string description:flags.1?string photo_id:flags.2?long author:flags.3?string published_date:flags.4?int = PageRelatedArticle;
page#ae891bec flags:# part:flags.0?true rtl:flags.1?true url:string blocks:Vector<PageBlock> photos:Vector<Photo> documents:Vector<Document> = Page;
page#ae891bec flags:# part:flags.0?true rtl:flags.1?true v2:flags.2?true url:string blocks:Vector<PageBlock> photos:Vector<Photo> documents:Vector<Document> = Page;
help.supportName#8c05f1c9 name:string = help.SupportName;
help.userInfoEmpty#f3ae2eed = help.UserInfo;
help.userInfo#1eb3758 message:string entities:Vector<MessageEntity> author:string date:int = help.UserInfo;
pollAnswer#6ca9c2e9 text:string option:bytes = PollAnswer;
poll#d5529d06 id:long flags:# closed:flags.0?true question:string answers:Vector<PollAnswer> = Poll;
pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true option:bytes voters:int = PollAnswerVoters;
pollResults#5755785a flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int = PollResults;
chatOnlines#f041e250 onlines:int = ChatOnlines;
statsURL#47a971e0 url:string = StatsURL;
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true = ChatAdminRights;
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true until_date:int = ChatBannedRights;
inputWallPaper#e630b979 id:long access_hash:long = InputWallPaper;
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;
wallPaperSettings#a12f40b8 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int intensity:flags.3?int = WallPaperSettings;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -1088,7 +1126,7 @@ invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;
invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
auth.sendCode#86aef0ec flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool api_id:int api_hash:string = auth.SentCode;
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.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
auth.logOut#5717da40 = Bool;
@@ -1111,7 +1149,7 @@ account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings;
account.resetNotifySettings#db7e1747 = Bool;
account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User;
account.updateStatus#6628562c offline:Bool = Bool;
account.getWallPapers#c04cfac2 = Vector<WallPaper>;
account.getWallPapers#aabb1763 hash:int = account.WallPapers;
account.reportPeer#ae189d5f peer:InputPeer reason:ReportReason = Bool;
account.checkUsername#2714d86c username:string = Bool;
account.updateUsername#3e0bdd7c username:string = User;
@@ -1120,7 +1158,7 @@ account.setPrivacy#c9f81ce8 key:InputPrivacyKey rules:Vector<InputPrivacyRule> =
account.deleteAccount#418d4e0b reason:string = Bool;
account.getAccountTTL#8fc711d = AccountDaysTTL;
account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool;
account.sendChangePhoneCode#8e57deb flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode;
account.sendChangePhoneCode#82574ae5 phone_number:string settings:CodeSettings = auth.SentCode;
account.changePhone#70c32edb phone_number:string phone_code_hash:string phone_code:string = User;
account.updateDeviceLocked#38df3532 period:int = Bool;
account.getAuthorizations#e320c158 = account.Authorizations;
@@ -1128,7 +1166,7 @@ account.resetAuthorization#df77f3bc hash:long = Bool;
account.getPassword#548a30f5 = account.Password;
account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings;
account.updatePasswordSettings#a59b102f password:InputCheckPasswordSRP new_settings:account.PasswordInputSettings = Bool;
account.sendConfirmPhoneCode#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode;
account.sendConfirmPhoneCode#1b3faa88 hash:string settings:CodeSettings = auth.SentCode;
account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool;
account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword;
account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
@@ -1140,7 +1178,7 @@ account.saveSecureValue#899fe31d value:InputSecureValue secure_secret_id:long =
account.deleteSecureValue#b880bc4b types:Vector<SecureValueType> = Bool;
account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm;
account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;
account.sendVerifyPhoneCode#823380b4 flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode;
account.sendVerifyPhoneCode#a5a356f9 phone_number:string settings:CodeSettings = auth.SentCode;
account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool;
account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode;
account.verifyEmail#ecba39db email:string code:string = Bool;
@@ -1149,6 +1187,14 @@ account.finishTakeoutSession#1d2652ee flags:# success:flags.0?true = Bool;
account.confirmPasswordEmail#8fdf1920 code:string = Bool;
account.resendPasswordEmail#7a7f2a15 = Bool;
account.cancelPasswordEmail#c1cbd5b6 = Bool;
account.getContactSignUpNotification#9f07c728 = Bool;
account.setContactSignUpNotification#cff43f61 silent:Bool = Bool;
account.getNotifyExceptions#53577479 flags:# compare_sound:flags.1?true peer:flags.0?InputNotifyPeer = Updates;
account.getWallPaper#fc8ddbea wallpaper:InputWallPaper = WallPaper;
account.uploadWallPaper#dd853661 file:InputFile mime_type:string settings:WallPaperSettings = WallPaper;
account.saveWallPaper#6c5a5b37 wallpaper:InputWallPaper unsave:Bool settings:WallPaperSettings = Bool;
account.installWallPaper#feed5769 wallpaper:InputWallPaper settings:WallPaperSettings = Bool;
account.resetWallPapers#bb3b9804 = Bool;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
@@ -1160,11 +1206,10 @@ contacts.getContacts#c023849f hash:int = contacts.Contacts;
contacts.importContacts#2c800be5 contacts:Vector<InputContact> = contacts.ImportedContacts;
contacts.deleteContact#8e953744 id:InputUser = contacts.Link;
contacts.deleteContacts#59ab389e id:Vector<InputUser> = Bool;
contacts.deleteByPhones#1013fd9e phones:Vector<string> = Bool;
contacts.block#332b49fc id:InputUser = Bool;
contacts.unblock#e54100bd id:InputUser = Bool;
contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked;
contacts.exportCard#84e53737 = Vector<int>;
contacts.importCard#4fe196fe export_card:Vector<int> = User;
contacts.search#11f812d8 q:string limit:int = contacts.Found;
contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers;
@@ -1211,7 +1256,7 @@ messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages
messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers;
messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers;
messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia;
messages.exportChatInvite#7d885289 chat_id:int = ExportedChatInvite;
messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite;
messages.checkChatInvite#3eadb1bb hash:string = ChatInvite;
messages.importChatInvite#6c50051c hash:string = Updates;
messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet;
@@ -1219,7 +1264,6 @@ messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = m
messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool;
messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_param:string = Updates;
messages.getMessagesViews#c4c8a55d peer:InputPeer id:Vector<int> increment:Bool = Vector<int>;
messages.toggleChatAdmins#ec8bd9e1 chat_id:int enabled:Bool = Updates;
messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bool;
messages.migrateChat#15a3b8e3 chat_id:int = Updates;
messages.searchGlobal#9e3cacb0 q:string offset_date:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
@@ -1230,10 +1274,10 @@ 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 peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates;
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.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
messages.editMessage#c000e4c8 flags:# no_webpage:flags.1?true stop_geo_live:flags.12?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> geo_point:flags.13?InputGeoPoint = Updates;
messages.editInlineBotMessage#adc3e828 flags:# no_webpage:flags.1?true stop_geo_live:flags.12?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> geo_point:flags.13?InputGeoPoint = Bool;
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.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;
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
@@ -1274,6 +1318,12 @@ messages.markDialogUnread#c286d98f flags:# unread:flags.0?true peer:InputDialogP
messages.getDialogUnreadMarks#22e24e22 = Vector<DialogPeer>;
messages.clearAllDrafts#7e58ee9c = Bool;
messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true peer:InputPeer id:int = Updates;
messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector<bytes> = Updates;
messages.getPollResults#73bb643b peer:InputPeer msg_id:int = Updates;
messages.getOnlines#6e2be050 peer:InputPeer = ChatOnlines;
messages.getStatsURL#83f6c0cd peer:InputPeer = StatsURL;
messages.editChatAbout#def60797 peer:InputPeer about:string = Bool;
messages.editChatDefaultBannedRights#a5866b41 peer:InputPeer banned_rights:ChatBannedRights = Updates;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@@ -1296,7 +1346,6 @@ upload.getFileHashes#c7025931 location:InputFileLocation offset:int = Vector<Fil
help.getConfig#c4f9186b = Config;
help.getNearestDc#1fb33026 = NearestDc;
help.getAppUpdate#522d5a7d source:string = help.AppUpdate;
help.saveAppLog#6f02f748 events:Vector<InputAppEvent> = Bool;
help.getInviteText#4d392343 = help.InviteText;
help.getSupport#9cdf08cd = help.Support;
help.getAppChangelog#9010ef6f prev_app_version:string = Updates;
@@ -1307,6 +1356,8 @@ help.getProxyData#3d7758e1 = help.ProxyData;
help.getTermsOfServiceUpdate#2ca51fd1 = help.TermsOfServiceUpdate;
help.acceptTermsOfService#ee72f79a id:DataJSON = Bool;
help.getDeepLinkInfo#3fedc75f path:string = help.DeepLinkInfo;
help.getAppConfig#98914110 = JSONValue;
help.saveAppLog#6f02f748 events:Vector<InputAppEvent> = Bool;
help.getPassportConfig#c661ad08 hash:int = help.PassportConfig;
help.getSupportName#d360e72c = help.SupportName;
help.getUserInfo#38a08d3 user_id:InputUser = help.UserInfo;
@@ -1322,8 +1373,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#f4893d7f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string = Updates;
channels.editAbout#13e27f1e channel:InputChannel about:string = Bool;
channels.editAdmin#20b88214 channel:InputChannel user_id:InputUser admin_rights:ChannelAdminRights = Updates;
channels.editAdmin#70f893ba channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights = 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;
@@ -1331,14 +1381,11 @@ channels.updateUsername#3514b3de channel:InputChannel username:string = Bool;
channels.joinChannel#24b524c5 channel:InputChannel = Updates;
channels.leaveChannel#f836aa95 channel:InputChannel = Updates;
channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> = Updates;
channels.exportInvite#c7560885 channel:InputChannel = ExportedChatInvite;
channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
channels.toggleInvites#49609307 channel:InputChannel enabled:Bool = Updates;
channels.exportMessageLink#ceb77163 channel:InputChannel id:int grouped:Bool = ExportedMessageLink;
channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
channels.updatePinnedMessage#a72ded52 flags:# silent:flags.0?true channel:InputChannel id:int = Updates;
channels.getAdminedPublicChannels#8d8d82d7 = messages.Chats;
channels.editBanned#bfd915cd channel:InputChannel user_id:InputUser banned_rights:ChannelBannedRights = Updates;
channels.editBanned#72796912 channel:InputChannel user_id:InputUser banned_rights:ChatBannedRights = Updates;
channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<InputUser> max_id:long min_id:long limit:int = channels.AdminLogResults;
channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool;
channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool;
@@ -1376,4 +1423,4 @@ langpack.getDifference#9d51e814 lang_code:string from_version:int = LangPackDiff
langpack.getLanguages#42c6978f lang_pack:string = Vector<LangPackLanguage>;
langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLanguage;
// LAYER 89
// LAYER 95

View File

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

View File

@@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,5,0,0
PRODUCTVERSION 1,5,0,0
FILEVERSION 1,5,11,0
PRODUCTVERSION 1,5,11,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -52,10 +52,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "1.5.0.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2018"
VALUE "FileVersion", "1.5.11.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2019"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "1.5.0.0"
VALUE "ProductVersion", "1.5.11.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,5,0,0
PRODUCTVERSION 1,5,0,0
FILEVERSION 1,5,11,0
PRODUCTVERSION 1,5,11,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -43,10 +43,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "1.5.0.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2018"
VALUE "FileVersion", "1.5.11.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2019"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "1.5.0.0"
VALUE "ProductVersion", "1.5.11.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -6,6 +6,7 @@ For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#import <Cocoa/Cocoa.h>
#include <sys/xattr.h>
NSString *appName = @"Telegram.app";
NSString *appDir = nil;
@@ -44,6 +45,20 @@ void writeLog(NSString *msg) {
[_logFile synchronizeFile];
}
void RemoveQuarantineAttribute(NSString *path) {
const char *kQuarantineAttribute = "com.apple.quarantine";
writeLog([@"Removing quarantine: " stringByAppendingString:path]);
removexattr([path fileSystemRepresentation], kQuarantineAttribute, 0);
}
void RemoveQuarantineFromBundle(NSString *path) {
RemoveQuarantineAttribute(path);
RemoveQuarantineAttribute([path stringByAppendingString:@"/Contents/MacOS/Telegram"]);
RemoveQuarantineAttribute([path stringByAppendingString:@"/Contents/Helpers/crashpad_handler"]);
RemoveQuarantineAttribute([path stringByAppendingString:@"/Contents/Frameworks/Updater"]);
}
void delFolder() {
writeLog([@"Fully clearing old path: " stringByAppendingString:[workDir stringByAppendingString:@"tupdates/ready"]]);
if (![[NSFileManager defaultManager] removeItemAtPath:[workDir stringByAppendingString:@"tupdates/ready"] error:nil]) {
@@ -232,6 +247,9 @@ int main(int argc, const char * argv[]) {
}
NSString *appPath = [[NSArray arrayWithObjects:appDir, appRealName, nil] componentsJoinedByString:@""];
RemoveQuarantineFromBundle(appPath);
NSMutableArray *args = [[NSMutableArray alloc] initWithObjects: @"-noupdate", nil];
if (toSettings) [args addObject:@"-tosettings"];
if (_debug) [args addObject:@"-debug"];

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer.h"
#include "base/flat_map.h"
#include "base/flat_set.h"
#include "core/single_timer.h"
#include "mtproto/sender.h"
#include "chat_helpers/stickers.h"
#include "data/data_messages.h"
@@ -24,6 +23,11 @@ enum class SendMediaType;
struct FileLoadTo;
class mtpFileLoader;
namespace Data {
struct UpdatedFileReferences;
class WallPaper;
} // namespace Data
namespace InlineBots {
class Result;
} // namespace InlineBots
@@ -43,14 +47,6 @@ struct CloudPasswordState;
namespace Api {
inline const MTPVector<MTPChat> *getChatsFromMessagesChats(const MTPmessages_Chats &chats) {
switch (chats.type()) {
case mtpc_messages_chats: return &chats.c_messages_chats().vchats;
case mtpc_messages_chatsSlice: return &chats.c_messages_chatsSlice().vchats;
}
return nullptr;
}
template <typename IntRange>
inline int32 CountHash(IntRange &&range) {
uint32 acc = 0;
@@ -62,7 +58,7 @@ inline int32 CountHash(IntRange &&range) {
} // namespace Api
class ApiWrap : private MTP::Sender, private base::Subscriber {
class ApiWrap : public MTP::Sender, private base::Subscriber {
public:
ApiWrap(not_null<AuthSession*> session);
@@ -87,16 +83,22 @@ public:
void requestDialogEntry(
not_null<History*> history,
Fn<void()> callback = nullptr);
void requestDialogEntries(std::vector<not_null<History*>> histories);
void dialogEntryApplied(not_null<History*> history);
//void applyFeedSources(const MTPDchannels_feedSources &data); // #feed
//void setFeedChannels(
// not_null<Data::Feed*> feed,
// const std::vector<not_null<ChannelData*>> &channels);
void changeDialogUnreadMark(not_null<History*> history, bool unread);
//void changeDialogUnreadMark(not_null<Data::Feed*> feed, bool unread); // #feed
void requestFakeChatListMessage(not_null<History*> history);
void requestFullPeer(PeerData *peer);
void requestPeer(PeerData *peer);
void requestWallPaper(
const QString &slug,
Fn<void(const Data::WallPaper &)> done,
Fn<void(const RPCError &)> fail);
void requestFullPeer(not_null<PeerData*> peer);
void requestPeer(not_null<PeerData*> peer);
void requestPeers(const QList<PeerData*> &peers);
void requestLastParticipants(not_null<ChannelData*> channel);
void requestBots(not_null<ChannelData*> channel);
@@ -134,24 +136,37 @@ public:
void requestChannelMembersForAdd(
not_null<ChannelData*> channel,
Fn<void(const MTPchannels_ChannelParticipants&)> callback);
void processFullPeer(PeerData *peer, const MTPmessages_ChatFull &result);
void processFullPeer(UserData *user, const MTPUserFull &result);
void processFullPeer(
not_null<PeerData*> peer,
const MTPmessages_ChatFull &result);
void processFullPeer(
not_null<UserData*> user,
const MTPUserFull &result);
void migrateChat(
not_null<ChatData*> chat,
FnMut<void(not_null<ChannelData*>)> done,
FnMut<void(const RPCError &)> fail = nullptr);
void markMediaRead(const base::flat_set<not_null<HistoryItem*>> &items);
void markMediaRead(not_null<HistoryItem*> item);
void requestSelfParticipant(ChannelData *channel);
void requestSelfParticipant(not_null<ChannelData*> channel);
void kickParticipant(not_null<ChatData*> chat, not_null<UserData*> user);
void kickParticipant(
not_null<ChannelData*> channel,
not_null<UserData*> user,
const MTPChannelBannedRights &currentRights);
const MTPChatBannedRights &currentRights);
void unblockParticipant(
not_null<ChannelData*> channel,
not_null<UserData*> user);
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);
@@ -209,12 +224,9 @@ public:
void jumpToDate(Dialogs::Key chat, const QDate &date);
void preloadEnoughUnreadMentions(not_null<History*> history);
void checkForUnreadMentions(const base::flat_set<MsgId> &possiblyReadMentions, ChannelData *channel = nullptr);
void editChatAdmins(
not_null<ChatData*> chat,
bool adminsEnabled,
base::flat_set<not_null<UserData*>> &&admins);
void checkForUnreadMentions(
const base::flat_set<MsgId> &possiblyReadMentions,
ChannelData *channel = nullptr);
using SliceType = Data::LoadDirection;
void requestSharedMedia(
@@ -260,6 +272,9 @@ public:
int availableCount,
const QVector<MTPChannelParticipant> &list)> callbackList,
Fn<void()> callbackNotModified = nullptr);
void addChatParticipants(
not_null<PeerData*> peer,
const std::vector<not_null<UserData*>> &users);
struct SendOptions {
SendOptions(not_null<History*> history);
@@ -329,7 +344,7 @@ public:
bool handleSupportSwitch = false;
};
void sendMessage(MessageToSend &&message);
void sendBotStart(not_null<UserData*> bot);
void sendBotStart(not_null<UserData*> bot, PeerData *chat = nullptr);
void sendInlineResult(
not_null<UserData*> bot,
not_null<InlineBots::Result*> data,
@@ -350,6 +365,11 @@ public:
rpl::producer<Core::CloudPasswordState> passwordState() const;
std::optional<Core::CloudPasswordState> passwordStateCurrent() const;
void reloadContactSignupSilent();
rpl::producer<bool> contactSignupSilent() const;
std::optional<bool> contactSignupSilentCurrent() const;
void saveContactSignupSilent(bool silent);
void saveSelfBio(const QString &text, FnMut<void()> done);
struct Privacy {
@@ -377,6 +397,17 @@ public:
rpl::producer<int> selfDestructValue() const;
void saveSelfDestruct(int days);
void createPoll(
const PollData &data,
const SendOptions &options,
FnMut<void()> done,
FnMut<void(const RPCError &error)> fail);
void sendPollVotes(
FullMsgId itemId,
const std::vector<QByteArray> &options);
void closePoll(FullMsgId itemId);
void reloadPollResults(not_null<HistoryItem*> item);
~ApiWrap();
private:
@@ -394,10 +425,6 @@ private:
TimeMs received = 0;
};
using SimpleFileLocationId = Data::SimpleFileLocationId;
using DocumentFileLocationId = Data::DocumentFileLocationId;
using FileLocationId = Data::FileLocationId;
void updatesReceived(const MTPUpdates &updates);
void checkQuitPreventFinished();
@@ -412,13 +439,18 @@ private:
QVector<MTPInputMessage> collectMessageIds(const MessageDataRequests &requests);
MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false);
void applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs);
void historyDialogEntryApplied(not_null<History*> history);
void applyFeedDialogs(
not_null<Data::Feed*> feed,
const MTPmessages_Dialogs &dialogs);
void gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mtpRequestId req);
void gotUserFull(UserData *user, const MTPUserFull &result, mtpRequestId req);
void gotChatFull(
not_null<PeerData*> peer,
const MTPmessages_ChatFull &result,
mtpRequestId req);
void gotUserFull(
not_null<UserData*> user,
const MTPUserFull &result,
mtpRequestId req);
void applyLastParticipantsList(
not_null<ChannelData*> channel,
int availableCount,
@@ -462,9 +494,6 @@ private:
void requestSavedGifs(TimeId now);
void readFeaturedSets();
void cancelEditChatAdmins(not_null<ChatData*> chat);
void saveChatAdmins(not_null<ChatData*> chat);
void sendSaveChatAdminsRequests(not_null<ChatData*> chat);
void refreshChannelAdmins(
not_null<ChannelData*> channel,
const QVector<MTPChannelParticipant> &participants);
@@ -568,6 +597,13 @@ private:
void setSelfDestructDays(int days);
void migrateDone(
not_null<PeerData*> peer,
not_null<ChannelData*> channel);
void migrateFail(not_null<PeerData*> peer, const RPCError &error);
void sendDialogRequests();
not_null<AuthSession*> _session;
MessageDataRequests _messageDataRequests;
@@ -596,7 +632,11 @@ private:
not_null<UserData*>>;
base::flat_map<KickRequest, mtpRequestId> _kickRequests;
QMap<ChannelData*, mtpRequestId> _selfParticipantRequests;
base::flat_map<
not_null<PeerData*>,
mtpRequestId> _defaultRestrictionsRequests;
base::flat_set<not_null<ChannelData*>> _selfParticipantRequests;
base::flat_map<
not_null<ChannelData*>,
@@ -638,19 +678,13 @@ private:
base::flat_map<
not_null<History*>,
std::vector<Fn<void()>>> _dialogRequests;
base::flat_map<
not_null<History*>,
std::vector<Fn<void()>>> _dialogRequestsPending;
base::flat_set<not_null<History*>> _fakeChatListRequests;
base::flat_map<not_null<History*>, mtpRequestId> _unreadMentionsRequests;
base::flat_map<
not_null<ChatData*>,
mtpRequestId> _chatAdminsEnabledRequests;
base::flat_map<
not_null<ChatData*>,
base::flat_set<not_null<UserData*>>> _chatAdminsToSave;
base::flat_map<
not_null<ChatData*>,
base::flat_set<mtpRequestId>> _chatAdminsSaveRequests;
base::flat_map<std::tuple<
not_null<PeerData*>,
SharedMediaType,
@@ -719,6 +753,14 @@ private:
FnMut<void(const MTPChatInvite &result)> _checkInviteDone;
FnMut<void(const RPCError &error)> _checkInviteFail;
struct MigrateCallbacks {
FnMut<void(not_null<ChannelData*>)> done;
FnMut<void(const RPCError&)> fail;
};
base::flat_map<
not_null<PeerData*>,
std::vector<MigrateCallbacks>> _migrateCallbacks;
std::vector<FnMut<void(const MTPUser &)>> _supportContactCallbacks;
base::flat_map<FullMsgId, not_null<PeerData*>> _peerPhotoUploads;
@@ -739,4 +781,17 @@ private:
std::optional<int> _selfDestructDays;
rpl::event_stream<int> _selfDestructChanges;
base::flat_map<FullMsgId, mtpRequestId> _pollVotesRequestIds;
base::flat_map<FullMsgId, mtpRequestId> _pollCloseRequestIds;
base::flat_map<FullMsgId, mtpRequestId> _pollReloadRequestIds;
mtpRequestId _wallPaperRequestId = 0;
QString _wallPaperSlug;
Fn<void(const Data::WallPaper &)> _wallPaperDone;
Fn<void(const RPCError &)> _wallPaperFail;
mtpRequestId _contactSignupSilentRequestId = 0;
std::optional<bool> _contactSignupSilent;
rpl::event_stream<bool> _contactSignupSilentChanges;
};

File diff suppressed because it is too large Load Diff

View File

@@ -8,16 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "data/data_types.h"
#include "data/data_peer.h"
enum NewMessageType : char;
enum class ImageRoundRadius;
class Messenger;
class MainWindow;
class MainWidget;
class HistoryItem;
class History;
class Histories;
namespace HistoryView {
class Element;
} // namespace HistoryView
@@ -67,16 +64,6 @@ namespace App {
QString formatPhone(QString phone);
UserData *feedUser(const MTPUser &user);
UserData *feedUsers(const MTPVector<MTPUser> &users); // returns last user
PeerData *feedChat(const MTPChat &chat);
PeerData *feedChats(const MTPVector<MTPChat> &chats); // returns last chat
void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos);
void feedParticipantAdd(const MTPDupdateChatParticipantAdd &d);
void feedParticipantDelete(const MTPDupdateChatParticipantDelete &d);
void feedChatAdmins(const MTPDupdateChatAdmins &d);
void feedParticipantAdmin(const MTPDupdateChatParticipantAdmin &d);
bool checkEntitiesAndViewsUpdate(const MTPDmessage &m); // returns true if item found and it is not detached
void updateEditedMessage(const MTPMessage &m);
void addSavedGif(DocumentData *doc);
@@ -90,68 +77,13 @@ namespace App {
ImagePtr image(const MTPPhotoSize &size);
PeerData *peer(const PeerId &id, PeerData::LoadedStatus restriction = PeerData::NotLoaded);
inline UserData *user(const PeerId &id, PeerData::LoadedStatus restriction = PeerData::NotLoaded) {
return asUser(peer(id, restriction));
}
inline ChatData *chat(const PeerId &id, PeerData::LoadedStatus restriction = PeerData::NotLoaded) {
return asChat(peer(id, restriction));
}
inline ChannelData *channel(const PeerId &id, PeerData::LoadedStatus restriction = PeerData::NotLoaded) {
return asChannel(peer(id, restriction));
}
inline UserData *user(UserId userId, PeerData::LoadedStatus restriction = PeerData::NotLoaded) {
return asUser(peer(peerFromUser(userId), restriction));
}
inline ChatData *chat(ChatId chatId, PeerData::LoadedStatus restriction = PeerData::NotLoaded) {
return asChat(peer(peerFromChat(chatId), restriction));
}
inline ChannelData *channel(ChannelId channelId, PeerData::LoadedStatus restriction = PeerData::NotLoaded) {
return asChannel(peer(peerFromChannel(channelId), restriction));
}
inline PeerData *peerLoaded(const PeerId &id) {
return peer(id, PeerData::FullLoaded);
}
inline UserData *userLoaded(const PeerId &id) {
return user(id, PeerData::FullLoaded);
}
inline ChatData *chatLoaded(const PeerId &id) {
return chat(id, PeerData::FullLoaded);
}
inline ChannelData *channelLoaded(const PeerId &id) {
return channel(id, PeerData::FullLoaded);
}
inline UserData *userLoaded(UserId userId) {
return user(userId, PeerData::FullLoaded);
}
inline ChatData *chatLoaded(ChatId chatId) {
return chat(chatId, PeerData::FullLoaded);
}
inline ChannelData *channelLoaded(ChannelId channelId) {
return channel(channelId, PeerData::FullLoaded);
}
void enumerateUsers(Fn<void(not_null<UserData*>)> action);
void enumerateGroups(Fn<void(not_null<PeerData*>)> action);
void enumerateChannels(Fn<void(not_null<ChannelData*>)> action);
[[nodiscard]] QString peerName(const PeerData *peer, bool forDialogs = false);
PeerData *peerByName(const QString &username);
QString peerName(const PeerData *peer, bool forDialogs = false);
Histories &histories();
not_null<History*> history(const PeerId &peer);
History *historyLoaded(const PeerId &peer);
HistoryItem *histItemById(ChannelId channelId, MsgId itemId);
inline not_null<History*> history(const PeerData *peer) {
Assert(peer != nullptr);
return history(peer->id);
}
inline History *historyLoaded(const PeerData *peer) {
return peer ? historyLoaded(peer->id) : nullptr;
}
inline HistoryItem *histItemById(const ChannelData *channel, MsgId itemId) {
return histItemById(channel ? peerToChannel(channel->id) : 0, itemId);
}
inline HistoryItem *histItemById(const FullMsgId &msgId) {
[[nodiscard]] HistoryItem *histItemById(ChannelId channelId, MsgId itemId);
[[nodiscard]] HistoryItem *histItemById(
const ChannelData *channel,
MsgId itemId);
[[nodiscard]] inline HistoryItem *histItemById(const FullMsgId &msgId) {
return histItemById(msgId.channel, msgId.msg);
}
void historyRegItem(not_null<HistoryItem*> item);
@@ -183,8 +115,6 @@ namespace App {
const style::font &monofont();
void clearHistories();
void initMedia();
void deinitMedia();
@@ -222,14 +152,4 @@ namespace App {
return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, radius, parts);
}
struct WallPaper {
WallPaper(int32 id, ImagePtr thumb, ImagePtr full) : id(id), thumb(thumb), full(full) {
}
int32 id;
ImagePtr thumb;
ImagePtr full;
};
typedef QList<WallPaper> WallPapers;
DeclareSetting(WallPapers, ServerBackgrounds);
};

View File

@@ -1,86 +0,0 @@
/*
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 Core {
class Launcher;
class UpdateChecker;
} // namespace Core
bool InternalPassportLink(const QString &url);
bool StartUrlRequiresActivate(const QString &url);
class Application : public QApplication {
Q_OBJECT
public:
Application(not_null<Core::Launcher*> launcher, int &argc, char **argv);
bool event(QEvent *e) override;
void createMessenger();
void refreshGlobalProxy();
~Application();
signals:
void adjustSingleTimers();
// Single instance application
public slots:
void socketConnected();
void socketError(QLocalSocket::LocalSocketError e);
void socketDisconnected();
void socketWritten(qint64 bytes);
void socketReading();
void newInstanceConnected();
void readClients();
void removeClients();
void startApplication(); // will be done in exec()
void closeApplication(); // will be done in aboutToQuit()
private:
typedef QPair<QLocalSocket*, QByteArray> LocalClient;
typedef QList<LocalClient> LocalClients;
not_null<Core::Launcher*> _launcher;
std::unique_ptr<Messenger> _messengerInstance;
QString _localServerName, _localSocketReadData;
QLocalServer _localServer;
QLocalSocket _localSocket;
LocalClients _localClients;
bool _secondInstance = false;
void singleInstanceChecked();
private:
std::unique_ptr<Core::UpdateChecker> _updateChecker;
};
namespace Sandbox {
QRect availableGeometry();
QRect screenGeometry(const QPoint &p);
void setActiveWindow(QWidget *window);
bool isSavingSession();
void execExternal(const QString &cmd);
void adjustSingleTimers();
void refreshGlobalProxy();
void connect(const char *signal, QObject *object, const char *method);
void launch();
} // namespace Sandbox

View File

@@ -8,7 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "auth_session.h"
#include "apiwrap.h"
#include "messenger.h"
#include "core/application.h"
#include "core/sandbox.h"
#include "core/changelogs.h"
#include "storage/file_download.h"
#include "storage/file_upload.h"
@@ -16,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_facade.h"
#include "storage/serialize_common.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "window/notifications_manager.h"
#include "window/themes/window_theme.h"
#include "platform/platform_specific.h"
@@ -45,11 +47,13 @@ AuthSessionSettings::Variables::Variables()
}
QByteArray AuthSessionSettings::serialize() const {
const auto autoDownload = _variables.autoDownload.serialize();
auto size = sizeof(qint32) * 23;
for (auto i = _variables.soundOverrides.cbegin(), e = _variables.soundOverrides.cend(); i != e; ++i) {
size += Serialize::stringSize(i.key()) + Serialize::stringSize(i.value());
}
size += _variables.groupStickersSectionHidden.size() * sizeof(quint64);
size += Serialize::bytearraySize(autoDownload);
auto result = QByteArray();
result.reserve(size);
@@ -88,6 +92,8 @@ QByteArray AuthSessionSettings::serialize() const {
stream << qint32(_variables.includeMutedCounter ? 1 : 0);
stream << qint32(_variables.countUnreadMessages ? 1 : 0);
stream << qint32(_variables.exeLaunchWarning ? 1 : 0);
stream << autoDownload;
stream << qint32(_variables.supportAllSearchResults.current() ? 1 : 0);
}
return result;
}
@@ -122,6 +128,8 @@ void AuthSessionSettings::constructFromSerialized(const QByteArray &serialized)
qint32 includeMutedCounter = _variables.includeMutedCounter ? 1 : 0;
qint32 countUnreadMessages = _variables.countUnreadMessages ? 1 : 0;
qint32 exeLaunchWarning = _variables.exeLaunchWarning ? 1 : 0;
QByteArray autoDownload;
qint32 supportAllSearchResults = _variables.supportAllSearchResults.current() ? 1 : 0;
stream >> selectorTab;
stream >> lastSeenWarningSeen;
@@ -195,11 +203,21 @@ void AuthSessionSettings::constructFromSerialized(const QByteArray &serialized)
if (!stream.atEnd()) {
stream >> exeLaunchWarning;
}
if (!stream.atEnd()) {
stream >> autoDownload;
}
if (!stream.atEnd()) {
stream >> supportAllSearchResults;
}
if (stream.status() != QDataStream::Ok) {
LOG(("App Error: "
"Bad data for AuthSessionSettings::constructFromSerialized()"));
return;
}
if (!autoDownload.isEmpty()
&& !_variables.autoDownload.setFromSerialized(autoDownload)) {
return;
}
auto uncheckedTab = static_cast<ChatHelpers::SelectorTab>(selectorTab);
switch (uncheckedTab) {
@@ -259,6 +277,7 @@ void AuthSessionSettings::constructFromSerialized(const QByteArray &serialized)
_variables.includeMutedCounter = (includeMutedCounter == 1);
_variables.countUnreadMessages = (countUnreadMessages == 1);
_variables.exeLaunchWarning = (exeLaunchWarning == 1);
_variables.supportAllSearchResults = (supportAllSearchResults == 1);
}
void AuthSessionSettings::setSupportChatsTimeSlice(int slice) {
@@ -273,6 +292,18 @@ rpl::producer<int> AuthSessionSettings::supportChatsTimeSliceValue() const {
return _variables.supportChatsTimeSlice.value();
}
void AuthSessionSettings::setSupportAllSearchResults(bool all) {
_variables.supportAllSearchResults = all;
}
bool AuthSessionSettings::supportAllSearchResults() const {
return _variables.supportAllSearchResults.current();
}
rpl::producer<bool> AuthSessionSettings::supportAllSearchResultsValue() const {
return _variables.supportAllSearchResults.value();
}
void AuthSessionSettings::setTabbedSelectorSectionEnabled(bool enabled) {
_variables.tabbedSelectorSectionEnabled = enabled;
if (enabled) {
@@ -342,14 +373,13 @@ rpl::producer<int> AuthSessionSettings::thirdColumnWidthChanges() const {
}
AuthSession &Auth() {
auto result = Messenger::Instance().authSession();
auto result = Core::App().authSession();
Assert(result != nullptr);
return *result;
}
AuthSession::AuthSession(const MTPUser &user)
: _user(App::user(user.match([](const auto &data) { return data.vid.v; })))
, _autoLockTimer([this] { checkAutoLock(); })
: _autoLockTimer([this] { checkAutoLock(); })
, _api(std::make_unique<ApiWrap>(this))
, _calls(std::make_unique<Calls::Instance>())
, _downloader(std::make_unique<Storage::Downloader>())
@@ -357,21 +387,17 @@ AuthSession::AuthSession(const MTPUser &user)
, _storage(std::make_unique<Storage::Facade>())
, _notifications(std::make_unique<Window::Notifications::System>(this))
, _data(std::make_unique<Data::Session>(this))
, _user(_data->processUser(user))
, _changelogs(Core::Changelogs::Create(this))
, _supportHelper(
(Support::ValidateAccount(user)
? std::make_unique<Support::Helper>(this)
: nullptr)) {
App::feedUser(user);
, _supportHelper(Support::Helper::Create(this)) {
_saveDataTimer.setCallback([=] {
Local::writeUserSettings();
});
Messenger::Instance().passcodeLockChanges(
Core::App().passcodeLockChanges(
) | rpl::start_with_next([=] {
_shouldLockAt = 0;
}, _lifetime);
Messenger::Instance().lockChanges(
Core::App().lockChanges(
) | rpl::start_with_next([=] {
notifications().updateAll();
}, _lifetime);
@@ -404,23 +430,29 @@ AuthSession::AuthSession(const MTPUser &user)
}
bool AuthSession::Exists() {
if (const auto messenger = Messenger::InstancePointer()) {
return (messenger->authSession() != nullptr);
}
return false;
return Core::Sandbox::Instance().applicationLaunched()
&& (Core::App().authSession() != nullptr);
}
base::Observable<void> &AuthSession::downloaderTaskFinished() {
return downloader().taskFinished();
}
UserId AuthSession::userId() const {
return _user->bareId();
}
PeerId AuthSession::userPeerId() const {
return _user->id;
}
bool AuthSession::validateSelf(const MTPUser &user) {
if (user.type() != mtpc_user || !user.c_user().is_self()) {
LOG(("API Error: bad self user received."));
return false;
} else if (user.c_user().vid.v != userId()) {
LOG(("Auth Error: wrong self user received."));
crl::on_main(this, [] { Messenger::Instance().logOut(); });
crl::on_main(this, [] { Core::App().logOut(); });
return false;
}
return true;
@@ -446,18 +478,18 @@ void AuthSession::saveSettingsDelayed(TimeMs delay) {
void AuthSession::checkAutoLock() {
if (!Global::LocalPasscode()
|| Messenger::Instance().passcodeLocked()) {
|| Core::App().passcodeLocked()) {
return;
}
Messenger::Instance().checkLocalTime();
Core::App().checkLocalTime();
auto now = getms(true);
auto shouldLockInMs = Global::AutoLock() * 1000LL;
auto idleForMs = psIdleTime();
auto notPlayingVideoForMs = now - settings().lastTimeVideoPlayedAt();
auto checkTimeMs = qMin(idleForMs, notPlayingVideoForMs);
if (checkTimeMs >= shouldLockInMs || (_shouldLockAt > 0 && now > _shouldLockAt + kAutoLockTimeoutLateMs)) {
Messenger::Instance().lockByPasscode();
Core::App().lockByPasscode();
} else {
_shouldLockAt = now + (shouldLockInMs - checkTimeMs);
_autoLockTimer.callOnce(shouldLockInMs - checkTimeMs);
@@ -486,4 +518,7 @@ Support::Templates& AuthSession::supportTemplates() const {
return supportHelper().templates();
}
AuthSession::~AuthSession() = default;
AuthSession::~AuthSession() {
ClickHandler::clearActive();
ClickHandler::unpressed();
}

View File

@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/filter.h>
#include <rpl/variable.h>
#include "base/timer.h"
#include "data/data_auto_download.h"
class ApiWrap;
enum class SendFilesWay;
@@ -102,6 +103,9 @@ public:
void setSupportChatsTimeSlice(int slice);
int supportChatsTimeSlice() const;
rpl::producer<int> supportChatsTimeSliceValue() const;
void setSupportAllSearchResults(bool all);
bool supportAllSearchResults() const;
rpl::producer<bool> supportAllSearchResultsValue() const;
ChatHelpers::SelectorTab selectorTab() const {
return _variables.selectorTab;
@@ -183,6 +187,13 @@ public:
_variables.groupStickersSectionHidden.remove(peerId);
}
Data::AutoDownload::Full &autoDownload() {
return _variables.autoDownload;
}
const Data::AutoDownload::Full &autoDownload() const {
return _variables.autoDownload;
}
bool hadLegacyCallsPeerToPeerNobody() const {
return _variables.hadLegacyCallsPeerToPeerNobody;
}
@@ -234,6 +245,7 @@ private:
bool includeMutedCounter = true;
bool countUnreadMessages = true;
bool exeLaunchWarning = true;
Data::AutoDownload::Full autoDownload;
static constexpr auto kDefaultSupportChatsLimitSlice
= 7 * 24 * 60 * 60;
@@ -243,6 +255,7 @@ private:
bool supportTemplatesAutocomplete = true;
rpl::variable<int> supportChatsTimeSlice
= kDefaultSupportChatsLimitSlice;
rpl::variable<bool> supportAllSearchResults = false;
};
rpl::event_stream<bool> _thirdSectionInfoEnabledValue;
@@ -254,7 +267,6 @@ private:
};
// One per Messenger.
class AuthSession;
AuthSession &Auth();
@@ -269,12 +281,8 @@ public:
static bool Exists();
UserId userId() const {
return _user->bareId();
}
PeerId userPeerId() const {
return _user->id;
}
UserId userId() const;
PeerId userPeerId() const;
not_null<UserData*> user() const {
return _user;
}
@@ -332,7 +340,6 @@ public:
private:
static constexpr auto kDefaultSaveDelay = TimeMs(1000);
const not_null<UserData*> _user;
AuthSessionSettings _settings;
base::Timer _saveDataTimer;
@@ -346,8 +353,9 @@ private:
const std::unique_ptr<Storage::Facade> _storage;
const std::unique_ptr<Window::Notifications::System> _notifications;
// _data depends on _downloader / _uploader, including destructor.
// _data depends on _downloader / _uploader / _notifications.
const std::unique_ptr<Data::Session> _data;
const not_null<UserData*> _user;
// _changelogs depends on _data, subscribes on chats loading event.
const std::unique_ptr<Core::Changelogs> _changelogs;

View File

@@ -24,6 +24,12 @@ inline constexpr size_t array_size(const Type(&)[Size]) {
return Size;
}
template <typename Container, typename T>
inline bool contains(const Container &container, const T &value) {
const auto end = std::end(container);
return std::find(std::begin(container), end, value) != end;
}
} // namespace base
template <typename T>

View File

@@ -19,7 +19,10 @@ void log(const char *message, const char *file, int line);
inline constexpr void noop() {
}
[[noreturn]] inline void fail(const char *message, const char *file, int line) {
[[noreturn]] inline void fail(
const char *message,
const char *file,
int line) {
log(message, file, line);
// Crash with access violation and generate crash report.
@@ -30,24 +33,36 @@ inline constexpr void noop() {
std::abort();
}
#ifndef GSL_UNLIKELY
#define DEFINED_GSL_UNLIKELY_
#define GSL_UNLIKELY(expression) (expression)
#endif // GSL_UNLIKELY
inline constexpr void validate(bool condition, const char *message, const char *file, int line) {
(GSL_UNLIKELY(!(condition))) ? fail(message, file, line) : noop();
constexpr const char* extract_basename(const char* path, size_t size) {
while (size != 0 && path[size - 1] != '/' && path[size - 1] != '\\') {
--size;
}
return path + size;
}
#ifdef DEFINED_GSL_UNLIKELY_
#undef GSL_UNLIKELY
#undef DEFINED_GSL_UNLIKELY_
#endif // DEFINED_GSL_UNLIKELY_
} // namespace assertion
} // namespace base
#define AssertCustom(condition, message) (::base::assertion::validate(condition, message, __FILE__, __LINE__))
#if defined(__clang__) || defined(__GNUC__)
#define AssertUnlikelyHelper(x) __builtin_expect(!!(x), 0)
#else
#define AssertUnlikelyHelper(x) (!!(x))
#endif
#define AssertValidationCondition(condition, message, file, line)\
((AssertUnlikelyHelper(!(condition)))\
? ::base::assertion::fail(message, file, line)\
: ::base::assertion::noop())
#define SOURCE_FILE_BASENAME (::base::assertion::extract_basename(\
__FILE__,\
sizeof(__FILE__)))
#define AssertCustom(condition, message) (AssertValidationCondition(\
condition,\
message,\
SOURCE_FILE_BASENAME,\
__LINE__))
#define Assert(condition) AssertCustom(condition, "\"" #condition "\"")
// Define our own versions of Expects() and Ensures().
@@ -55,17 +70,28 @@ inline constexpr void validate(bool condition, const char *message, const char *
#ifdef Expects
#undef Expects
#endif // Expects
#define Expects(condition) (::base::assertion::validate(condition, "\"" #condition "\"", __FILE__, __LINE__))
#define Expects(condition) (AssertValidationCondition(\
condition,\
"\"" #condition "\"",\
SOURCE_FILE_BASENAME,\
__LINE__))
#ifdef Ensures
#undef Ensures
#endif // Ensures
#define Ensures(condition) (::base::assertion::validate(condition, "\"" #condition "\"", __FILE__, __LINE__))
#define Ensures(condition) (AssertValidationCondition(\
condition,\
"\"" #condition "\"",\
SOURCE_FILE_BASENAME,\
__LINE__))
#ifdef Unexpected
#undef Unexpected
#endif // Unexpected
#define Unexpected(message) (::base::assertion::fail("Unexpected: " message, __FILE__, __LINE__))
#define Unexpected(message) (::base::assertion::fail(\
"Unexpected: " message,\
SOURCE_FILE_BASENAME,\
__LINE__))
#ifdef _DEBUG
#define AssertIsDebug(...)

View File

@@ -21,7 +21,9 @@ public:
~binary_guard();
bool alive() const;
void kill();
binary_guard &operator=(std::nullptr_t);
explicit operator bool() const;
private:
void destroy();
@@ -44,15 +46,20 @@ inline binary_guard &binary_guard::operator=(binary_guard &&other) {
return *this;
}
inline binary_guard::~binary_guard() {
inline binary_guard &binary_guard::operator=(std::nullptr_t) {
destroy();
return *this;
}
inline binary_guard::operator bool() const {
return alive();
}
inline bool binary_guard::alive() const {
return _bothAlive && _bothAlive->load();
}
inline void binary_guard::kill() {
inline binary_guard::~binary_guard() {
destroy();
}

View File

@@ -301,11 +301,11 @@ void ConcurrentTimer::cancelAndSchedule(int timeout) {
runner = _runner,
guard = std::move(guards.second)
]() mutable {
if (!guard.alive()) {
if (!guard) {
return;
}
runner([=, guard = std::move(guard)] {
if (!guard.alive()) {
if (!guard) {
return;
}
timerEvent();

View File

@@ -172,15 +172,28 @@ public:
return error();
}
int goToFirstFile() {
if (error() == UNZ_OK) {
_error = _handle ? unzGoToFirstFile(_handle) : -1;
}
return error();
}
int goToNextFile() {
if (error() == UNZ_OK) {
_error = _handle ? unzGoToNextFile(_handle) : -1;
}
return error();
}
int getCurrentFileInfo(
unz_file_info *pfile_info,
char *szFileName,
uLong fileNameBufferSize,
void *extraField,
uLong extraFieldBufferSize,
char *szComment,
uLong commentBufferSize
) {
unz_file_info *pfile_info,
char *szFileName,
uLong fileNameBufferSize,
void *extraField,
uLong extraFieldBufferSize,
char *szComment,
uLong commentBufferSize) {
if (error() == UNZ_OK) {
_error = _handle ? unzGetCurrentFileInfo(
_handle,
@@ -196,6 +209,21 @@ public:
return error();
}
QString getCurrentFileName() {
unz_file_info info = { 0 };
constexpr auto kMaxName = 128;
char name[kMaxName + 1] = { 0 };
const auto result = getCurrentFileInfo(
&info,
name,
kMaxName,
nullptr,
0,
nullptr,
0);
return (result == UNZ_OK) ? QString::fromUtf8(name) : QString();
}
int openCurrentFile() {
if (error() == UNZ_OK) {
_error = _handle ? unzOpenCurrentFile(_handle) : -1;

View File

@@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h"
#include "mainwindow.h"
#include "boxes/confirm_box.h"
#include "application.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "styles/style_boxes.h"
@@ -70,7 +69,7 @@ void AboutBox::showVersionHistory() {
}
url = url.arg(qsl("talpha%1_%2").arg(cRealAlphaVersion()).arg(Core::countAlphaVersionSignature(cRealAlphaVersion())));
Application::clipboard()->setText(url);
QApplication::clipboard()->setText(url);
Ui::show(Box<InformBox>("The link to the current private alpha version of Telegram Desktop was copied to the clipboard."));
} else {
@@ -87,13 +86,19 @@ void AboutBox::keyPressEvent(QKeyEvent *e) {
}
QString telegramFaqLink() {
auto result = qsl("https://telegram.org/faq");
auto language = Lang::Current().id();
for (auto faqLanguage : { "de", "es", "it", "ko", "br" }) {
if (language.startsWith(QLatin1String(faqLanguage))) {
result.append('/').append(faqLanguage);
const auto result = qsl("https://telegram.org/faq");
const auto langpacked = [&](const char *language) {
return result + '/' + language;
};
const auto current = Lang::Current().id();
for (const auto language : { "de", "es", "it", "ko" }) {
if (current.startsWith(QLatin1String(language))) {
return langpacked(language);
}
}
if (current.startsWith(qstr("pt-br"))) {
return langpacked("br");
}
return result;
}

View File

@@ -378,11 +378,14 @@ void AbstractBox::updateButtonsPositions() {
if (_leftButton) {
_leftButton->moveToLeft(right, top);
}
for_const (auto &button, _buttons) {
for (const auto &button : _buttons) {
button->moveToRight(right, top);
right += button->width() + padding.left();
}
}
if (_topButton) {
_topButton->moveToRight(0, 0);
}
}
QPointer<QWidget> AbstractBox::outerContainer() {
@@ -403,6 +406,7 @@ void AbstractBox::clearButtons() {
button.destroy();
}
_leftButton.destroy();
_topButton = nullptr;
}
QPointer<Ui::RoundButton> AbstractBox::addButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) {
@@ -423,6 +427,15 @@ QPointer<Ui::RoundButton> AbstractBox::addLeftButton(Fn<QString()> textFactory,
return result;
}
QPointer<Ui::IconButton> AbstractBox::addTopButton(const style::IconButton &st, Fn<void()> clickCallback) {
_topButton = base::make_unique_q<Ui::IconButton>(this, st);
auto result = QPointer<Ui::IconButton>(_topButton.get());
result->setClickedCallback(std::move(clickCallback));
result->show();
updateButtonsPositions();
return result;
}
void AbstractBox::setDimensions(int newWidth, int maxHeight) {
_maxContentHeight = maxHeight;

View File

@@ -8,10 +8,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "window/layer_widget.h"
#include "base/unique_qptr.h"
#include "ui/rp_widget.h"
namespace style {
struct RoundButton;
struct IconButton;
struct ScrollArea;
} // namespace style
@@ -35,6 +37,7 @@ public:
virtual void clearButtons() = 0;
virtual QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) = 0;
virtual QPointer<Ui::RoundButton> addLeftButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) = 0;
virtual QPointer<Ui::IconButton> addTopButton(const style::IconButton &st, Fn<void()> clickCallback) = 0;
virtual void updateButtonsPositions() = 0;
virtual void showBox(
@@ -100,8 +103,14 @@ public:
void clearButtons() {
getDelegate()->clearButtons();
}
QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, Fn<void()> clickCallback);
QPointer<Ui::RoundButton> addLeftButton(Fn<QString()> textFactory, Fn<void()> clickCallback);
QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, Fn<void()> clickCallback = nullptr);
QPointer<Ui::RoundButton> addLeftButton(Fn<QString()> textFactory, Fn<void()> clickCallback = nullptr);
QPointer<Ui::IconButton> addTopButton(const style::IconButton &st, Fn<void()> clickCallback = nullptr) {
return getDelegate()->addTopButton(st, std::move(clickCallback));
}
QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, const style::RoundButton &st) {
return getDelegate()->addButton(std::move(textFactory), nullptr, st);
}
QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) {
return getDelegate()->addButton(std::move(textFactory), std::move(clickCallback), st);
}
@@ -251,6 +260,7 @@ public:
void clearButtons() override;
QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) override;
QPointer<Ui::RoundButton> addLeftButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) override;
QPointer<Ui::IconButton> addTopButton(const style::IconButton &st, Fn<void()> clickCallback) override;
void updateButtonsPositions() override;
QPointer<QWidget> outerContainer() override;
@@ -319,6 +329,7 @@ private:
std::vector<object_ptr<Ui::RoundButton>> _buttons;
object_ptr<Ui::RoundButton> _leftButton = { nullptr };
base::unique_qptr<Ui::IconButton> _topButton = { nullptr };
};
@@ -337,3 +348,45 @@ enum CreatingGroupType {
CreatingGroupGroup,
CreatingGroupChannel,
};
class BoxPointer {
public:
BoxPointer() = default;
BoxPointer(const BoxPointer &other) = default;
BoxPointer(BoxPointer &&other) : _value(base::take(other._value)) {
}
BoxPointer &operator=(const BoxPointer &other) {
if (_value != other._value) {
destroy();
_value = other._value;
}
return *this;
}
BoxPointer &operator=(BoxPointer &&other) {
if (_value != other._value) {
destroy();
_value = base::take(other._value);
}
return *this;
}
BoxPointer &operator=(BoxContent *other) {
if (_value != other) {
destroy();
_value = other;
}
return *this;
}
~BoxPointer() {
destroy();
}
private:
void destroy() {
if (const auto value = base::take(_value)) {
value->closeBox();
}
}
QPointer<BoxContent> _value;
};

View File

@@ -10,13 +10,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_boxes.h"
#include "styles/style_dialogs.h"
#include "lang/lang_keys.h"
#include "messenger.h"
#include "mtproto/sender.h"
#include "base/flat_set.h"
#include "boxes/confirm_box.h"
#include "boxes/photo_crop_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/peers/add_participants_box.h"
#include "boxes/peers/edit_participant_box.h"
#include "boxes/peers/edit_participants_box.h"
#include "core/file_utilities.h"
#include "core/application.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
@@ -25,6 +28,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h"
#include "ui/special_buttons.h"
#include "ui/text_options.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "data/data_session.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "apiwrap.h"
@@ -59,7 +66,7 @@ style::InputField CreateBioFieldStyle() {
QString PeerFloodErrorText(PeerFloodType type) {
auto link = textcmdLink(
Messenger::Instance().createInternalLinkFull(qsl("spambot")),
Core::App().createInternalLinkFull(qsl("spambot")),
lang(lng_cant_more_info));
if (type == PeerFloodType::InviteGroup) {
return lng_cant_invite_not_contact(lt_more_info, link);
@@ -67,6 +74,77 @@ QString PeerFloodErrorText(PeerFloodType type) {
return lng_cant_send_to_not_contact(lt_more_info, link);
}
void ShowAddParticipantsError(
const QString &error,
not_null<PeerData*> chat,
const std::vector<not_null<UserData*>> &users) {
if (error == qstr("USER_BOT")) {
const auto channel = chat->asChannel();
if ((users.size() == 1)
&& (users.front()->botInfo != nullptr)
&& channel
&& !channel->isMegagroup()
&& channel->canAddAdmins()) {
const auto makeAdmin = [=] {
const auto user = users.front();
const auto weak = std::make_shared<QPointer<EditAdminBox>>();
const auto close = [=](auto&&...) {
if (*weak) {
(*weak)->closeBox();
}
};
const auto saveCallback = SaveAdminCallback(
channel,
user,
close,
close);
auto box = Box<EditAdminBox>(
channel,
user,
MTP_chatAdminRights(MTP_flags(0)));
box->setSaveCallback(saveCallback);
*weak = Ui::show(std::move(box));
};
Ui::show(
Box<ConfirmBox>(
lang(lng_cant_invite_offer_admin),
lang(lng_cant_invite_make_admin),
lang(lng_cancel),
makeAdmin),
LayerOption::KeepOther);
return;
}
}
const auto bot = ranges::find_if(users, [](not_null<UserData*> user) {
return user->botInfo != nullptr;
});
const auto hasBot = (bot != end(users));
const auto text = [&] {
if (error == qstr("USER_BOT")) {
return lang(lng_cant_invite_bot_to_channel);
} else if (error == qstr("USER_LEFT_CHAT")) {
// Trying to return a user who has left.
} else if (error == qstr("USER_KICKED")) {
// Trying to return a user who was kicked by admin.
return lang(lng_cant_invite_banned);
} else if (error == qstr("USER_PRIVACY_RESTRICTED")) {
return lang(lng_cant_invite_privacy);
} else if (error == qstr("USER_NOT_MUTUAL_CONTACT")) {
// Trying to return user who does not have me in contacts.
return lang(lng_failed_add_not_mutual);
} else if (error == qstr("USER_ALREADY_PARTICIPANT") && hasBot) {
return lang(lng_bot_already_in_group);
} else if (error == qstr("PEER_FLOOD")) {
const auto isGroup = (chat->isChat() || chat->isMegagroup());
return PeerFloodErrorText(isGroup
? PeerFloodType::InviteGroup
: PeerFloodType::InviteChannel);
}
return lang(lng_failed_add_participant);
}();
Ui::show(Box<InformBox>(text), LayerOption::KeepOther);
}
class RevokePublicLinkBox::Inner : public TWidget, private MTP::Sender {
public:
Inner(QWidget *parent, Fn<void()> revokeCallback);
@@ -244,8 +322,9 @@ bool AddContactBox::onSaveUserFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
_addRequest = 0;
QString err(error.type());
QString firstName = _first->getLastText().trimmed(), lastName = _last->getLastText().trimmed();
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();
@@ -263,21 +342,21 @@ void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) {
if (!isBoxShown() || !App::main()) return;
const auto &d = res.c_contacts_importedContacts();
App::feedUsers(d.vusers);
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 App::userLoaded(c.vuser_id.v);
return Auth().data().userLoaded(c.vuser_id.v);
}
}
return nullptr;
}();
if (user) {
if (user->contactStatus() == UserData::ContactStatus::Contact
|| Auth().supportMode()) {
|| user->session().supportMode()) {
Ui::showPeerHistory(user, ShowAtTheEndMsgId);
}
Ui::hideLayer();
@@ -291,7 +370,7 @@ void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) {
void AddContactBox::onSaveUserDone(const MTPcontacts_ImportedContacts &res) {
auto &d = res.c_contacts_importedContacts();
App::feedUsers(d.vusers);
Auth().data().processUsers(d.vusers);
closeBox();
}
@@ -329,9 +408,9 @@ void GroupInfoBox::prepare() {
_photo.create(
this,
(_creating == CreatingGroupChannel)
? peerFromChannel(0)
: peerFromChat(0),
lang((_creating == CreatingGroupChannel)
? lng_create_channel_crop
: lng_create_group_crop),
Ui::UserpicButton::Role::ChangePhoto,
st::defaultUserpicButton);
_title.create(
@@ -458,11 +537,13 @@ void GroupInfoBox::createGroup(
: std::nullopt;
}
| [](auto chats) {
return App::chat(chats->front().c_chat().vid.v);
return Auth().data().chat(chats->front().c_chat().vid.v);
}
| [&](not_null<ChatData*> chat) {
if (!image.isNull()) {
Auth().api().uploadPeerPhoto(chat, std::move(image));
chat->session().api().uploadPeerPhoto(
chat,
std::move(image));
}
Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
};
@@ -531,8 +612,7 @@ void GroupInfoBox::submit() {
};
Ui::show(
Box<PeerListBox>(
std::make_unique<AddParticipantsBoxController>(
nullptr),
std::make_unique<AddParticipantsBoxController>(),
std::move(initBox)),
LayerOption::KeepOther);
}
@@ -541,7 +621,11 @@ void GroupInfoBox::submit() {
void GroupInfoBox::createChannel(const QString &title, const QString &description) {
bool mega = false;
auto flags = mega ? MTPchannels_CreateChannel::Flag::f_megagroup : MTPchannels_CreateChannel::Flag::f_broadcast;
_creationRequestId = request(MTPchannels_CreateChannel(MTP_flags(flags), MTP_string(title), MTP_string(description))).done([this](const MTPUpdates &result) {
_creationRequestId = request(MTPchannels_CreateChannel(
MTP_flags(flags),
MTP_string(title),
MTP_string(description)
)).done([=](const MTPUpdates &result) {
Auth().api().applyUpdates(result);
auto success = base::make_optional(&result)
@@ -561,17 +645,19 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
: std::nullopt;
}
| [](auto chats) {
return App::channel(chats->front().c_channel().vid.v);
return Auth().data().channel(chats->front().c_channel().vid.v);
}
| [this](not_null<ChannelData*> channel) {
| [&](not_null<ChannelData*> channel) {
auto image = _photo->takeResultImage();
if (!image.isNull()) {
Auth().api().uploadPeerPhoto(channel, std::move(image));
channel->session().api().uploadPeerPhoto(
channel,
std::move(image));
}
_createdChannel = channel;
_creationRequestId = request(
MTPchannels_ExportInvite(_createdChannel->inputChannel)
).done([this](const MTPExportedChatInvite &result) {
_creationRequestId = request(MTPmessages_ExportChatInvite(
_createdChannel->input
)).done([=](const MTPExportedChatInvite &result) {
_creationRequestId = 0;
if (result.type() == mtpc_chatInviteExported) {
auto link = qs(result.c_chatInviteExported().vlink);
@@ -749,7 +835,7 @@ void SetupChannelBox::mouseMoveEvent(QMouseEvent *e) {
void SetupChannelBox::mousePressEvent(QMouseEvent *e) {
if (_linkOver) {
if (_channel->inviteLink().isEmpty()) {
Auth().api().exportInviteLink(_channel);
_channel->session().api().exportInviteLink(_channel);
} else {
QGuiApplication::clipboard()->setText(_channel->inviteLink());
Ui::Toast::Show(lang(lng_create_channel_link_copied));
@@ -1069,7 +1155,7 @@ void EditNameBox::save() {
}
void EditNameBox::saveSelfDone(const MTPUser &user) {
App::feedUsers(MTP_vector<MTPUser>(1, user));
_user->owner().processUsers(MTP_vector<MTPUser>(1, user));
closeBox();
}
@@ -1096,261 +1182,6 @@ bool EditNameBox::saveSelfFail(const RPCError &error) {
return true;
}
EditChannelBox::EditChannelBox(QWidget*, not_null<ChannelData*> channel)
: _channel(channel)
, _title(this, st::defaultInputField, langFactory(_channel->isMegagroup() ? lng_dlg_new_group_name : lng_dlg_new_channel_name), _channel->name)
, _description(
this,
st::newGroupDescription,
Ui::InputField::Mode::MultiLine,
langFactory(lng_create_group_description),
_channel->about())
, _sign(this, lang(lng_edit_sign_messages), channel->addsSignature(), st::defaultBoxCheckbox)
, _inviteGroup(std::make_shared<Ui::RadioenumGroup<Invites>>(channel->anyoneCanAddMembers() ? Invites::Everybody : Invites::OnlyAdmins))
, _inviteEverybody(this, _inviteGroup, Invites::Everybody, lang(lng_edit_group_invites_everybody))
, _inviteOnlyAdmins(this, _inviteGroup, Invites::OnlyAdmins, lang(lng_edit_group_invites_only_admins))
, _publicLink(this, lang(channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link), st::boxLinkButton) {
}
void EditChannelBox::prepare() {
setTitle(langFactory(_channel->isMegagroup() ? lng_edit_group : lng_edit_channel_title));
addButton(langFactory(lng_settings_save), [this] { save(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::NameChanged, [this](const Notify::PeerUpdate &update) {
if (update.peer == _channel) {
handleChannelNameChange();
}
}));
setMouseTracking(true);
_title->setMaxLength(kMaxGroupChannelTitle);
_title->setInstantReplaces(Ui::InstantReplaces::Default());
_title->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_title);
_description->setMaxLength(kMaxChannelDescription);
_description->setInstantReplaces(Ui::InstantReplaces::Default());
_description->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
connect(_description, &Ui::InputField::resized, [=] { descriptionResized(); });
connect(_description, &Ui::InputField::submitted, [=] { save(); });
connect(_description, &Ui::InputField::cancelled, [=] { closeBox(); });
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_description);
_publicLink->addClickHandler([=] { setupPublicLink(); });
_publicLink->setVisible(_channel->canEditUsername());
_sign->setVisible(canEditSignatures());
_inviteEverybody->setVisible(canEditInvites());
_inviteOnlyAdmins->setVisible(canEditInvites());
updateMaxHeight();
}
void EditChannelBox::setInnerFocus() {
_title->setFocusFast();
}
void EditChannelBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
if (_title->hasFocus()) {
save();
}
} else {
BoxContent::keyPressEvent(e);
}
}
void EditChannelBox::handleChannelNameChange() {
_publicLink->setText(lang(_channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link));
_sign->setChecked(_channel->addsSignature());
}
void EditChannelBox::descriptionResized() {
updateMaxHeight();
update();
}
bool EditChannelBox::canEditSignatures() const {
return _channel->canEditInformation() && !_channel->isMegagroup();
}
bool EditChannelBox::canEditInvites() const {
return _channel->canEditInformation() && _channel->isMegagroup();
}
void EditChannelBox::updateMaxHeight() {
auto newHeight = st::newGroupInfoPadding.top() + _title->height();
newHeight += st::newGroupDescriptionPadding.top() + _description->height() + st::newGroupDescriptionPadding.bottom();
if (canEditSignatures()) {
newHeight += st::newGroupPublicLinkPadding.top() + _sign->heightNoMargins() + st::newGroupPublicLinkPadding.bottom();
}
if (canEditInvites()) {
newHeight += st::boxTitleHeight + _inviteEverybody->heightNoMargins();
newHeight += st::boxLittleSkip + _inviteOnlyAdmins->heightNoMargins();
}
if (_channel->canEditUsername()) {
newHeight += st::newGroupPublicLinkPadding.top() + _publicLink->height() + st::newGroupPublicLinkPadding.bottom();
}
newHeight += st::boxPadding.bottom() + st::newGroupInfoPadding.bottom();
setDimensions(st::boxWideWidth, newHeight);
}
void EditChannelBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
_title->resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _title->height());
_title->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::newGroupInfoPadding.top() + st::newGroupNamePosition.y());
_description->resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _description->height());
_description->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _title->y() + _title->height() + st::newGroupDescriptionPadding.top());
_sign->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description->y() + _description->height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top());
_inviteEverybody->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description->y() + _description->height() + st::boxTitleHeight);
_inviteOnlyAdmins->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _inviteEverybody->bottomNoMargins() + st::boxLittleSkip);
if (canEditSignatures()) {
_publicLink->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _sign->bottomNoMargins() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top());
} else if (canEditInvites()) {
_publicLink->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _inviteOnlyAdmins->bottomNoMargins() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top());
} else {
_publicLink->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description->y() + _description->height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top());
}
}
void EditChannelBox::paintEvent(QPaintEvent *e) {
BoxContent::paintEvent(e);
if (canEditInvites()) {
Painter p(this);
p.setPen(st::boxTitleFg);
p.setFont(st::autoDownloadTitleFont);
p.drawTextLeft(st::boxTitlePosition.x(), _description->y() + _description->height() + st::boxTitlePosition.y(), width(), lang(lng_edit_group_who_invites));
}
}
void EditChannelBox::save() {
if (_saveTitleRequestId || _saveDescriptionRequestId || _saveSignRequestId || _saveInvitesRequestId) return;
auto title = TextUtilities::PrepareForSending(_title->getLastText());
auto description = TextUtilities::PrepareForSending(_description->getLastText(), TextUtilities::PrepareTextOption::CheckLinks);
if (title.isEmpty()) {
_title->setFocus();
_title->showError();
return;
}
_sentTitle = title;
_sentDescription = description;
if (_sentTitle == _channel->name) {
saveDescription();
} else {
_saveTitleRequestId = MTP::send(MTPchannels_EditTitle(_channel->inputChannel, MTP_string(_sentTitle)), rpcDone(&EditChannelBox::onSaveTitleDone), rpcFail(&EditChannelBox::onSaveFail));
}
}
void EditChannelBox::setupPublicLink() {
Ui::show(
Box<SetupChannelBox>(_channel, true),
LayerOption::KeepOther);
}
void EditChannelBox::saveDescription() {
if (_sentDescription == _channel->about()) {
saveSign();
} else {
_saveDescriptionRequestId = MTP::send(MTPchannels_EditAbout(_channel->inputChannel, MTP_string(_sentDescription)), rpcDone(&EditChannelBox::onSaveDescriptionDone), rpcFail(&EditChannelBox::onSaveFail));
}
}
void EditChannelBox::saveSign() {
if (!canEditSignatures() || _channel->addsSignature() == _sign->checked()) {
saveInvites();
} else {
_saveSignRequestId = MTP::send(MTPchannels_ToggleSignatures(_channel->inputChannel, MTP_bool(_sign->checked())), rpcDone(&EditChannelBox::onSaveSignDone), rpcFail(&EditChannelBox::onSaveFail));
}
}
void EditChannelBox::saveInvites() {
if (!canEditInvites() || _channel->anyoneCanAddMembers() == (_inviteGroup->value() == Invites::Everybody)) {
closeBox();
} else {
_saveInvitesRequestId = MTP::send(MTPchannels_ToggleInvites(_channel->inputChannel, MTP_bool(_inviteGroup->value() == Invites::Everybody)), rpcDone(&EditChannelBox::onSaveInvitesDone), rpcFail(&EditChannelBox::onSaveFail));
}
}
bool EditChannelBox::onSaveFail(const RPCError &error, mtpRequestId req) {
if (MTP::isDefaultHandledError(error)) return false;
QString err(error.type());
if (req == _saveTitleRequestId) {
_saveTitleRequestId = 0;
if (err == qstr("CHAT_NOT_MODIFIED") || err == qstr("CHAT_TITLE_NOT_MODIFIED")) {
_channel->setName(_sentTitle, _channel->username);
saveDescription();
return true;
} else if (err == qstr("NO_CHAT_TITLE")) {
_title->setFocus();
_title->showError();
return true;
} else {
_title->setFocus();
}
} else if (req == _saveDescriptionRequestId) {
_saveDescriptionRequestId = 0;
if (err == qstr("CHAT_ABOUT_NOT_MODIFIED")) {
_channel->setAbout(_sentDescription);
saveSign();
return true;
} else {
_description->setFocus();
}
} else if (req == _saveSignRequestId) {
_saveSignRequestId = 0;
if (err == qstr("CHAT_NOT_MODIFIED")) {
saveInvites();
return true;
}
} else if (req == _saveInvitesRequestId) {
_saveInvitesRequestId = 0;
if (err == qstr("CHAT_NOT_MODIFIED")) {
closeBox();
return true;
}
}
return true;
}
void EditChannelBox::onSaveTitleDone(const MTPUpdates &result) {
_saveTitleRequestId = 0;
Auth().api().applyUpdates(result);
saveDescription();
}
void EditChannelBox::onSaveDescriptionDone(const MTPBool &result) {
_saveDescriptionRequestId = 0;
_channel->setAbout(_sentDescription);
saveSign();
}
void EditChannelBox::onSaveSignDone(const MTPUpdates &result) {
_saveSignRequestId = 0;
Auth().api().applyUpdates(result);
saveInvites();
}
void EditChannelBox::onSaveInvitesDone(const MTPUpdates &result) {
_saveSignRequestId = 0;
Auth().api().applyUpdates(result);
closeBox();
}
RevokePublicLinkBox::Inner::Inner(QWidget *parent, Fn<void()> revokeCallback) : TWidget(parent)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _revokeWidth(st::normalFont->width(lang(lng_channels_too_much_public_revoke)))
@@ -1359,27 +1190,29 @@ RevokePublicLinkBox::Inner::Inner(QWidget *parent, Fn<void()> revokeCallback) :
resize(width(), 5 * _rowHeight);
request(MTPchannels_GetAdminedPublicChannels()).done([this](const MTPmessages_Chats &result) {
if (auto chats = Api::getChatsFromMessagesChats(result)) {
for_const (auto &chat, chats->v) {
if (auto peer = App::feedChat(chat)) {
if (!peer->isChannel() || peer->userName().isEmpty()) {
continue;
}
auto row = ChatRow(peer);
row.peer = peer;
row.name.setText(
st::contactsNameStyle,
peer->name,
Ui::NameTextOptions());
row.status.setText(
st::defaultTextStyle,
Messenger::Instance().createInternalLink(
textcmdLink(1, peer->userName())),
Ui::DialogTextOptions());
_rows.push_back(std::move(row));
request(MTPchannels_GetAdminedPublicChannels(
)).done([=](const MTPmessages_Chats &result) {
const auto &chats = result.match([](const auto &data) {
return data.vchats.v;
});
for (const auto &chat : chats) {
if (const auto peer = Auth().data().processChat(chat)) {
if (!peer->isChannel() || peer->userName().isEmpty()) {
continue;
}
auto row = ChatRow(peer);
row.peer = peer;
row.name.setText(
st::contactsNameStyle,
peer->name,
Ui::NameTextOptions());
row.status.setText(
st::defaultTextStyle,
Core::App().createInternalLink(
textcmdLink(1, peer->userName())),
Ui::DialogTextOptions());
_rows.push_back(std::move(row));
}
}
resize(width(), _rows.size() * _rowHeight);
@@ -1387,23 +1220,30 @@ RevokePublicLinkBox::Inner::Inner(QWidget *parent, Fn<void()> revokeCallback) :
}).send();
}
RevokePublicLinkBox::RevokePublicLinkBox(QWidget*, Fn<void()> revokeCallback)
: _aboutRevoke(this, lang(lng_channels_too_much_public_about), Ui::FlatLabel::InitType::Simple, st::aboutRevokePublicLabel)
RevokePublicLinkBox::RevokePublicLinkBox(
QWidget*,
Fn<void()> revokeCallback)
: _aboutRevoke(
this,
lang(lng_channels_too_much_public_about),
Ui::FlatLabel::InitType::Simple,
st::aboutRevokePublicLabel)
, _revokeCallback(std::move(revokeCallback)) {
}
void RevokePublicLinkBox::prepare() {
_innerTop = st::boxPadding.top() + _aboutRevoke->height() + st::boxPadding.top();
_inner = setInnerWidget(object_ptr<Inner>(this, [this] {
_inner = setInnerWidget(object_ptr<Inner>(this, [=] {
const auto callback = _revokeCallback;
closeBox();
if (_revokeCallback) {
_revokeCallback();
if (callback) {
callback();
}
}), st::boxLayerScroll, _innerTop);
addButton(langFactory(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_cancel), [=] { closeBox(); });
subscribe(Auth().downloaderTaskFinished(), [this] { update(); });
subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
_inner->resizeToWidth(st::boxWideWidth);
setDimensions(st::boxWideWidth, _innerTop + _inner->height());
@@ -1444,16 +1284,20 @@ void RevokePublicLinkBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
setCursor((_selected || _pressed) ? style::cur_pointer : style::cur_default);
if (pressed && pressed == _selected) {
auto text_method = pressed->isMegagroup() ? lng_channels_too_much_public_revoke_confirm_group : lng_channels_too_much_public_revoke_confirm_channel;
auto text = text_method(lt_link, Messenger::Instance().createInternalLink(pressed->userName()), lt_group, pressed->name);
auto text = text_method(lt_link, Core::App().createInternalLink(pressed->userName()), lt_group, pressed->name);
auto confirmText = lang(lng_channels_too_much_public_revoke);
_weakRevokeConfirmBox = Ui::show(Box<ConfirmBox>(text, confirmText, crl::guard(this, [this, pressed]() {
if (_revokeRequestId) return;
_revokeRequestId = request(MTPchannels_UpdateUsername(pressed->asChannel()->inputChannel, MTP_string(""))).done([this](const MTPBool &result) {
_revokeRequestId = request(MTPchannels_UpdateUsername(
pressed->asChannel()->inputChannel,
MTP_string("")
)).done([=](const MTPBool &result) {
const auto callback = _revokeCallback;
if (_weakRevokeConfirmBox) {
_weakRevokeConfirmBox->closeBox();
}
if (_revokeCallback) {
_revokeCallback();
if (callback) {
callback();
}
}).send();
})), LayerOption::KeepOther);

View File

@@ -38,6 +38,10 @@ enum class PeerFloodType {
InviteChannel,
};
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 {
public:
@@ -96,7 +100,6 @@ private:
void descriptionResized();
void updateMaxHeight();
void updateSelected(const QPoint &cursorGlobalPosition);
CreatingGroupType _creating;
bool _fromTypeChoose = false;
@@ -134,7 +137,6 @@ private:
};
void privacyChanged(Privacy value);
void updateSelected(const QPoint &cursorGlobalPosition);
void showAddContactsToChannelBox() const;
void handleChange();
void check();
void save();
@@ -201,62 +203,6 @@ private:
};
class EditChannelBox : public BoxContent, public RPCSender {
public:
EditChannelBox(QWidget*, not_null<ChannelData*> channel);
protected:
void prepare() override;
void setInnerFocus() override;
void keyPressEvent(QKeyEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
private:
void updateMaxHeight();
bool canEditSignatures() const;
bool canEditInvites() const;
void handleChannelNameChange();
void descriptionResized();
void setupPublicLink();
void save();
void onSaveTitleDone(const MTPUpdates &result);
void onSaveDescriptionDone(const MTPBool &result);
void onSaveSignDone(const MTPUpdates &result);
void onSaveInvitesDone(const MTPUpdates &result);
bool onSaveFail(const RPCError &error, mtpRequestId req);
void saveDescription();
void saveSign();
void saveInvites();
not_null<ChannelData*> _channel;
object_ptr<Ui::InputField> _title;
object_ptr<Ui::InputField> _description;
object_ptr<Ui::Checkbox> _sign;
enum class Invites {
Everybody,
OnlyAdmins,
};
std::shared_ptr<Ui::RadioenumGroup<Invites>> _inviteGroup;
object_ptr<Ui::Radioenum<Invites>> _inviteEverybody;
object_ptr<Ui::Radioenum<Invites>> _inviteOnlyAdmins;
object_ptr<Ui::LinkButton> _publicLink;
mtpRequestId _saveTitleRequestId = 0;
mtpRequestId _saveDescriptionRequestId = 0;
mtpRequestId _saveSignRequestId = 0;
mtpRequestId _saveInvitesRequestId = 0;
QString _sentTitle, _sentDescription;
};
class RevokePublicLinkBox : public BoxContent, public RPCSender {
public:
RevokePublicLinkBox(QWidget*, Fn<void()> revokeCallback);

View File

@@ -0,0 +1,165 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/auto_download_box.h"
#include "lang/lang_keys.h"
#include "auth_session.h"
#include "data/data_session.h"
#include "info/profile/info_profile_button.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/wrap.h"
#include "storage/localstorage.h"
#include "settings/settings_common.h"
#include "export/view/export_view_settings.h"
#include "styles/style_boxes.h"
#include "styles/style_settings.h"
namespace {
constexpr auto kMegabyte = 1024 * 1024;
constexpr auto kDefaultLimit = 10 * kMegabyte;
} // namespace
AutoDownloadBox::AutoDownloadBox(
QWidget*,
Data::AutoDownload::Source source)
: _source(source) {
}
void AutoDownloadBox::prepare() {
setupContent();
}
void AutoDownloadBox::setupContent() {
using namespace Settings;
using namespace Data::AutoDownload;
using namespace rpl::mappers;
using Type = Data::AutoDownload::Type;
setTitle(langFactory(lng_media_auto_title));
const auto settings = &Auth().settings().autoDownload();
const auto checked = [=](Source source, Type type) {
return (settings->bytesLimit(source, type) > 0);
};
auto wrap = object_ptr<Ui::VerticalLayout>(this);
const auto content = wrap.data();
setInnerWidget(object_ptr<Ui::OverrideMargins>(
this,
std::move(wrap)));
const auto values = Ui::CreateChild<base::flat_map<Type, int>>(content);
const auto add = [&](Type type, LangKey label) {
const auto value = settings->bytesLimit(_source, type);
AddButton(
content,
label,
st::settingsButton
)->toggleOn(
rpl::single(value > 0)
)->toggledChanges(
) | rpl::start_with_next([=](bool enabled) {
(*values)[type] = enabled ? 1 : 0;
}, content->lifetime());
values->emplace(type, value);
};
add(Type::Photo, lng_media_photo_title);
add(Type::VoiceMessage, lng_media_audio_title);
add(Type::VideoMessage, lng_media_video_messages_title);
add(Type::Video, lng_media_video_title);
add(Type::File, lng_media_file_title);
add(Type::Music, lng_media_music_title);
add(Type::GIF, lng_media_animation_title);
const auto limits = Ui::CreateChild<rpl::event_stream<int>>(content);
using Pair = base::flat_map<Type, int>::value_type;
const auto settingsLimit = ranges::max_element(
*values,
std::less<>(),
[](Pair pair) { return pair.second; })->second;
const auto initialLimit = settingsLimit ? settingsLimit : kDefaultLimit;
const auto limit = Ui::CreateChild<int>(content, initialLimit);
AddButtonWithLabel(
content,
lng_media_size_limit,
limits->events_starting_with_copy(
initialLimit
) | rpl::map([](int value) {
return lng_media_size_up_to(
lt_size,
QString::number(value / kMegabyte) + " MB");
}),
st::autoDownloadLimitButton
)->setAttribute(Qt::WA_TransparentForMouseEvents);
const auto slider = content->add(
object_ptr<Ui::MediaSlider>(content, st::autoDownloadLimitSlider),
st::autoDownloadLimitPadding);
slider->resize(st::autoDownloadLimitSlider.seekSize);
slider->setPseudoDiscrete(
Export::View::kSizeValueCount,
Export::View::SizeLimitByIndex,
*limit,
[=](int value) {
*limit = value;
limits->fire_copy(value);
});
const auto save = [=](
Type type,
std::pair<bool, bool> pair) {
const auto limit = [](bool checked) {
return checked ? kMaxBytesLimit : 0;
};
settings->setBytesLimit(Source::User, type, limit(pair.first));
settings->setBytesLimit(Source::Group, type, limit(pair.second));
settings->setBytesLimit(Source::Channel, type, limit(pair.second));
};
addButton(langFactory(lng_connection_save), [=] {
auto allowMore = ranges::view::all(
*values
) | ranges::view::filter([&](Pair pair) {
const auto [type, enabled] = pair;
const auto value = enabled ? *limit : 0;
const auto old = settings->bytesLimit(_source, type);
return (old < value);
}) | ranges::view::transform([](Pair pair) {
return pair.first;
});
const auto allowMoreTypes = base::flat_set<Type>(
allowMore.begin(),
allowMore.end());
const auto changed = ranges::find_if(*values, [&](Pair pair) {
const auto [type, enabled] = pair;
const auto value = enabled ? *limit : 0;
return settings->bytesLimit(_source, type) != value;
}) != end(*values);
if (changed) {
for (const auto [type, enabled] : *values) {
const auto value = enabled ? *limit : 0;
settings->setBytesLimit(_source, type, value);
}
Local::writeUserSettings();
}
if (allowMoreTypes.contains(Type::Photo)) {
Auth().data().photoLoadSettingsChanged();
}
if (ranges::find_if(allowMoreTypes, _1 != Type::Photo)
!= allowMoreTypes.end()) {
Auth().data().documentLoadSettingsChanged();
}
closeBox();
});
addButton(langFactory(lng_cancel), [=] { closeBox(); });
setDimensionsToContent(st::boxWidth, content);
}

View File

@@ -0,0 +1,30 @@
/*
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 "boxes/abstract_box.h"
namespace Data {
namespace AutoDownload {
enum class Source;
} // namespace AutoDownload
} // namespace Data
class AutoDownloadBox : public BoxContent {
public:
AutoDownloadBox(QWidget*, Data::AutoDownload::Source source);
protected:
void prepare() override;
private:
void setupContent();
Data::AutoDownload::Source _source;
};

View File

@@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/autolock_box.h"
#include "boxes/auto_lock_box.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"

View File

@@ -12,12 +12,158 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h"
#include "window/themes/window_theme.h"
#include "ui/effects/round_checkbox.h"
#include "ui/toast/toast.h"
#include "ui/image/image.h"
#include "history/history.h"
#include "history/history_message.h"
#include "history/view/history_view_message.h"
#include "auth_session.h"
#include "apiwrap.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "data/data_document.h"
#include "core/application.h"
#include "boxes/confirm_box.h"
#include "styles/style_overview.h"
#include "styles/style_history.h"
#include "styles/style_boxes.h"
class BackgroundBox::Inner : public TWidget, public RPCSender, private base::Subscriber {
namespace {
constexpr auto kBackgroundsInRow = 3;
constexpr auto kMaxWallPaperSlugLength = 255;
[[nodiscard]] bool IsValidWallPaperSlug(const QString &slug) {
if (slug.isEmpty() || slug.size() > kMaxWallPaperSlugLength) {
return false;
}
return ranges::find_if(slug, [](QChar ch) {
return (ch != '.')
&& (ch != '_')
&& (ch != '-')
&& (ch < '0' || ch > '9')
&& (ch < 'a' || ch > 'z')
&& (ch < 'A' || ch > 'Z');
}) == slug.end();
}
AdminLog::OwnedItem GenerateTextItem(
not_null<HistoryView::ElementDelegate*> delegate,
not_null<History*> history,
const QString &text,
bool out) {
Expects(history->peer->isUser());
using Flag = MTPDmessage::Flag;
static auto id = ServerMaxMsgId + (ServerMaxMsgId / 3);
const auto flags = Flag::f_entities
| Flag::f_from_id
| (out ? Flag::f_out : Flag(0));
const auto replyTo = 0;
const auto viaBotId = 0;
const auto item = new HistoryMessage(
history,
++id,
flags,
replyTo,
viaBotId,
unixtime(),
out ? history->session().userId() : peerToUser(history->peer->id),
QString(),
TextWithEntities{ TextUtilities::Clean(text) });
return AdminLog::OwnedItem(delegate, item);
}
QImage PrepareScaledNonPattern(
const QImage &image,
Images::Option blur) {
const auto size = st::boxWideWidth;
const auto width = std::max(image.width(), 1);
const auto height = std::max(image.height(), 1);
const auto takeWidth = (width > height)
? (width * size / height)
: size;
const auto takeHeight = (width > height)
? size
: (height * size / width);
return Images::prepare(
image,
takeWidth * cIntRetinaFactor(),
takeHeight * cIntRetinaFactor(),
Images::Option::Smooth
| Images::Option::TransparentBackground
| blur,
size,
size);
}
QImage ColorizePattern(QImage image, QColor color) {
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
image = std::move(image).convertToFormat(
QImage::Format_ARGB32_Premultiplied);
}
// Similar to style::colorizeImage.
// But style::colorizeImage takes pattern with all pixels having the
// same components value, from (0, 0, 0, 0) to (255, 255, 255, 255).
//
// While in patterns we have different value ranges, usually they are
// from (0, 0, 0, 0) to (0, 0, 0, 255), so we should use only 'alpha'.
const auto width = image.width();
const auto height = image.height();
const auto pattern = anim::shifted(color);
const auto resultBytesPerPixel = (image.depth() >> 3);
constexpr auto resultIntsPerPixel = 1;
const auto resultIntsPerLine = (image.bytesPerLine() >> 2);
const auto resultIntsAdded = resultIntsPerLine - width * resultIntsPerPixel;
auto resultInts = reinterpret_cast<uint32*>(image.bits());
Assert(resultIntsAdded >= 0);
Assert(image.depth() == static_cast<int>((resultIntsPerPixel * sizeof(uint32)) << 3));
Assert(image.bytesPerLine() == (resultIntsPerLine << 2));
const auto maskBytesPerPixel = (image.depth() >> 3);
const auto maskBytesPerLine = image.bytesPerLine();
const auto maskBytesAdded = maskBytesPerLine - width * maskBytesPerPixel;
// We want to read the last byte of four available.
// This is the difference with style::colorizeImage.
auto maskBytes = image.constBits() + (maskBytesPerPixel - 1);
Assert(maskBytesAdded >= 0);
Assert(image.depth() == (maskBytesPerPixel << 3));
for (auto y = 0; y != height; ++y) {
for (auto x = 0; x != width; ++x) {
auto maskOpacity = static_cast<anim::ShiftedMultiplier>(*maskBytes) + 1;
*resultInts = anim::unshifted(pattern * maskOpacity);
maskBytes += maskBytesPerPixel;
resultInts += resultIntsPerPixel;
}
maskBytes += maskBytesAdded;
resultInts += resultIntsAdded;
}
return image;
}
QImage PrepareScaledFromFull(
const QImage &image,
std::optional<QColor> patternBackground,
Images::Option blur = Images::Option(0)) {
auto result = PrepareScaledNonPattern(image, blur);
if (patternBackground) {
result = ColorizePattern(
std::move(result),
Data::PatternColor(*patternBackground));
}
return std::move(result).convertToFormat(
QImage::Format_ARGB32_Premultiplied);
}
} // namespace
class BackgroundBox::Inner
: public Ui::RpWidget
, private MTP::Sender
, private base::Subscriber {
public:
Inner(QWidget *parent);
@@ -34,15 +180,18 @@ protected:
void mouseReleaseEvent(QMouseEvent *e) override;
private:
void gotWallpapers(const MTPVector<MTPWallPaper> &result);
void updateWallpapers();
void paintPaper(
Painter &p,
const Data::WallPaper &paper,
int column,
int row) const;
Fn<void(int index)> _backgroundChosenCallback;
int _bgCount = 0;
int _rows = 0;
int _over = -1;
int _overDown = -1;
std::unique_ptr<Ui::RoundCheckbox> _check; // this is not a widget
};
@@ -53,37 +202,43 @@ BackgroundBox::BackgroundBox(QWidget*) {
void BackgroundBox::prepare() {
setTitle(langFactory(lng_backgrounds_header));
addButton(langFactory(lng_close), [this] { closeBox(); });
addButton(langFactory(lng_close), [=] { closeBox(); });
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
_inner = setInnerWidget(object_ptr<Inner>(this), st::backgroundScroll);
_inner->setBackgroundChosenCallback([this](int index) { backgroundChosen(index); });
_inner->setBackgroundChosenCallback([=](int index) {
backgroundChosen(index);
});
}
void BackgroundBox::backgroundChosen(int index) {
if (index >= 0 && index < App::cServerBackgrounds().size()) {
auto &paper = App::cServerBackgrounds()[index];
if (App::main()) App::main()->setChatBackground(paper);
using Update = Window::Theme::BackgroundUpdate;
Window::Theme::Background()->notify(Update(Update::Type::Start, !paper.id));
const auto &papers = Auth().data().wallpapers();
if (index >= 0 && index < papers.size()) {
Ui::show(
Box<BackgroundPreviewBox>(papers[index]),
LayerOption::KeepOther);
}
closeBox();
}
BackgroundBox::Inner::Inner(QWidget *parent) : TWidget(parent)
, _check(std::make_unique<Ui::RoundCheckbox>(st::overviewCheck, [this] { update(); })) {
BackgroundBox::Inner::Inner(QWidget *parent) : RpWidget(parent)
, _check(std::make_unique<Ui::RoundCheckbox>(st::overviewCheck, [=] { update(); })) {
_check->setChecked(true, Ui::RoundCheckbox::SetStyle::Fast);
if (App::cServerBackgrounds().isEmpty()) {
resize(BackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
MTP::send(MTPaccount_GetWallPapers(), rpcDone(&Inner::gotWallpapers));
if (Auth().data().wallpapers().empty()) {
resize(kBackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
} else {
updateWallpapers();
}
request(MTPaccount_GetWallPapers(
MTP_int(Auth().data().wallpapersHash())
)).done([=](const MTPaccount_WallPapers &result) {
if (Auth().data().updateWallpapers(result)) {
updateWallpapers();
}
}).send();
subscribe(Auth().downloaderTaskFinished(), [this] { update(); });
subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &update) {
subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
subscribe(Window::Theme::Background(), [=](const Window::Theme::BackgroundUpdate &update) {
if (update.paletteChanged()) {
_check->invalidateCache();
}
@@ -91,74 +246,17 @@ BackgroundBox::Inner::Inner(QWidget *parent) : TWidget(parent)
setMouseTracking(true);
}
void BackgroundBox::Inner::gotWallpapers(const MTPVector<MTPWallPaper> &result) {
App::WallPapers wallpapers;
auto oldBackground = Images::Create(qsl(":/gui/art/bg_initial.jpg"), "JPG");
wallpapers.push_back(App::WallPaper(Window::Theme::kInitialBackground, oldBackground, oldBackground));
auto &v = result.v;
for_const (auto &w, v) {
switch (w.type()) {
case mtpc_wallPaper: {
auto &d = w.c_wallPaper();
auto &sizes = d.vsizes.v;
const MTPPhotoSize *thumb = 0, *full = 0;
int32 thumbLevel = -1, fullLevel = -1;
for (QVector<MTPPhotoSize>::const_iterator j = sizes.cbegin(), e = sizes.cend(); j != e; ++j) {
char size = 0;
int32 w = 0, h = 0;
switch (j->type()) {
case mtpc_photoSize: {
auto &s = j->c_photoSize().vtype.v;
if (s.size()) size = s[0];
w = j->c_photoSize().vw.v;
h = j->c_photoSize().vh.v;
} break;
case mtpc_photoCachedSize: {
auto &s = j->c_photoCachedSize().vtype.v;
if (s.size()) size = s[0];
w = j->c_photoCachedSize().vw.v;
h = j->c_photoCachedSize().vh.v;
} break;
}
if (!size || !w || !h) continue;
int32 newThumbLevel = qAbs((st::backgroundSize.width() * cIntRetinaFactor()) - w), newFullLevel = qAbs(2560 - w);
if (thumbLevel < 0 || newThumbLevel < thumbLevel) {
thumbLevel = newThumbLevel;
thumb = &(*j);
}
if (fullLevel < 0 || newFullLevel < fullLevel) {
fullLevel = newFullLevel;
full = &(*j);
}
}
if (thumb && full && full->type() != mtpc_photoSizeEmpty) {
wallpapers.push_back(App::WallPaper(d.vid.v ? d.vid.v : INT_MAX, App::image(*thumb), App::image(*full)));
}
} break;
case mtpc_wallPaperSolid: {
auto &d = w.c_wallPaperSolid();
} break;
}
}
App::cSetServerBackgrounds(wallpapers);
updateWallpapers();
}
void BackgroundBox::Inner::updateWallpapers() {
_bgCount = App::cServerBackgrounds().size();
_rows = _bgCount / BackgroundsInRow;
if (_bgCount % BackgroundsInRow) ++_rows;
const auto &papers = Auth().data().wallpapers();
const auto count = papers.size();
const auto rows = (count / kBackgroundsInRow)
+ (count % kBackgroundsInRow ? 1 : 0);
resize(BackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, _rows * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
for (int i = 0; i < BackgroundsInRow * 3; ++i) {
if (i >= _bgCount) break;
resize(kBackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, rows * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
App::cServerBackgrounds()[i].thumb->load(Data::FileOrigin());
const auto preload = kBackgroundsInRow * 3;
for (const auto &paper : papers | ranges::view::take(preload)) {
paper.loadThumbnail();
}
}
@@ -166,52 +264,76 @@ void BackgroundBox::Inner::paintEvent(QPaintEvent *e) {
QRect r(e->rect());
Painter p(this);
if (_rows) {
for (int i = 0; i < _rows; ++i) {
if ((st::backgroundSize.height() + st::backgroundPadding) * (i + 1) <= r.top()) continue;
for (int j = 0; j < BackgroundsInRow; ++j) {
int index = i * BackgroundsInRow + j;
if (index >= _bgCount) break;
const auto &paper = App::cServerBackgrounds()[index];
paper.thumb->load(Data::FileOrigin());
int x = st::backgroundPadding + j * (st::backgroundSize.width() + st::backgroundPadding);
int y = st::backgroundPadding + i * (st::backgroundSize.height() + st::backgroundPadding);
const auto &pix = paper.thumb->pix(
Data::FileOrigin(),
st::backgroundSize.width(),
st::backgroundSize.height());
p.drawPixmap(x, y, pix);
if (paper.id == Window::Theme::Background()->id()) {
auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
_check->paint(p, getms(), checkLeft, checkTop, width());
}
}
}
} else {
const auto &papers = Auth().data().wallpapers();
if (papers.empty()) {
p.setFont(st::noContactsFont);
p.setPen(st::noContactsColor);
p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center);
return;
}
auto row = 0;
auto column = 0;
for (const auto &paper : papers) {
const auto increment = gsl::finally([&] {
++column;
if (column == kBackgroundsInRow) {
column = 0;
++row;
}
});
if ((st::backgroundSize.height() + st::backgroundPadding) * (row + 1) <= r.top()) {
continue;
} else if ((st::backgroundSize.height() + st::backgroundPadding) * row >= r.top() + r.height()) {
break;
}
paintPaper(p, paper, column, row);
}
}
void BackgroundBox::Inner::paintPaper(
Painter &p,
const Data::WallPaper &paper,
int column,
int row) const {
Expects(paper.thumbnail() != nullptr);
const auto x = st::backgroundPadding + column * (st::backgroundSize.width() + st::backgroundPadding);
const auto y = st::backgroundPadding + row * (st::backgroundSize.height() + st::backgroundPadding);
const auto &pixmap = paper.thumbnail()->pix(
paper.fileOrigin(),
st::backgroundSize.width(),
st::backgroundSize.height());
p.drawPixmap(x, y, pixmap);
if (paper.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, getms(), checkLeft, checkTop, width());
}
}
void BackgroundBox::Inner::mouseMoveEvent(QMouseEvent *e) {
int x = e->pos().x(), y = e->pos().y();
int row = int((y - st::backgroundPadding) / (st::backgroundSize.height() + st::backgroundPadding));
if (y - row * (st::backgroundSize.height() + st::backgroundPadding) > st::backgroundPadding + st::backgroundSize.height()) row = _rows + 1;
int col = int((x - st::backgroundPadding) / (st::backgroundSize.width() + st::backgroundPadding));
if (x - col * (st::backgroundSize.width() + st::backgroundPadding) > st::backgroundPadding + st::backgroundSize.width()) row = _rows + 1;
int newOver = row * BackgroundsInRow + col;
if (newOver >= _bgCount) newOver = -1;
if (newOver != _over) {
const auto newOver = [&] {
const auto x = e->pos().x();
const auto y = e->pos().y();
const auto width = st::backgroundSize.width();
const auto height = st::backgroundSize.height();
const auto skip = st::backgroundPadding;
const auto row = int((y - skip) / (height + skip));
const auto column = int((x - skip) / (width + skip));
if (y - row * (height + skip) > skip + height) {
return -1;
} else if (x - column * (width + skip) > skip + width) {
return -1;
}
const auto result = row * kBackgroundsInRow + column;
return (result < Auth().data().wallpapers().size()) ? result : -1;
}();
if (_over != newOver) {
_over = newOver;
setCursor((_over >= 0 || _overDown >= 0) ? style::cur_pointer : style::cur_default);
setCursor((_over >= 0 || _overDown >= 0)
? style::cur_pointer
: style::cur_default);
}
}
@@ -230,3 +352,306 @@ void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
}
BackgroundBox::Inner::~Inner() = default;
BackgroundPreviewBox::BackgroundPreviewBox(
QWidget*,
const Data::WallPaper &paper)
: _text1(GenerateTextItem(
this,
Auth().data().history(peerFromUser(ServiceUserId)),
lang(lng_background_text1),
false))
, _text2(GenerateTextItem(
this,
Auth().data().history(peerFromUser(ServiceUserId)),
lang(lng_background_text2),
true))
, _paper(paper)
, _radial(animation(this, &BackgroundPreviewBox::step_radial)) {
Expects(_paper.thumbnail() != nullptr);
subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
}
void BackgroundPreviewBox::prepare() {
setTitle(langFactory(lng_background_header));
addButton(langFactory(lng_background_apply), [=] { apply(); });
addButton(langFactory(lng_cancel), [=] { closeBox(); });
if (_paper.hasShareUrl()) {
addLeftButton(langFactory(lng_background_share), [=] { share(); });
}
updateServiceBg(_paper.backgroundColor());
_paper.loadThumbnail();
_paper.loadDocument();
if (_paper.document() && _paper.document()->loading()) {
_radial.start(_paper.document()->progress());
}
setScaledFromThumb();
checkLoadedDocument();
_text1->setDisplayDate(true);
_text1->initDimensions();
_text1->resizeGetHeight(st::boxWideWidth);
_text2->initDimensions();
_text2->resizeGetHeight(st::boxWideWidth);
setDimensions(st::boxWideWidth, st::boxWideWidth);
}
void BackgroundPreviewBox::apply() {
App::main()->setChatBackground(_paper, std::move(_full));
closeBox();
}
void BackgroundPreviewBox::share() {
QApplication::clipboard()->setText(_paper.shareUrl());
Ui::Toast::Show(lang(lng_background_link_copied));
}
void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
Painter p(this);
const auto ms = getms();
const auto color = _paper.backgroundColor();
if (color) {
p.fillRect(e->rect(), *color);
}
if (!color || _paper.isPattern()) {
if (_scaled.isNull() && !setScaledFromThumb()) {
p.fillRect(e->rect(), st::boxBg);
return;
}
paintImage(p);
paintRadial(p, ms);
}
paintTexts(p, ms);
}
void BackgroundPreviewBox::paintImage(Painter &p) {
Expects(!_scaled.isNull());
p.setOpacity(_paper.isPattern()
? std::clamp(_paper.patternIntensity() / 100., 0., 1.)
: 1.);
const auto guard = gsl::finally([&] { p.setOpacity(1.); });
const auto factor = cIntRetinaFactor();
const auto size = st::boxWideWidth;
const auto from = QRect(
0,
(size - height()) / 2 * factor,
size * factor,
height() * factor);
p.drawPixmap(rect(), _scaled, from);
}
void BackgroundPreviewBox::paintRadial(Painter &p, TimeMs ms) {
bool radial = false;
float64 radialOpacity = 0;
if (_radial.animating()) {
_radial.step(ms);
radial = _radial.animating();
radialOpacity = _radial.opacity();
}
if (!radial) {
return;
}
auto inner = radialRect();
p.setPen(Qt::NoPen);
p.setOpacity(radialOpacity);
p.setBrush(st::radialBg);
{
PainterHighQualityEnabler hq(p);
p.drawEllipse(inner);
}
p.setOpacity(1);
QRect arc(inner.marginsRemoved(QMargins(st::radialLine, st::radialLine, st::radialLine, st::radialLine)));
_radial.draw(p, arc, st::radialLine, st::radialFg);
}
QRect BackgroundPreviewBox::radialRect() const {
const auto available = height()
- st::historyPaddingBottom
- _text1->height()
- _text2->height()
- st::historyPaddingBottom;
return QRect(
QPoint(
(width() - st::radialSize.width()) / 2,
(available - st::radialSize.height()) / 2),
st::radialSize);
}
void BackgroundPreviewBox::paintTexts(Painter &p, TimeMs ms) {
const auto height1 = _text1->height();
const auto height2 = _text2->height();
const auto top = height()
- height1
- height2
- st::historyPaddingBottom;
p.translate(0, top);
paintDate(p);
_text1->draw(p, rect(), TextSelection(), ms);
p.translate(0, height1);
_text2->draw(p, rect(), TextSelection(), ms);
p.translate(0, height2);
}
void BackgroundPreviewBox::paintDate(Painter &p) {
const auto date = _text1->Get<HistoryView::DateBadge>();
if (!date || !_serviceBg) {
return;
}
const auto text = date->text;
const auto bubbleHeight = st::msgServicePadding.top() + st::msgServiceFont->height + st::msgServicePadding.bottom();
const auto bubbleTop = st::msgServiceMargin.top();
const auto textWidth = st::msgServiceFont->width(text);
const auto bubbleWidth = st::msgServicePadding.left() + textWidth + st::msgServicePadding.right();
const auto bubbleLeft = (width() - bubbleWidth) / 2;
const auto radius = bubbleHeight / 2;
p.setPen(Qt::NoPen);
p.setBrush(*_serviceBg);
p.drawRoundedRect(bubbleLeft, bubbleTop, bubbleWidth, bubbleHeight, radius, radius);
p.setPen(st::msgServiceFg);
p.setFont(st::msgServiceFont);
p.drawText(bubbleLeft + st::msgServicePadding.left(), bubbleTop + st::msgServicePadding.top() + st::msgServiceFont->ascent, text);
}
void BackgroundPreviewBox::step_radial(TimeMs ms, bool timer) {
Expects(_paper.document() != nullptr);
const auto document = _paper.document();
const auto wasAnimating = _radial.animating();
const auto updated = _radial.update(
document->progress(),
!document->loading(),
ms);
if (timer
&& (wasAnimating || _radial.animating())
&& (!anim::Disabled() || updated)) {
update(radialRect());
}
checkLoadedDocument();
}
bool BackgroundPreviewBox::setScaledFromThumb() {
Expects(_paper.thumbnail() != nullptr);
const auto thumbnail = _paper.thumbnail();
if (!thumbnail->loaded()) {
return false;
}
setScaledFromImage(PrepareScaledFromFull(
thumbnail->original(),
patternBackgroundColor(),
_paper.document() ? Images::Option::Blurred : Images::Option(0)));
return true;
}
void BackgroundPreviewBox::setScaledFromImage(QImage &&image) {
updateServiceBg(Window::Theme::CountAverageColor(image));
_scaled = App::pixmapFromImageInPlace(std::move(image));
}
void BackgroundPreviewBox::updateServiceBg(std::optional<QColor> background) {
if (background) {
_serviceBg = Window::Theme::AdjustedColor(
st::msgServiceBg->c,
*background);
}
}
std::optional<QColor> BackgroundPreviewBox::patternBackgroundColor() const {
return _paper.isPattern() ? _paper.backgroundColor() : std::nullopt;
}
void BackgroundPreviewBox::checkLoadedDocument() {
const auto document = _paper.document();
if (!document
|| !document->loaded(DocumentData::FilePathResolveChecked)
|| _generating) {
return;
}
_generating = Data::ReadImageAsync(document, [=](
QImage &&image) mutable {
auto [left, right] = base::make_binary_guard();
_generating = std::move(left);
crl::async([
this,
image = std::move(image),
patternBackground = patternBackgroundColor(),
guard = std::move(right)
]() mutable {
auto scaled = PrepareScaledFromFull(image, patternBackground);
crl::on_main([
this,
image = std::move(image),
scaled = std::move(scaled),
guard = std::move(guard)
]() mutable {
if (!guard) {
return;
}
setScaledFromImage(std::move(scaled));
_full = std::move(image);
update();
});
});
});
}
bool BackgroundPreviewBox::Start(
const QString &slug,
const QMap<QString, QString> &params) {
if (const auto paper = Data::WallPaper::FromColorSlug(slug)) {
Ui::show(Box<BackgroundPreviewBox>(paper->withUrlParams(params)));
return true;
}
if (!IsValidWallPaperSlug(slug)) {
Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
return false;
}
Auth().api().requestWallPaper(slug, [=](const Data::WallPaper &result) {
Ui::show(Box<BackgroundPreviewBox>(result.withUrlParams(params)));
}, [](const RPCError &error) {
Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
});
return true;
}
HistoryView::Context BackgroundPreviewBox::elementContext() {
return HistoryView::Context::ContactPreview;
}
std::unique_ptr<HistoryView::Element> BackgroundPreviewBox::elementCreate(
not_null<HistoryMessage*> message) {
return std::make_unique<HistoryView::Message>(this, message);
}
std::unique_ptr<HistoryView::Element> BackgroundPreviewBox::elementCreate(
not_null<HistoryService*> message) {
Unexpected("Service message in BackgroundPreviewBox.");
}
bool BackgroundPreviewBox::elementUnderCursor(
not_null<const Element*> view) {
return false;
}
void BackgroundPreviewBox::elementAnimationAutoplayAsync(
not_null<const Element*> element) {
}
TimeMs BackgroundPreviewBox::elementHighlightTime(
not_null<const Element*> element) {
return TimeMs();
}
bool BackgroundPreviewBox::elementInSelectionMode() {
return false;
}

View File

@@ -7,7 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/binary_guard.h"
#include "boxes/abstract_box.h"
#include "window/themes/window_theme.h"
#include "history/admin_log/history_admin_log_item.h"
#include "history/view/history_view_element.h"
#include "ui/effects/radial_animation.h"
namespace Ui {
class RoundCheckbox;
@@ -27,3 +32,58 @@ private:
QPointer<Inner> _inner;
};
class BackgroundPreviewBox
: public BoxContent
, public HistoryView::ElementDelegate {
public:
BackgroundPreviewBox(QWidget*, const Data::WallPaper &paper);
static bool Start(
const QString &slug,
const QMap<QString, QString> &params);
using Element = HistoryView::Element;
HistoryView::Context elementContext() override;
std::unique_ptr<Element> elementCreate(
not_null<HistoryMessage*> message) override;
std::unique_ptr<Element> elementCreate(
not_null<HistoryService*> message) override;
bool elementUnderCursor(not_null<const Element*> view) override;
void elementAnimationAutoplayAsync(
not_null<const Element*> element) override;
TimeMs elementHighlightTime(
not_null<const Element*> element) override;
bool elementInSelectionMode() override;
protected:
void prepare() override;
void paintEvent(QPaintEvent *e) override;
private:
void apply();
void share();
void step_radial(TimeMs ms, bool timer);
QRect radialRect() const;
void checkLoadedDocument();
bool setScaledFromThumb();
void setScaledFromImage(QImage &&image);
void updateServiceBg(std::optional<QColor> background);
std::optional<QColor> patternBackgroundColor() const;
void paintImage(Painter &p);
void paintRadial(Painter &p, TimeMs ms);
void paintTexts(Painter &p, TimeMs ms);
void paintDate(Painter &p);
AdminLog::OwnedItem _text1;
AdminLog::OwnedItem _text2;
Data::WallPaper _paper;
QImage _full;
QPixmap _scaled;
Ui::RadialAnimation _radial;
base::binary_guard _generating;
std::optional<QColor> _serviceBg;
};

View File

@@ -69,7 +69,7 @@ boxTitle: FlatLabel(defaultFlatLabel) {
linkFontOver: font(17px semibold underline);
}
}
boxTitlePosition: point(23px, 20px);
boxTitlePosition: point(23px, 16px);
boxTitleHeight: 56px;
boxLayerTitlePosition: point(23px, 16px);
boxLayerTitleHeight: 56px;
@@ -533,6 +533,10 @@ aboutLabel: FlatLabel(defaultFlatLabel) {
autoDownloadTopDelta: 10px;
autoDownloadTitlePosition: point(23px, 18px);
autoDownloadTitleFont: font(15px semibold);
autoDownloadLimitSlider: MediaSlider(defaultContinuousSlider) {
seekSize: size(15px, 15px);
}
autoDownloadLimitPadding: margins(22px, 8px, 22px, 8px);
confirmCaptionArea: InputField(defaultInputField) {
textMargins: margins(1px, 26px, 31px, 4px);
@@ -688,6 +692,7 @@ rightsCheckbox: Checkbox(defaultBoxCheckbox) {
rightsToggle: Toggle(defaultToggle) {
toggledFg: windowBgActive;
untoggledFg: attentionButtonFg;
lockIcon: icon {{ "info_rights_lock", windowBgActive }};
xsize: 8px;
vsize: 5px;
vshift: 1px;
@@ -695,7 +700,8 @@ rightsToggle: Toggle(defaultToggle) {
duration: 120;
}
rightsDividerHeight: 10px;
rightsHeaderMargin: margins(23px, 20px, 23px, 8px);
rightsDividerMargin: margins(0px, 0px, 0px, 20px);
rightsHeaderMargin: margins(23px, 0px, 23px, 8px);
rightsToggleMargin: margins(23px, 8px, 23px, 8px);
rightsAboutMargin: margins(23px, 8px, 23px, 8px);
rightsPhotoButton: UserpicButton(defaultUserpicButton) {
@@ -718,7 +724,7 @@ rightsHeaderLabel: FlatLabel(boxLabel) {
}
textFg: windowActiveTextFg;
}
rightsUntilMargin: margins(0px, 8px, 0px, 0px);
rightsUntilMargin: margins(0px, 8px, 0px, 20px);
mutePhotoButton: UserpicButton(defaultUserpicButton) {
size: size(40px, 40px);
@@ -834,3 +840,74 @@ themesScroll: ScrollArea(defaultScrollArea) {
bottomsh: 0px;
topsh: 0px;
}
createPollField: InputField(defaultInputField) {
font: boxTextFont;
textMargins: margins(0px, 0px, 0px, 0px);
textAlign: align(left);
heightMin: 36px;
heightMax: 86px;
placeholderFg: placeholderFg;
placeholderFgActive: placeholderFgActive;
placeholderFgError: placeholderFgActive;
placeholderMargins: margins(2px, 0px, 2px, 0px);
placeholderAlign: align(topleft);
placeholderScale: 0.;
placeholderFont: boxTextFont;
placeholderShift: -50px;
border: 0px;
borderActive: 0px;
duration: 100;
}
createPollFieldPadding: margins(22px, 5px, 22px, 5px);
createPollOptionField: InputField(createPollField) {
textMargins: margins(22px, 8px, 40px, 8px);
placeholderMargins: margins(2px, 0px, 2px, 0px);
heightMax: 64px;
}
createPollLimitLabel: FlatLabel(defaultFlatLabel) {
minWidth: 274px;
align: align(topleft);
}
createPollLimitPadding: margins(22px, 10px, 22px, 5px);
createPollOptionRemove: CrossButton {
width: 22px;
height: 22px;
cross: CrossAnimation {
size: 22px;
skip: 6px;
stroke: 1px;
minScale: 0.3;
}
crossFg: boxTitleCloseFg;
crossFgOver: boxTitleCloseFgOver;
crossPosition: point(0px, 0px);
duration: 150;
loadingPeriod: 1000;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
}
createPollOptionRemovePosition: point(10px, 7px);
createPollWarning: FlatLabel(defaultFlatLabel) {
textFg: windowSubTextFg;
palette: TextPalette(defaultTextPalette) {
linkFg: boxTextFgError;
}
}
createPollWarningPosition: point(16px, 6px);
callSettingsButton: IconButton {
width: 50px;
height: boxLayerTitleHeight;
icon: icon {{ "menu_settings", boxTitleCloseFg }};
iconOver: icon {{ "menu_settings", boxTitleCloseFgOver }};
iconPosition: point(8px, -1px);
rippleAreaSize: 44px;
rippleAreaPosition: point(0px, 6px);
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
}

View File

@@ -495,16 +495,8 @@ void CalendarBox::setMaxDate(QDate date) {
}
void CalendarBox::prepare() {
_previous->setClickedCallback([this] {
if (isPreviousEnabled()) {
_context->skipMonth(-1);
}
});
_next->setClickedCallback([this] {
if (isNextEnabled()) {
_context->skipMonth(1);
}
});
_previous->setClickedCallback([this] { goPreviousMonth(); });
_next->setClickedCallback([this] { goNextMonth(); });
// _inner = setInnerWidget(object_ptr<Inner>(this, _context.get()), st::calendarScroll, st::calendarTitleHeight);
_inner->setDateChosenCallback(std::move(_callback));
@@ -528,6 +520,18 @@ bool CalendarBox::isNextEnabled() const {
return (_context->maxDayIndex() >= _context->daysCount());
}
void CalendarBox::goPreviousMonth() {
if (isPreviousEnabled()) {
_context->skipMonth(-1);
}
}
void CalendarBox::goNextMonth() {
if (isNextEnabled()) {
_context->skipMonth(1);
}
}
void CalendarBox::monthChanged(QDate month) {
setDimensions(_st.width, st::calendarTitleHeight + _inner->countHeight());
auto previousEnabled = isPreviousEnabled();
@@ -548,4 +552,14 @@ void CalendarBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
}
void CalendarBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
e->ignore();
} else if (e->key() == Qt::Key_Left) {
goPreviousMonth();
} else if (e->key() == Qt::Key_Right) {
goNextMonth();
}
}
CalendarBox::~CalendarBox() = default;

View File

@@ -42,6 +42,7 @@ public:
protected:
void prepare() override;
void keyPressEvent(QKeyEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
@@ -50,6 +51,9 @@ private:
bool isPreviousEnabled() const;
bool isNextEnabled() const;
void goPreviousMonth();
void goNextMonth();
const style::CalendarSizes &_st;
class Context;

View File

@@ -8,13 +8,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/change_phone_box.h"
#include "lang/lang_keys.h"
#include "styles/style_boxes.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/input_fields.h"
#include "ui/wrap/fade_wrap.h"
#include "boxes/confirm_phone_box.h"
#include "ui/toast/toast.h"
#include "boxes/confirm_box.h"
#include "auth_session.h"
#include "data/data_session.h"
#include "styles/style_boxes.h"
namespace {
@@ -144,11 +146,19 @@ void ChangePhoneBox::EnterPhone::submit() {
hideError();
auto phoneNumber = _phone->getLastText().trimmed();
_requestId = MTP::send(MTPaccount_SendChangePhoneCode(MTP_flags(0), MTP_string(phoneNumber), MTP_bool(false)), rpcDone(crl::guard(this, [this, phoneNumber](const MTPauth_SentCode &result) {
return sendPhoneDone(phoneNumber, result);
})), rpcFail(crl::guard(this, [this, phoneNumber](const RPCError &error) {
return sendPhoneFail(phoneNumber, error);
})));
_requestId = MTP::send(
MTPaccount_SendChangePhoneCode(
MTP_string(phoneNumber),
MTP_codeSettings(
MTP_flags(0),
MTPstring())),
rpcDone(crl::guard(this, [=](
const MTPauth_SentCode &result) {
return sendPhoneDone(phoneNumber, result);
})), rpcFail(crl::guard(this, [=](
const RPCError &error) {
return sendPhoneFail(phoneNumber, error);
})));
}
void ChangePhoneBox::EnterPhone::sendPhoneDone(const QString &phoneNumber, const MTPauth_SentCode &result) {
@@ -260,7 +270,7 @@ void ChangePhoneBox::EnterCode::submit() {
MTP_string(_hash),
MTP_string(code)
), rpcDone([weak = make_weak(this)](const MTPUser &result) {
App::feedUser(result);
Auth().data().processUser(result);
if (weak) {
Ui::hideLayer();
}

View File

@@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h"
#include "mainwindow.h"
#include "apiwrap.h"
#include "application.h"
#include "history/history.h"
#include "history/history_item.h"
#include "ui/widgets/checkbox.h"
@@ -25,40 +24,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/click_handler_types.h"
#include "storage/localstorage.h"
#include "data/data_session.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "auth_session.h"
#include "observer_peer.h"
namespace {
void ConvertToSupergroupDone(const MTPUpdates &updates) {
Auth().api().applyUpdates(updates);
auto handleChats = [](const MTPVector<MTPChat> &chats) {
for (const auto &chat : chats.v) {
if (chat.type() == mtpc_channel) {
const auto channel = App::channel(chat.c_channel().vid.v);
Ui::showPeerHistory(channel, ShowAtUnreadMsgId);
Auth().api().requestParticipantsCountDelayed(channel);
}
}
};
switch (updates.type()) {
case mtpc_updates:
handleChats(updates.c_updates().vchats);
break;
case mtpc_updatesCombined:
handleChats(updates.c_updatesCombined().vchats);
break;
default:
LOG(("API Error: unexpected update cons %1 "
"(ConvertToSupergroupBox::convertDone)").arg(updates.type()));
break;
}
}
} // namespace
TextParseOptions _confirmBoxTextOptions = {
TextParseLinks | TextParseMultiline | TextParseRichText, // flags
0, // maxw
@@ -66,7 +37,11 @@ TextParseOptions _confirmBoxTextOptions = {
Qt::LayoutDirectionAuto, // dir
};
ConfirmBox::ConfirmBox(QWidget*, const QString &text, FnMut<void()> confirmedCallback, FnMut<void()> cancelledCallback)
ConfirmBox::ConfirmBox(
QWidget*,
const QString &text,
FnMut<void()> confirmedCallback,
FnMut<void()> cancelledCallback)
: _confirmText(lang(lng_box_ok))
, _cancelText(lang(lng_cancel))
, _confirmStyle(st::defaultBoxButton)
@@ -76,7 +51,12 @@ ConfirmBox::ConfirmBox(QWidget*, const QString &text, FnMut<void()> confirmedCal
init(text);
}
ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText, FnMut<void()> confirmedCallback, FnMut<void()> cancelledCallback)
ConfirmBox::ConfirmBox(
QWidget*,
const QString &text,
const QString &confirmText,
FnMut<void()> confirmedCallback,
FnMut<void()> cancelledCallback)
: _confirmText(confirmText)
, _cancelText(lang(lng_cancel))
, _confirmStyle(st::defaultBoxButton)
@@ -86,7 +66,12 @@ ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText
init(text);
}
ConfirmBox::ConfirmBox(QWidget*, const TextWithEntities &text, const QString &confirmText, FnMut<void()> confirmedCallback, FnMut<void()> cancelledCallback)
ConfirmBox::ConfirmBox(
QWidget*,
const TextWithEntities &text,
const QString &confirmText,
FnMut<void()> confirmedCallback,
FnMut<void()> cancelledCallback)
: _confirmText(confirmText)
, _cancelText(lang(lng_cancel))
, _confirmStyle(st::defaultBoxButton)
@@ -96,7 +81,13 @@ ConfirmBox::ConfirmBox(QWidget*, const TextWithEntities &text, const QString &co
init(text);
}
ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const style::RoundButton &confirmStyle, FnMut<void()> confirmedCallback, FnMut<void()> cancelledCallback)
ConfirmBox::ConfirmBox(
QWidget*,
const QString &text,
const QString &confirmText,
const style::RoundButton &confirmStyle,
FnMut<void()> confirmedCallback,
FnMut<void()> cancelledCallback)
: _confirmText(confirmText)
, _cancelText(lang(lng_cancel))
, _confirmStyle(confirmStyle)
@@ -106,7 +97,13 @@ ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText
init(text);
}
ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const QString &cancelText, FnMut<void()> confirmedCallback, FnMut<void()> cancelledCallback)
ConfirmBox::ConfirmBox(
QWidget*,
const QString &text,
const QString &confirmText,
const QString &cancelText,
FnMut<void()> confirmedCallback,
FnMut<void()> cancelledCallback)
: _confirmText(confirmText)
, _cancelText(cancelText)
, _confirmStyle(st::defaultBoxButton)
@@ -116,7 +113,14 @@ ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText
init(text);
}
ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const style::RoundButton &confirmStyle, const QString &cancelText, FnMut<void()> confirmedCallback, FnMut<void()> cancelledCallback)
ConfirmBox::ConfirmBox(
QWidget*,
const QString &text,
const QString &confirmText,
const style::RoundButton &confirmStyle,
const QString &cancelText,
FnMut<void()> confirmedCallback,
FnMut<void()> cancelledCallback)
: _confirmText(confirmText)
, _cancelText(cancelText)
, _confirmStyle(st::defaultBoxButton)
@@ -126,7 +130,11 @@ ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText
init(text);
}
ConfirmBox::ConfirmBox(const InformBoxTag &, const QString &text, const QString &doneText, Fn<void()> closedCallback)
ConfirmBox::ConfirmBox(
const InformBoxTag &,
const QString &text,
const QString &doneText,
Fn<void()> closedCallback)
: _confirmText(doneText)
, _confirmStyle(st::defaultBoxButton)
, _informative(true)
@@ -136,7 +144,11 @@ ConfirmBox::ConfirmBox(const InformBoxTag &, const QString &text, const QString
init(text);
}
ConfirmBox::ConfirmBox(const InformBoxTag &, const TextWithEntities &text, const QString &doneText, Fn<void()> closedCallback)
ConfirmBox::ConfirmBox(
const InformBoxTag &,
const TextWithEntities &text,
const QString &doneText,
Fn<void()> closedCallback)
: _confirmText(doneText)
, _confirmStyle(st::defaultBoxButton)
, _informative(true)
@@ -146,8 +158,9 @@ ConfirmBox::ConfirmBox(const InformBoxTag &, const TextWithEntities &text, const
init(text);
}
FnMut<void()> ConfirmBox::generateInformCallback(Fn<void()> closedCallback) {
return crl::guard(this, [this, closedCallback] {
FnMut<void()> ConfirmBox::generateInformCallback(
Fn<void()> closedCallback) {
return crl::guard(this, [=] {
closeBox();
if (closedCallback) {
closedCallback();
@@ -156,7 +169,10 @@ FnMut<void()> ConfirmBox::generateInformCallback(Fn<void()> closedCallback) {
}
void ConfirmBox::init(const QString &text) {
_text.setText(st::boxLabelStyle, text, _informative ? _confirmBoxTextOptions : _textPlainOptions);
_text.setText(
st::boxLabelStyle,
text,
_informative ? _confirmBoxTextOptions : _textPlainOptions);
}
void ConfirmBox::init(const TextWithEntities &text) {
@@ -164,9 +180,14 @@ void ConfirmBox::init(const TextWithEntities &text) {
}
void ConfirmBox::prepare() {
addButton([this] { return _confirmText; }, [this] { confirmed(); }, _confirmStyle);
addButton(
[=] { return _confirmText; },
[=] { confirmed(); },
_confirmStyle);
if (!_informative) {
addButton([this] { return _cancelText; }, [this] { _cancelled = true; closeBox(); });
addButton(
[=] { return _cancelText; },
[=] { _cancelled = true; closeBox(); });
}
boxClosing() | rpl::start_with_next([=] {
@@ -318,7 +339,7 @@ void MaxInviteBox::mousePressEvent(QMouseEvent *e) {
mouseMoveEvent(e);
if (_linkOver) {
if (_channel->inviteLink().isEmpty()) {
Auth().api().exportInviteLink(_channel);
_channel->session().api().exportInviteLink(_channel);
} else {
QGuiApplication::clipboard()->setText(_channel->inviteLink());
Ui::Toast::Show(lang(lng_create_channel_link_copied));
@@ -363,65 +384,6 @@ void MaxInviteBox::resizeEvent(QResizeEvent *e) {
_invitationLink = myrtlrect(st::boxPadding.left(), st::boxPadding.top() + _textHeight + st::boxTextFont->height, width() - st::boxPadding.left() - st::boxPadding.right(), 2 * st::boxTextFont->height);
}
ConvertToSupergroupBox::ConvertToSupergroupBox(QWidget*, ChatData *chat)
: _chat(chat)
, _text(100)
, _note(100) {
}
void ConvertToSupergroupBox::prepare() {
QStringList text;
text.push_back(lang(lng_profile_convert_feature1));
text.push_back(lang(lng_profile_convert_feature2));
text.push_back(lang(lng_profile_convert_feature3));
text.push_back(lang(lng_profile_convert_feature4));
setTitle(langFactory(lng_profile_convert_title));
addButton(langFactory(lng_profile_convert_confirm), [this] { convertToSupergroup(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
_text.setText(st::boxLabelStyle, text.join('\n'), _confirmBoxTextOptions);
_note.setText(st::boxLabelStyle, lng_profile_convert_warning(lt_bold_start, textcmdStartSemibold(), lt_bold_end, textcmdStopSemibold()), _confirmBoxTextOptions);
_textWidth = st::boxWideWidth - st::boxPadding.left() - st::boxButtonPadding.right();
_textHeight = _text.countHeight(_textWidth);
setDimensions(st::boxWideWidth, _textHeight + st::boxPadding.bottom() + _note.countHeight(_textWidth));
}
void ConvertToSupergroupBox::convertToSupergroup() {
MTP::send(MTPmessages_MigrateChat(_chat->inputChat), rpcDone(&ConvertToSupergroupBox::convertDone), rpcFail(&ConvertToSupergroupBox::convertFail));
}
void ConvertToSupergroupBox::convertDone(const MTPUpdates &updates) {
Ui::hideLayer();
ConvertToSupergroupDone(updates);
}
bool ConvertToSupergroupBox::convertFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
Ui::hideLayer();
return true;
}
void ConvertToSupergroupBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
convertToSupergroup();
} else {
BoxContent::keyPressEvent(e);
}
}
void ConvertToSupergroupBox::paintEvent(QPaintEvent *e) {
BoxContent::paintEvent(e);
Painter p(this);
// draw box title / text
p.setPen(st::boxTextFg);
_text.drawLeft(p, st::boxPadding.left(), 0, _textWidth, width());
_note.drawLeft(p, st::boxPadding.left(), _textHeight + st::boxPadding.bottom(), _textWidth, width());
}
PinMessageBox::PinMessageBox(
QWidget*,
not_null<PeerData*> peer,
@@ -479,7 +441,7 @@ void PinMessageBox::pinMessage() {
}
void PinMessageBox::pinDone(const MTPUpdates &updates) {
Auth().api().applyUpdates(updates);
_peer->session().api().applyUpdates(updates);
Ui::hideLayer();
}
@@ -618,20 +580,21 @@ void DeleteMessagesBox::keyPressEvent(QKeyEvent *e) {
void DeleteMessagesBox::deleteAndClear() {
if (_moderateFrom) {
if (_banUser && _banUser->checked()) {
Auth().api().kickParticipant(
_moderateInChannel->session().api().kickParticipant(
_moderateInChannel,
_moderateFrom,
MTP_channelBannedRights(MTP_flags(0), MTP_int(0)));
MTP_chatBannedRights(MTP_flags(0), MTP_int(0)));
}
if (_reportSpam->checked()) {
MTP::send(
_moderateInChannel->session().api().request(
MTPchannels_ReportSpam(
_moderateInChannel->inputChannel,
_moderateFrom->inputUser,
MTP_vector<MTPint>(1, MTP_int(_ids[0].msg))));
MTP_vector<MTPint>(1, MTP_int(_ids[0].msg)))
).send();
}
if (_deleteAll && _deleteAll->checked()) {
Auth().api().deleteAllFromUser(
_moderateInChannel->session().api().deleteAllFromUser(
_moderateInChannel,
_moderateFrom);
}
@@ -641,25 +604,26 @@ void DeleteMessagesBox::deleteAndClear() {
_deleteConfirmedCallback();
}
QMap<PeerData*, QVector<MTPint>> idsByPeer;
base::flat_map<not_null<PeerData*>, QVector<MTPint>> idsByPeer;
for (const auto itemId : _ids) {
if (auto item = App::histItemById(itemId)) {
auto history = item->history();
auto wasOnServer = (item->id > 0);
auto wasLast = (history->lastMessage() == item);
if (const auto item = App::histItemById(itemId)) {
const auto history = item->history();
const auto wasOnServer = IsServerMsgId(item->id);
const auto wasLast = (history->lastMessage() == item);
const auto wasInChats = (history->chatListMessage() == item);
item->destroy();
if (wasOnServer) {
idsByPeer[history->peer].push_back(MTP_int(itemId.msg));
} else if (wasLast && !history->lastMessageKnown()) {
Auth().api().requestDialogEntry(history);
} else if (wasLast || wasInChats) {
history->requestChatListMessage();
}
}
}
auto forEveryone = _forEveryone ? _forEveryone->checked() : false;
for (auto i = idsByPeer.cbegin(), e = idsByPeer.cend(); i != e; ++i) {
App::main()->deleteMessages(i.key(), i.value(), forEveryone);
const auto revoke = _forEveryone ? _forEveryone->checked() : false;
for (const auto &[peer, ids] : idsByPeer) {
App::main()->deleteMessages(peer, ids, revoke);
}
Ui::hideLayer();
Auth().data().sendHistoryChangeNotifications();
@@ -724,7 +688,7 @@ std::vector<not_null<UserData*>> ConfirmInviteBox::GetParticipants(
auto result = std::vector<not_null<UserData*>>();
result.reserve(v.size());
for (const auto &participant : v) {
if (const auto user = App::feedUser(participant)) {
if (const auto user = Auth().data().processUser(participant)) {
result.push_back(user);
}
}

View File

@@ -119,27 +119,6 @@ private:
};
class ConvertToSupergroupBox : public BoxContent, public RPCSender {
public:
ConvertToSupergroupBox(QWidget*, ChatData *chat);
protected:
void prepare() override;
void keyPressEvent(QKeyEvent *e) override;
void paintEvent(QPaintEvent *e) override;
private:
void convertToSupergroup();
void convertDone(const MTPUpdates &updates);
bool convertFail(const RPCError &error);
ChatData *_chat;
Text _text, _note;
int32 _textWidth, _textHeight;
};
class PinMessageBox : public BoxContent, public RPCSender {
public:
PinMessageBox(QWidget*, not_null<PeerData*> peer, MsgId msgId);

View File

@@ -170,7 +170,14 @@ void ConfirmPhoneBox::checkPhoneAndHash() {
if (_sendCodeRequestId) {
return;
}
_sendCodeRequestId = MTP::send(MTPaccount_SendConfirmPhoneCode(MTP_flags(0), MTP_string(_hash), MTPBool()), rpcDone(&ConfirmPhoneBox::sendCodeDone), rpcFail(&ConfirmPhoneBox::sendCodeFail));
_sendCodeRequestId = MTP::send(
MTPaccount_SendConfirmPhoneCode(
MTP_string(_hash),
MTP_codeSettings(
MTP_flags(0),
MTPstring())),
rpcDone(&ConfirmPhoneBox::sendCodeDone),
rpcFail(&ConfirmPhoneBox::sendCodeFail));
}
void ConfirmPhoneBox::sendCodeDone(const MTPauth_SentCode &result) {

View File

@@ -7,37 +7,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/connection_box.h"
#include "data/data_photo.h"
#include "data/data_document.h"
#include "boxes/confirm_box.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "base/qthelp_url.h"
#include "mainwidget.h"
#include "messenger.h"
#include "mainwindow.h"
#include "auth_session.h"
#include "data/data_session.h"
#include "mtproto/connection.h"
#include "core/application.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/dropdown_menu.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/toast/toast.h"
#include "ui/effects/radial_animation.h"
#include "ui/text_options.h"
#include "history/history_location_manager.h"
#include "settings/settings_common.h"
#include "application.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_info.h"
#include "styles/style_settings.h"
namespace {
@@ -360,7 +347,7 @@ void ProxyRow::paintCheck(Painter &p, TimeMs ms) {
}
const auto loading = _progress
? _progress->computeState()
: Ui::InfiniteRadialAnimation::State{ 0., 0, FullArcLength };
: Ui::RadialState{ 0., 0, FullArcLength };
const auto toggled = _toggled.current(ms, _view.selected ? 1. : 0.)
* (1. - loading.shown);
const auto _st = &st::defaultRadio;
@@ -374,6 +361,7 @@ void ProxyRow::paintCheck(Painter &p, TimeMs ms) {
auto pen = anim::pen(_st->untoggledFg, _st->toggledFg, toggled * set);
pen.setWidth(_st->thickness);
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);
@@ -936,102 +924,6 @@ void ProxyBox::addLabel(
} // namespace
AutoDownloadBox::AutoDownloadBox(QWidget *parent) {
}
void AutoDownloadBox::prepare() {
setupContent();
}
void AutoDownloadBox::setupContent() {
using namespace Settings;
setTitle(langFactory(lng_media_auto_title));
auto wrap = object_ptr<Ui::VerticalLayout>(this);
const auto content = wrap.data();
setInnerWidget(object_ptr<Ui::OverrideMargins>(
this,
std::move(wrap)));
using pair = std::pair<Ui::Checkbox*, Ui::Checkbox*>;
const auto pairValue = [](pair checkboxes) {
return (checkboxes.first->checked() ? 0 : dbiadNoPrivate)
| (checkboxes.second->checked() ? 0 : dbiadNoGroups);
};
const auto enabledSomething = [](int32 oldValue, int32 newValue) {
return (uint32(oldValue) & ~uint32(newValue)) != 0;
};
const auto addCheckbox = [&](int32 value, DBIAutoDownloadFlags flag) {
const auto label = (flag == dbiadNoPrivate)
? lng_media_auto_private_chats
: lng_media_auto_groups;
return content->add(
object_ptr<Ui::Checkbox>(
content,
lang(label),
!(value & flag),
st::settingsSendType),
st::settingsSendTypePadding);
};
const auto addPair = [&](int32 value) {
const auto first = addCheckbox(value, dbiadNoPrivate);
const auto second = addCheckbox(value, dbiadNoGroups);
return pair(first, second);
};
AddSubsectionTitle(content, lng_media_photo_title);
const auto photo = addPair(cAutoDownloadPhoto());
AddSkip(content);
AddSkip(content);
AddSubsectionTitle(content, lng_media_audio_title);
const auto audio = addPair(cAutoDownloadAudio());
AddSkip(content);
AddSkip(content);
AddSubsectionTitle(content, lng_media_gif_title);
const auto gif = addPair(cAutoDownloadGif());
AddSkip(content);
addButton(langFactory(lng_connection_save), [=] {
const auto photoValue = pairValue(photo);
const auto audioValue = pairValue(audio);
const auto gifValue = pairValue(gif);
const auto photosEnabled = enabledSomething(
cAutoDownloadPhoto(),
photoValue);
const auto audioEnabled = enabledSomething(
cAutoDownloadAudio(),
audioValue);
const auto gifEnabled = enabledSomething(
cAutoDownloadGif(),
gifValue);
const auto photosChanged = (cAutoDownloadPhoto() != photoValue);
const auto documentsChanged = (cAutoDownloadAudio() != audioValue)
|| (cAutoDownloadGif() != gifValue);
cSetAutoDownloadAudio(audioValue);
cSetAutoDownloadGif(gifValue);
cSetAutoDownloadPhoto(photoValue);
if (photosChanged || documentsChanged) {
Local::writeUserSettings();
}
if (photosEnabled) {
Auth().data().photoLoadSettingsChanged();
}
if (audioEnabled) {
Auth().data().voiceLoadSettingsChanged();
}
if (gifEnabled) {
Auth().data().animationLoadSettingsChanged();
}
closeBox();
});
addButton(langFactory(lng_cancel), [=] { closeBox(); });
setDimensionsToContent(st::boxWideWidth, content);
}
ProxiesBoxController::ProxiesBoxController()
: _saveTimer([] { Local::writeSettings(); }) {
_list = ranges::view::all(
@@ -1083,7 +975,7 @@ void ProxiesBoxController::ShowApplyConfirmation(
if (ranges::find(proxies, proxy) == end(proxies)) {
proxies.push_back(proxy);
}
Messenger::Instance().setCurrentProxy(
Core::App().setCurrentProxy(
proxy,
ProxyData::Settings::Enabled);
Local::writeSettings();
@@ -1106,7 +998,7 @@ void ProxiesBoxController::refreshChecker(Item &item) {
const auto type = (item.data.type == Type::Http)
? Variants::Http
: Variants::Tcp;
const auto mtproto = Messenger::Instance().mtp();
const auto mtproto = Core::App().mtp();
const auto dcId = mtproto->mainDcId();
item.state = ItemState::Checking;
@@ -1248,7 +1140,7 @@ void ProxiesBoxController::applyItem(int id) {
auto j = findByProxy(Global::SelectedProxy());
Messenger::Instance().setCurrentProxy(
Core::App().setCurrentProxy(
item->data,
ProxyData::Settings::Enabled);
saveDelayed();
@@ -1271,7 +1163,7 @@ void ProxiesBoxController::setDeleted(int id, bool deleted) {
_lastSelectedProxy = base::take(Global::RefSelectedProxy());
if (Global::ProxySettings() == ProxyData::Settings::Enabled) {
_lastSelectedProxyUsed = true;
Messenger::Instance().setCurrentProxy(
Core::App().setCurrentProxy(
ProxyData(),
ProxyData::Settings::System);
saveDelayed();
@@ -1296,7 +1188,7 @@ void ProxiesBoxController::setDeleted(int id, bool deleted) {
Assert(Global::ProxySettings() != ProxyData::Settings::Enabled);
if (base::take(_lastSelectedProxyUsed)) {
Messenger::Instance().setCurrentProxy(
Core::App().setCurrentProxy(
base::take(_lastSelectedProxy),
ProxyData::Settings::Enabled);
} else {
@@ -1401,9 +1293,7 @@ bool ProxiesBoxController::setProxySettings(ProxyData::Settings value) {
}
}
}
Messenger::Instance().setCurrentProxy(
Global::SelectedProxy(),
value);
Core::App().setCurrentProxy(Global::SelectedProxy(), value);
saveDelayed();
return true;
}
@@ -1491,7 +1381,7 @@ void ProxiesBoxController::share(const ProxyData &proxy) {
? "&pass=" + qthelp::url_encode(proxy.password) : "")
+ ((proxy.type == Type::Mtproto && !proxy.password.isEmpty())
? "&secret=" + proxy.password : "");
Application::clipboard()->setText(link);
QApplication::clipboard()->setText(link);
Ui::Toast::Show(lang(lng_username_copied));
}

View File

@@ -22,18 +22,6 @@ template <typename Enum>
class Radioenum;
} // namespace Ui
class AutoDownloadBox : public BoxContent {
public:
AutoDownloadBox(QWidget *parent);
protected:
void prepare() override;
private:
void setupContent();
};
class ProxiesBoxController : public base::Subscriber {
public:
using Type = ProxyData::Type;

View File

@@ -0,0 +1,729 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/create_poll_box.h"
#include "lang/lang_keys.h"
#include "data/data_poll.h"
#include "ui/toast/toast.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "core/event_filter.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "settings/settings_common.h"
#include "base/unique_qptr.h"
#include "styles/style_boxes.h"
#include "styles/style_settings.h"
namespace {
constexpr auto kQuestionLimit = 255;
constexpr auto kMaxOptionsCount = PollData::kMaxOptions;
constexpr auto kOptionLimit = 100;
constexpr auto kWarnQuestionLimit = 80;
constexpr auto kWarnOptionLimit = 30;
constexpr auto kErrorLimit = 99;
class Options {
public:
Options(
not_null<QWidget*> outer,
not_null<Ui::VerticalLayout*> container);
[[nodiscard]] bool isValid() const;
[[nodiscard]] rpl::producer<bool> isValidChanged() const;
[[nodiscard]] std::vector<PollAnswer> toPollAnswers() const;
void focusFirst();
[[nodiscard]] rpl::producer<int> usedCount() const;
[[nodiscard]] rpl::producer<not_null<QWidget*>> scrollToWidget() const;
[[nodiscard]] rpl::producer<> backspaceInFront() const;
private:
class Option {
public:
static Option Create(
not_null<QWidget*> outer,
not_null<Ui::VerticalLayout*> container,
int position);
void toggleRemoveAlways(bool toggled);
void show(anim::type animated);
void destroy(FnMut<void()> done);
//[[nodisacrd]] bool hasShadow() const;
//void destroyShadow();
[[nodiscard]] bool isEmpty() const;
[[nodiscard]] bool isGood() const;
[[nodiscard]] bool isTooLong() const;
[[nodiscard]] bool hasFocus() const;
void setFocus() const;
void clearValue();
void setPlaceholder() const;
void removePlaceholder() const;
not_null<Ui::InputField*> field() const;
[[nodiscard]] PollAnswer toPollAnswer(char id) const;
[[nodiscard]] rpl::producer<Qt::MouseButton> removeClicks() const;
inline bool operator<(const Option &other) const {
return field() < other.field();
}
friend inline bool operator<(
const Option &option,
Ui::InputField *field) {
return option.field() < field;
}
friend inline bool operator<(
Ui::InputField *field,
const Option &option) {
return field < option.field();
}
private:
Option() = default;
void createShadow();
void createRemove();
void createWarning();
base::unique_qptr<Ui::SlideWrap<Ui::InputField>> _field;
base::unique_qptr<Ui::PlainShadow> _shadow;
base::unique_qptr<Ui::CrossButton> _remove;
rpl::variable<bool> *_removeAlways = nullptr;
};
[[nodiscard]] bool full() const;
//[[nodiscard]] bool correctShadows() const;
//void fixShadows();
void removeEmptyTail();
void addEmptyOption();
void checkLastOption();
void validateState();
void fixAfterErase();
void destroy(Option &&option);
void removeDestroyed(not_null<Ui::InputField*> field);
int findField(not_null<Ui::InputField*> field) const;
not_null<QWidget*> _outer;
not_null<Ui::VerticalLayout*> _container;
int _position = 0;
std::vector<Option> _list;
std::set<Option, std::less<>> _destroyed;
rpl::variable<bool> _valid = false;
rpl::variable<int> _usedCount = 0;
rpl::event_stream<not_null<QWidget*>> _scrollToWidget;
rpl::event_stream<> _backspaceInFront;
};
void InitField(
not_null<QWidget*> container,
not_null<Ui::InputField*> field) {
field->setInstantReplaces(Ui::InstantReplaces::Default());
field->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
Ui::Emoji::SuggestionsController::Init(container, field);
}
not_null<Ui::FlatLabel*> CreateWarningLabel(
not_null<QWidget*> parent,
not_null<Ui::InputField*> field,
int valueLimit,
int warnLimit) {
const auto result = Ui::CreateChild<Ui::FlatLabel>(
parent.get(),
QString(),
Ui::FlatLabel::InitType::Simple,
st::createPollWarning);
result->setAttribute(Qt::WA_TransparentForMouseEvents);
QObject::connect(field, &Ui::InputField::changed, [=] {
Ui::PostponeCall(crl::guard(field, [=] {
const auto length = field->getLastText().size();
const auto value = valueLimit - length;
const auto shown = (value < warnLimit)
&& (field->height() > st::createPollOptionField.heightMin);
result->setRichText((value >= 0)
? QString::number(value)
: textcmdLink(1, QString::number(value)));
result->setVisible(shown);
}));
});
return result;
}
void FocusAtEnd(not_null<Ui::InputField*> field) {
field->setFocus();
field->setCursorPosition(field->getLastText().size());
field->ensureCursorVisible();
}
Options::Option Options::Option::Create(
not_null<QWidget*> outer,
not_null<Ui::VerticalLayout*> container,
int position) {
auto result = Option();
const auto field = container->insert(
position,
object_ptr<Ui::SlideWrap<Ui::InputField>>(
container,
object_ptr<Ui::InputField>(
container,
st::createPollOptionField,
langFactory(lng_polls_create_option_add))));
InitField(outer, field->entity());
field->entity()->setMaxLength(kOptionLimit + kErrorLimit);
result._field.reset(field);
result.createShadow();
result.createRemove();
result.createWarning();
return result;
}
//bool Options::Option::hasShadow() const {
// return (_shadow != nullptr);
//}
void Options::Option::createShadow() {
Expects(_field != nullptr);
if (_shadow) {
return;
}
const auto value = Ui::CreateChild<Ui::PlainShadow>(field().get());
value->show();
field()->sizeValue(
) | rpl::start_with_next([=](QSize size) {
const auto left = st::createPollFieldPadding.left();
value->setGeometry(
left,
size.height() - st::lineWidth,
size.width() - left,
st::lineWidth);
}, value->lifetime());
_shadow.reset(value);
}
//void Options::Option::destroyShadow() {
// _shadow = nullptr;
//}
void Options::Option::createRemove() {
using namespace rpl::mappers;
const auto field = this->field();
auto &lifetime = field->lifetime();
const auto remove = Ui::CreateChild<Ui::CrossButton>(
field.get(),
st::createPollOptionRemove);
remove->hide(anim::type::instant);
const auto toggle = lifetime.make_state<rpl::variable<bool>>(false);
_removeAlways = lifetime.make_state<rpl::variable<bool>>(false);
QObject::connect(field, &Ui::InputField::changed, [=] {
// Don't capture 'this'! Because Option is a value type.
*toggle = !field->getLastText().isEmpty();
});
rpl::combine(
toggle->value(),
_removeAlways->value(),
_1 || _2
) | rpl::start_with_next([=](bool shown) {
remove->toggle(shown, anim::type::normal);
}, remove->lifetime());
field->widthValue(
) | rpl::start_with_next([=](int width) {
remove->moveToRight(
st::createPollOptionRemovePosition.x(),
st::createPollOptionRemovePosition.y(),
width);
}, remove->lifetime());
_remove.reset(remove);
}
void Options::Option::createWarning() {
using namespace rpl::mappers;
const auto field = this->field();
const auto warning = CreateWarningLabel(
field,
field,
kOptionLimit,
kWarnOptionLimit);
rpl::combine(
field->sizeValue(),
warning->sizeValue()
) | rpl::start_with_next([=](QSize size, QSize label) {
warning->moveToLeft(
(size.width()
- label.width()
- st::createPollWarningPosition.x()),
(size.height()
- label.height()
- st::createPollWarningPosition.y()),
size.width());
}, warning->lifetime());
}
bool Options::Option::isEmpty() const {
return field()->getLastText().trimmed().isEmpty();
}
bool Options::Option::isGood() const {
return !field()->getLastText().trimmed().isEmpty() && !isTooLong();
}
bool Options::Option::isTooLong() const {
return (field()->getLastText().size() > kOptionLimit);
}
bool Options::Option::hasFocus() const {
return field()->hasFocus();
}
void Options::Option::setFocus() const {
FocusAtEnd(field());
}
void Options::Option::clearValue() {
field()->setText(QString());
}
void Options::Option::setPlaceholder() const {
field()->setPlaceholder(langFactory(lng_polls_create_option_add));
}
void Options::Option::toggleRemoveAlways(bool toggled) {
*_removeAlways = toggled;
}
not_null<Ui::InputField*> Options::Option::field() const {
return _field->entity();
}
void Options::Option::removePlaceholder() const {
field()->setPlaceholder(nullptr);
}
PollAnswer Options::Option::toPollAnswer(char id) const {
return PollAnswer{
field()->getLastText().trimmed(),
QByteArray(1, id)
};
}
rpl::producer<Qt::MouseButton> Options::Option::removeClicks() const {
return _remove->clicks();
}
Options::Options(
not_null<QWidget*> outer,
not_null<Ui::VerticalLayout*> container)
: _outer(outer)
, _container(container)
, _position(_container->count()) {
checkLastOption();
}
bool Options::full() const {
return (_list.size() == kMaxOptionsCount);
}
bool Options::isValid() const {
return _valid.current();
}
rpl::producer<bool> Options::isValidChanged() const {
return _valid.changes();
}
rpl::producer<int> Options::usedCount() const {
return _usedCount.value();
}
rpl::producer<not_null<QWidget*>> Options::scrollToWidget() const {
return _scrollToWidget.events();
}
rpl::producer<> Options::backspaceInFront() const {
return _backspaceInFront.events();
}
void Options::Option::show(anim::type animated) {
_field->hide(anim::type::instant);
_field->show(animated);
}
void Options::Option::destroy(FnMut<void()> done) {
if (anim::Disabled() || _field->isHidden()) {
Ui::PostponeCall(std::move(done));
return;
}
_field->hide(anim::type::normal);
App::CallDelayed(
st::slideWrapDuration * 2,
_field.get(),
std::move(done));
}
std::vector<PollAnswer> Options::toPollAnswers() const {
auto result = std::vector<PollAnswer>();
result.reserve(_list.size());
auto counter = char(0);
const auto makeAnswer = [&](const Option &option) {
return option.toPollAnswer(++counter);
};
ranges::copy(
_list
| ranges::view::filter(&Option::isGood)
| ranges::view::transform(makeAnswer),
ranges::back_inserter(result));
return result;
}
void Options::focusFirst() {
Expects(!_list.empty());
_list.front().setFocus();
}
//
//bool Options::correctShadows() const {
// // Last one should be without shadow if all options were used.
// const auto noShadow = ranges::find(
// _list,
// true,
// ranges::not_fn(&Option::hasShadow));
// return (noShadow == end(_list) - (full() ? 1 : 0));
//}
//
//void Options::fixShadows() {
// if (correctShadows()) {
// return;
// }
// for (auto &option : _list) {
// option.createShadow();
// }
// if (full()) {
// _list.back().destroyShadow();
// }
//}
void Options::removeEmptyTail() {
// Only one option at the end of options list can be empty.
// Remove all other trailing empty options.
// Only last empty and previous option have non-empty placeholders.
const auto focused = ranges::find_if(
_list,
&Option::hasFocus);
const auto end = _list.end();
const auto reversed = ranges::view::reverse(_list);
const auto emptyItem = ranges::find_if(
reversed,
ranges::not_fn(&Option::isEmpty)).base();
const auto focusLast = (focused > emptyItem) && (focused < end);
if (emptyItem == end) {
return;
}
if (focusLast) {
emptyItem->setFocus();
}
for (auto i = emptyItem + 1; i != end; ++i) {
destroy(std::move(*i));
}
_list.erase(emptyItem + 1, end);
fixAfterErase();
}
void Options::destroy(Option &&option) {
const auto field = option.field();
option.destroy([=] { removeDestroyed(field); });
_destroyed.emplace(std::move(option));
}
void Options::fixAfterErase() {
Expects(!_list.empty());
const auto last = _list.end() - 1;
last->setPlaceholder();
last->toggleRemoveAlways(false);
if (last != begin(_list)) {
(last - 1)->setPlaceholder();
(last - 1)->toggleRemoveAlways(false);
}
}
void Options::addEmptyOption() {
if (full()) {
return;
} else if (!_list.empty() && _list.back().isEmpty()) {
return;
}
if (_list.size() > 1) {
(_list.end() - 2)->removePlaceholder();
(_list.end() - 2)->toggleRemoveAlways(true);
}
_list.push_back(Option::Create(
_outer,
_container,
_position + _list.size() + _destroyed.size()));
const auto field = _list.back().field();
QObject::connect(field, &Ui::InputField::submitted, [=] {
const auto index = findField(field);
if (_list[index].isGood() && index + 1 < _list.size()) {
_list[index + 1].setFocus();
}
});
QObject::connect(field, &Ui::InputField::changed, [=] {
Ui::PostponeCall(crl::guard(field, [=] {
validateState();
}));
});
QObject::connect(field, &Ui::InputField::focused, [=] {
_scrollToWidget.fire_copy(field);
});
Core::InstallEventFilter(field, [=](not_null<QEvent*> event) {
if (event->type() != QEvent::KeyPress
|| !field->getLastText().isEmpty()) {
return false;
}
const auto key = static_cast<QKeyEvent*>(event.get())->key();
if (key != Qt::Key_Backspace) {
return false;
}
const auto index = findField(field);
if (index > 0) {
_list[index - 1].setFocus();
} else {
_backspaceInFront.fire({});
}
return true;
});
_list.back().removeClicks(
) | rpl::start_with_next([=] {
Ui::PostponeCall(crl::guard(field, [=] {
Expects(!_list.empty());
const auto item = begin(_list) + findField(field);
if (item == _list.end() - 1) {
item->clearValue();
return;
}
if (item->hasFocus()) {
(item + 1)->setFocus();
}
destroy(std::move(*item));
_list.erase(item);
fixAfterErase();
validateState();
}));
}, field->lifetime());
_list.back().show((_list.size() == 1)
? anim::type::instant
: anim::type::normal);
//fixShadows();
}
void Options::removeDestroyed(not_null<Ui::InputField*> field) {
_destroyed.erase(_destroyed.find(field));
}
void Options::validateState() {
checkLastOption();
_valid = (ranges::count_if(_list, &Option::isGood) > 1)
&& (ranges::find_if(_list, &Option::isTooLong) == end(_list));
const auto lastEmpty = !_list.empty() && _list.back().isEmpty();
_usedCount = _list.size() - (lastEmpty ? 1 : 0);
}
int Options::findField(not_null<Ui::InputField*> field) const {
const auto result = ranges::find(
_list,
field,
&Option::field) - begin(_list);
Ensures(result >= 0 && result < _list.size());
return result;
}
void Options::checkLastOption() {
removeEmptyTail();
addEmptyOption();
}
} // namespace
CreatePollBox::CreatePollBox(QWidget*) {
}
rpl::producer<PollData> CreatePollBox::submitRequests() const {
return _submitRequests.events();
}
void CreatePollBox::setInnerFocus() {
_setInnerFocus();
}
void CreatePollBox::submitFailed(const QString &error) {
Ui::Toast::Show(error);
}
not_null<Ui::InputField*> CreatePollBox::setupQuestion(
not_null<Ui::VerticalLayout*> container) {
using namespace Settings;
AddSubsectionTitle(container, lng_polls_create_question);
const auto question = container->add(
object_ptr<Ui::InputField>(
container,
st::createPollField,
Ui::InputField::Mode::MultiLine,
langFactory(lng_polls_create_question_placeholder)),
st::createPollFieldPadding);
InitField(getDelegate()->outerContainer(), question);
question->setMaxLength(kQuestionLimit + kErrorLimit);
const auto warning = CreateWarningLabel(
container,
question,
kQuestionLimit,
kWarnQuestionLimit);
rpl::combine(
question->geometryValue(),
warning->sizeValue()
) | rpl::start_with_next([=](QRect geometry, QSize label) {
warning->moveToLeft(
(container->width()
- label.width()
- st::createPollWarningPosition.x()),
(geometry.y()
- st::createPollFieldPadding.top()
- st::settingsSubsectionTitlePadding.bottom()
- st::settingsSubsectionTitle.style.font->height
+ st::settingsSubsectionTitle.style.font->ascent
- st::createPollWarning.style.font->ascent),
geometry.width());
}, warning->lifetime());
return question;
}
object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
using namespace Settings;
const auto id = rand_value<uint64>();
const auto valid = lifetime().make_state<rpl::event_stream<bool>>();
auto result = object_ptr<Ui::VerticalLayout>(this);
const auto container = result.data();
const auto question = setupQuestion(container);
AddDivider(container);
AddSkip(container);
AddSubsectionTitle(container, lng_polls_create_options);
const auto options = lifetime().make_state<Options>(
getDelegate()->outerContainer(),
container);
auto limit = options->usedCount() | rpl::after_next([=](int count) {
setCloseByEscape(!count);
setCloseByOutsideClick(!count);
}) | rpl::map([=](int count) {
return (count < kMaxOptionsCount)
? lng_polls_create_limit(lt_count, kMaxOptionsCount - count)
: lang(lng_polls_create_maximum);
}) | rpl::after_next([=] {
container->resizeToWidth(container->widthNoMargins());
});
container->add(
object_ptr<Ui::FlatLabel>(
container,
std::move(limit),
st::createPollLimitLabel),
st::createPollLimitPadding);
const auto isValidQuestion = [=] {
const auto text = question->getLastText().trimmed();
return !text.isEmpty() && (text.size() <= kQuestionLimit);
};
connect(question, &Ui::InputField::submitted, [=] {
if (isValidQuestion()) {
options->focusFirst();
}
});
_setInnerFocus = [=] {
question->setFocusFast();
};
const auto collectResult = [=] {
auto result = PollData(id);
result.question = question->getLastText().trimmed();
result.answers = options->toPollAnswers();
return result;
};
const auto updateValid = [=] {
valid->fire(isValidQuestion() && options->isValid());
};
connect(question, &Ui::InputField::changed, [=] {
updateValid();
});
valid->events_starting_with(
false
) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](bool valid) {
clearButtons();
if (valid) {
addButton(
langFactory(lng_polls_create_button),
[=] { _submitRequests.fire(collectResult()); });
}
addButton(langFactory(lng_cancel), [=] { closeBox(); });
}, lifetime());
options->isValidChanged(
) | rpl::start_with_next([=] {
updateValid();
}, lifetime());
options->scrollToWidget(
) | rpl::start_with_next([=](not_null<QWidget*> widget) {
scrollToWidget(widget);
}, lifetime());
options->backspaceInFront(
) | rpl::start_with_next([=] {
FocusAtEnd(question);
}, lifetime());
return std::move(result);
}
void CreatePollBox::prepare() {
setTitle(langFactory(lng_polls_create_title));
const auto inner = setInnerWidget(setupContent());
setDimensionsToContent(st::boxWideWidth, inner);
}

View File

@@ -0,0 +1,39 @@
/*
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 "boxes/abstract_box.h"
namespace Ui {
class VerticalLayout;
} // namespace Ui
struct PollData;
class CreatePollBox : public BoxContent {
public:
CreatePollBox(QWidget*);
rpl::producer<PollData> submitRequests() const;
void submitFailed(const QString &error);
void setInnerFocus() override;
protected:
void prepare() override;
private:
object_ptr<Ui::RpWidget> setupContent();
not_null<Ui::InputField*> setupQuestion(
not_null<Ui::VerticalLayout*> container);
Fn<void()> _setInnerFocus;
Fn<rpl::producer<bool>()> _dataIsValidValue;
rpl::event_stream<PollData> _submitRequests;
};

View File

@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_media_types.h"
#include "data/data_photo.h"
#include "data/data_document.h"
#include "data/data_user.h"
#include "lang/lang_keys.h"
#include "core/event_filter.h"
#include "chat_helpers/message_field.h"
@@ -41,17 +42,17 @@ EditCaptionBox::EditCaptionBox(
Expects(item->media()->allowsEditCaption());
QSize dimensions;
ImagePtr image;
auto image = (Image*)nullptr;
DocumentData *doc = nullptr;
const auto media = item->media();
if (const auto photo = media->photo()) {
_photo = true;
dimensions = QSize(photo->full->width(), photo->full->height());
image = photo->full;
dimensions = QSize(photo->width(), photo->height());
image = photo->large();
} else if (const auto document = media->document()) {
dimensions = document->dimensions;
image = document->thumb;
image = document->thumbnail();
if (document->isAnimation()) {
_animated = true;
} else if (document->isVideoFile()) {
@@ -67,8 +68,8 @@ EditCaptionBox::EditCaptionBox(
ConvertEntitiesToTextTags(original.entities)
};
if (!_animated && (dimensions.isEmpty() || doc || image->isNull())) {
if (image->isNull()) {
if (!_animated && (dimensions.isEmpty() || doc || !image)) {
if (!image) {
_thumbw = 0;
} else {
int32 tw = image->width(), th = image->height();
@@ -500,8 +501,7 @@ void EditCaptionBox::save() {
MTP_string(sending.text),
MTPInputMedia(),
MTPnullMarkup,
sentEntities,
MTP_inputGeoPointEmpty()),
sentEntities),
rpcDone(&EditCaptionBox::saveDone),
rpcFail(&EditCaptionBox::saveFail));
}

View File

@@ -63,7 +63,7 @@ private:
not_null<Window::Controller*> _controller;
FullMsgId _msgId;
ImagePtr _thumbnailImage;
Image *_thumbnailImage = nullptr;
bool _thumbnailImageLoaded = false;
Fn<void()> _refreshThumbnail;
bool _animated = false;

View File

@@ -1,523 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/edit_participant_box.h"
#include "lang/lang_keys.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/text_options.h"
#include "ui/special_buttons.h"
#include "boxes/calendar_box.h"
#include "data/data_peer_values.h"
#include "styles/style_boxes.h"
namespace {
constexpr auto kMaxRestrictDelayDays = 366;
constexpr auto kSecondsInDay = 24 * 60 * 60;
constexpr auto kSecondsInWeek = 7 * kSecondsInDay;
template <typename CheckboxesMap, typename DependenciesMap>
void ApplyDependencies(CheckboxesMap &checkboxes, DependenciesMap &dependencies, QPointer<Ui::Checkbox> changed) {
auto checkAndApply = [&checkboxes](auto &&current, auto dependency, bool isChecked) {
for (auto &&checkbox : checkboxes) {
if ((checkbox.first & dependency) && (checkbox.second->checked() == isChecked)) {
current->setChecked(isChecked);
return true;
}
}
return false;
};
auto applySomeDependency = [&checkboxes, &dependencies, &changed, checkAndApply] {
auto result = false;
for (auto &&entry : checkboxes) {
if (entry.second == changed) {
continue;
}
auto isChecked = entry.second->checked();
for (auto &&dependency : dependencies) {
if (entry.first & (isChecked ? dependency.first : dependency.second)) {
if (checkAndApply(entry.second, (isChecked ? dependency.second : dependency.first), !isChecked)) {
result = true;
break;
}
}
}
}
return result;
};
while (true) {
if (!applySomeDependency()) {
break;
}
};
}
} // namespace
class EditParticipantBox::Inner : public TWidget {
public:
Inner(
QWidget *parent,
not_null<ChannelData*> channel,
not_null<UserData*> user,
bool hasAdminRights);
template <typename Widget>
QPointer<Widget> addControl(object_ptr<Widget> widget, QMargins margin) {
doAddControl(std::move(widget), margin);
return static_cast<Widget*>(_rows.back().widget.data());
}
void removeControl(QPointer<TWidget> widget);
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
void doAddControl(object_ptr<TWidget> widget, QMargins margin);
not_null<ChannelData*> _channel;
not_null<UserData*> _user;
object_ptr<Ui::UserpicButton> _userPhoto;
Text _userName;
bool _hasAdminRights = false;
struct Control {
object_ptr<TWidget> widget;
QMargins margin;
};
std::vector<Control> _rows;
};
EditParticipantBox::Inner::Inner(
QWidget *parent,
not_null<ChannelData*> channel,
not_null<UserData*> user,
bool hasAdminRights)
: TWidget(parent)
, _channel(channel)
, _user(user)
, _userPhoto(
this,
_user,
Ui::UserpicButton::Role::Custom,
st::rightsPhotoButton)
, _hasAdminRights(hasAdminRights) {
_userPhoto->setPointerCursor(false);
_userName.setText(
st::rightsNameStyle,
App::peerName(_user),
Ui::NameTextOptions());
}
void EditParticipantBox::Inner::removeControl(QPointer<TWidget> widget) {
auto row = std::find_if(_rows.begin(), _rows.end(), [widget](auto &&row) {
return (row.widget == widget);
});
Assert(row != _rows.end());
row->widget.destroy();
_rows.erase(row);
}
void EditParticipantBox::Inner::doAddControl(object_ptr<TWidget> widget, QMargins margin) {
widget->setParent(this);
_rows.push_back({ std::move(widget), margin });
_rows.back().widget->show();
}
int EditParticipantBox::Inner::resizeGetHeight(int newWidth) {
_userPhoto->moveToLeft(st::rightsPhotoMargin.left(), st::rightsPhotoMargin.top());
auto newHeight = st::rightsPhotoMargin.top()
+ st::rightsPhotoButton.size.height()
+ st::rightsPhotoMargin.bottom();
for (auto &&row : _rows) {
auto rowWidth = newWidth - row.margin.left() - row.margin.right();
newHeight += row.margin.top();
row.widget->resizeToNaturalWidth(rowWidth);
row.widget->moveToLeft(row.margin.left(), newHeight);
newHeight += row.widget->heightNoMargins() + row.margin.bottom();
}
return newHeight;
}
void EditParticipantBox::Inner::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(e->rect(), st::boxBg);
p.setPen(st::contactsNameFg);
auto namex = st::rightsPhotoMargin.left()
+ st::rightsPhotoButton.size .width()
+ st::rightsPhotoMargin.right();
auto namew = width() - namex - st::rightsPhotoMargin.right();
_userName.drawLeftElided(p, namex, st::rightsPhotoMargin.top() + st::rightsNameTop, namew, width());
auto statusText = [this] {
if (_user->botInfo) {
auto seesAllMessages = (_user->botInfo->readsAllHistory || _hasAdminRights);
return lang(seesAllMessages ? lng_status_bot_reads_all : lng_status_bot_not_reads_all);
}
return Data::OnlineText(_user->onlineTill, unixtime());
};
p.setFont(st::contactsStatusFont);
p.setPen(st::contactsStatusFg);
p.drawTextLeft(namex, st::rightsPhotoMargin.top() + st::rightsStatusTop, width(), statusText());
}
EditParticipantBox::EditParticipantBox(QWidget*, not_null<ChannelData*> channel, not_null<UserData*> user, bool hasAdminRights) : BoxContent()
, _channel(channel)
, _user(user)
, _hasAdminRights(hasAdminRights) {
}
void EditParticipantBox::prepare() {
_inner = setInnerWidget(object_ptr<Inner>(
this,
_channel,
_user,
hasAdminRights()));
}
template <typename Widget>
QPointer<Widget> EditParticipantBox::addControl(object_ptr<Widget> widget, QMargins margin) {
Expects(_inner != nullptr);
return _inner->addControl(std::move(widget), margin);
}
void EditParticipantBox::removeControl(QPointer<TWidget> widget) {
Expects(_inner != nullptr);
return _inner->removeControl(widget);
}
void EditParticipantBox::resizeToContent() {
_inner->resizeToWidth(st::boxWideWidth);
setDimensions(_inner->width(), qMin(_inner->height(), st::boxMaxListHeight));
}
EditAdminBox::EditAdminBox(QWidget*, not_null<ChannelData*> channel, not_null<UserData*> user, const MTPChannelAdminRights &rights) : EditParticipantBox(nullptr, channel, user, (rights.c_channelAdminRights().vflags.v != 0))
, _oldRights(rights) {
auto dependency = [this](Flag dependent, Flag dependency) {
_dependencies.push_back(std::make_pair(dependent, dependency));
};
dependency(Flag::f_invite_link, Flag::f_invite_users); // invite_link <-> invite_users
dependency(Flag::f_invite_users, Flag::f_invite_link);
}
MTPChannelAdminRights EditAdminBox::DefaultRights(not_null<ChannelData*> channel) {
auto defaultRights = channel->isMegagroup()
? (Flag::f_change_info | Flag::f_delete_messages | Flag::f_ban_users | Flag::f_invite_users | Flag::f_invite_link | Flag::f_pin_messages)
: (Flag::f_change_info | Flag::f_post_messages | Flag::f_edit_messages | Flag::f_delete_messages | Flag::f_invite_users | Flag::f_invite_link);
return MTP_channelAdminRights(MTP_flags(defaultRights));
}
void EditAdminBox::prepare() {
EditParticipantBox::prepare();
auto hadRights = _oldRights.c_channelAdminRights().vflags.v;
setTitle(langFactory(hadRights ? lng_rights_edit_admin : lng_channel_add_admin));
addControl(object_ptr<BoxContentDivider>(this), QMargins());
addControl(object_ptr<Ui::FlatLabel>(this, lang(lng_rights_edit_admin_header), Ui::FlatLabel::InitType::Simple, st::rightsHeaderLabel), st::rightsHeaderMargin);
const auto prepareRights = hadRights ? _oldRights : DefaultRights(channel());
const auto filterByMyRights = canSave()
&& !hadRights
&& !channel()->amCreator();
const auto prepareFlags = prepareRights.c_channelAdminRights().vflags.v
& (filterByMyRights ? channel()->adminRights() : ~Flag(0));
auto addCheckbox = [&](Flags flags, const QString &text) {
const auto checked = (prepareFlags & flags) != 0;
auto control = addControl(object_ptr<Ui::Checkbox>(this, text, checked, st::rightsCheckbox, st::rightsToggle), st::rightsToggleMargin);
control->checkedChanges(
) | rpl::start_with_next([=](bool checked) {
InvokeQueued(this, [=] {
applyDependencies(control);
});
}, control->lifetime());
if (!channel()->amCreator()) {
if (!(channel()->adminRights() & flags)) {
control->setDisabled(true); // Grey out options that we don't have ourselves.
}
}
if (!canSave()) {
control->setDisabled(true);
}
_checkboxes.emplace(flags, control);
};
if (channel()->isMegagroup()) {
addCheckbox(Flag::f_change_info, lang(lng_rights_group_info));
addCheckbox(Flag::f_delete_messages, lang(lng_rights_group_delete));
addCheckbox(Flag::f_ban_users, lang(lng_rights_group_ban));
addCheckbox(Flag::f_invite_users | Flag::f_invite_link, lang(channel()->anyoneCanAddMembers() ? lng_rights_group_invite_link : lng_rights_group_invite));
addCheckbox(Flag::f_pin_messages, lang(lng_rights_group_pin));
addCheckbox(Flag::f_add_admins, lang(lng_rights_add_admins));
} else {
addCheckbox(Flag::f_change_info, lang(lng_rights_channel_info));
addCheckbox(Flag::f_post_messages, lang(lng_rights_channel_post));
addCheckbox(Flag::f_edit_messages, lang(lng_rights_channel_edit));
addCheckbox(Flag::f_delete_messages, lang(lng_rights_channel_delete));
addCheckbox(Flag::f_invite_users | Flag::f_invite_link, lang(lng_rights_group_invite));
addCheckbox(Flag::f_add_admins, lang(lng_rights_add_admins));
}
auto addAdmins = _checkboxes.find(Flag::f_add_admins);
if (addAdmins != _checkboxes.end()) {
_aboutAddAdmins = addControl(
object_ptr<Ui::FlatLabel>(this, st::boxLabel),
st::rightsAboutMargin);
Assert(addAdmins != _checkboxes.end());
addAdmins->second->checkedChanges(
) | rpl::start_with_next([=](bool checked) {
refreshAboutAddAdminsText();
}, addAdmins->second->lifetime());
refreshAboutAddAdminsText();
}
if (canSave()) {
addButton(langFactory(lng_settings_save), [this] {
if (!_saveCallback) {
return;
}
auto newFlags = MTPDchannelAdminRights::Flags(0);
for (auto &&checkbox : _checkboxes) {
if (checkbox.second->checked()) {
newFlags |= checkbox.first;
} else {
newFlags &= ~checkbox.first;
}
}
if (!channel()->amCreator()) {
// Leave only rights that we have so we could save them.
newFlags &= channel()->adminRights();
}
_saveCallback(_oldRights, MTP_channelAdminRights(MTP_flags(newFlags)));
});
addButton(langFactory(lng_cancel), [this] { closeBox(); });
} else {
addButton(langFactory(lng_box_ok), [this] { closeBox(); });
}
applyDependencies(nullptr);
for (auto &&checkbox : _checkboxes) {
checkbox.second->finishAnimating();
}
resizeToContent();
}
void EditAdminBox::applyDependencies(QPointer<Ui::Checkbox> changed) {
ApplyDependencies(_checkboxes, _dependencies, changed);
}
void EditAdminBox::refreshAboutAddAdminsText() {
auto addAdmins = _checkboxes.find(Flag::f_add_admins);
Assert(addAdmins != _checkboxes.end());
auto text = [this, addAdmins] {
if (!canSave()) {
return lang(lng_rights_about_admin_cant_edit);
} else if (addAdmins->second->checked()) {
return lang(lng_rights_about_add_admins_yes);
}
return lang(lng_rights_about_add_admins_no);
};
_aboutAddAdmins->setText(text());
resizeToContent();
}
EditRestrictedBox::EditRestrictedBox(QWidget*, not_null<ChannelData*> channel, not_null<UserData*> user, bool hasAdminRights, const MTPChannelBannedRights &rights) : EditParticipantBox(nullptr, channel, user, hasAdminRights)
, _oldRights(rights) {
auto dependency = [this](Flag dependent, Flag dependency) {
_dependencies.push_back(std::make_pair(dependent, dependency));
};
dependency(Flag::f_send_gifs, Flag::f_send_stickers); // stickers <-> gifs
dependency(Flag::f_send_stickers, Flag::f_send_gifs);
dependency(Flag::f_send_games, Flag::f_send_stickers); // stickers <-> games
dependency(Flag::f_send_stickers, Flag::f_send_games);
dependency(Flag::f_send_inline, Flag::f_send_stickers); // stickers <-> inline
dependency(Flag::f_send_stickers, Flag::f_send_inline);
dependency(Flag::f_send_stickers, Flag::f_send_media); // stickers -> send_media
dependency(Flag::f_embed_links, Flag::f_send_media); // embed_links -> send_media
dependency(Flag::f_send_media, Flag::f_send_messages); // send_media- > send_messages
dependency(Flag::f_send_messages, Flag::f_view_messages); // send_messages -> view_messages
}
void EditRestrictedBox::prepare() {
EditParticipantBox::prepare();
setTitle(langFactory(lng_rights_user_restrictions));
addControl(object_ptr<BoxContentDivider>(this), QMargins());
addControl(object_ptr<Ui::FlatLabel>(this, lang(lng_rights_user_restrictions_header), Ui::FlatLabel::InitType::Simple, st::rightsHeaderLabel), st::rightsHeaderMargin);
auto prepareRights = (_oldRights.c_channelBannedRights().vflags.v ? _oldRights : DefaultRights(channel()));
_until = prepareRights.c_channelBannedRights().vuntil_date.v;
auto addCheckbox = [&](Flags flags, const QString &text) {
auto checked = (prepareRights.c_channelBannedRights().vflags.v & flags) == 0;
auto control = addControl(object_ptr<Ui::Checkbox>(this, text, checked, st::rightsCheckbox, st::rightsToggle), st::rightsToggleMargin);
control->checkedChanges(
) | rpl::start_with_next([=](bool checked) {
InvokeQueued(this, [=] {
applyDependencies(control);
});
}, control->lifetime());
if (!canSave()) {
control->setDisabled(true);
}
_checkboxes.emplace(flags, control);
};
addCheckbox(Flag::f_view_messages, lang(lng_rights_chat_read));
addCheckbox(Flag::f_send_messages, lang(lng_rights_chat_send_text));
addCheckbox(Flag::f_send_media, lang(lng_rights_chat_send_media));
addCheckbox(Flag::f_send_stickers | Flag::f_send_gifs | Flag::f_send_games | Flag::f_send_inline, lang(lng_rights_chat_send_stickers));
addCheckbox(Flag::f_embed_links, lang(lng_rights_chat_send_links));
addControl(object_ptr<BoxContentDivider>(this), st::rightsUntilMargin);
addControl(object_ptr<Ui::FlatLabel>(this, lang(lng_rights_chat_banned_until_header), Ui::FlatLabel::InitType::Simple, st::rightsHeaderLabel), st::rightsHeaderMargin);
setRestrictUntil(_until);
//addControl(object_ptr<Ui::LinkButton>(this, lang(lng_rights_chat_banned_block), st::boxLinkButton));
if (canSave()) {
addButton(langFactory(lng_settings_save), [this] {
if (!_saveCallback) {
return;
}
auto newFlags = MTPDchannelBannedRights::Flags(0);
for (auto &&checkbox : _checkboxes) {
if (checkbox.second->checked()) {
newFlags &= ~checkbox.first;
} else {
newFlags |= checkbox.first;
}
}
_saveCallback(_oldRights, MTP_channelBannedRights(MTP_flags(newFlags), MTP_int(getRealUntilValue())));
});
addButton(langFactory(lng_cancel), [this] { closeBox(); });
} else {
addButton(langFactory(lng_box_ok), [this] { closeBox(); });
}
applyDependencies(nullptr);
for (auto &&checkbox : _checkboxes) {
checkbox.second->finishAnimating();
}
resizeToContent();
}
void EditRestrictedBox::applyDependencies(QPointer<Ui::Checkbox> changed) {
ApplyDependencies(_checkboxes, _dependencies, changed);
}
MTPChannelBannedRights EditRestrictedBox::DefaultRights(not_null<ChannelData*> channel) {
auto defaultRights = Flag::f_send_messages | Flag::f_send_media | Flag::f_embed_links | Flag::f_send_stickers | Flag::f_send_gifs | Flag::f_send_games | Flag::f_send_inline;
return MTP_channelBannedRights(MTP_flags(defaultRights), MTP_int(0));
}
void EditRestrictedBox::showRestrictUntil() {
auto tomorrow = QDate::currentDate().addDays(1);
auto highlighted = isUntilForever() ? tomorrow : ParseDateTime(getRealUntilValue()).date();
auto month = highlighted;
_restrictUntilBox = Ui::show(
Box<CalendarBox>(
month,
highlighted,
[this](const QDate &date) {
setRestrictUntil(static_cast<int>(QDateTime(date).toTime_t()));
}),
LayerOption::KeepOther);
_restrictUntilBox->setMaxDate(QDate::currentDate().addDays(kMaxRestrictDelayDays));
_restrictUntilBox->setMinDate(tomorrow);
_restrictUntilBox->addLeftButton(langFactory(lng_rights_chat_banned_forever), [this] { setRestrictUntil(0); });
}
void EditRestrictedBox::setRestrictUntil(TimeId until) {
_until = until;
if (_restrictUntilBox) {
_restrictUntilBox->closeBox();
}
clearVariants();
createUntilGroup();
createUntilVariants();
resizeToContent();
}
void EditRestrictedBox::clearVariants() {
for (auto &&widget : base::take(_untilVariants)) {
removeControl(widget.data());
}
}
void EditRestrictedBox::createUntilGroup() {
_untilGroup = std::make_shared<Ui::RadiobuttonGroup>(isUntilForever() ? 0 : _until);
_untilGroup->setChangedCallback([this](int value) {
if (value == kUntilCustom) {
_untilGroup->setValue(_until);
showRestrictUntil();
} else if (_until != value) {
_until = value;
}
});
}
void EditRestrictedBox::createUntilVariants() {
auto addVariant = [this](int value, const QString &text) {
if (!canSave() && _untilGroup->value() != value) {
return;
}
_untilVariants.push_back(addControl(object_ptr<Ui::Radiobutton>(this, _untilGroup, value, text, st::defaultBoxCheckbox), st::rightsToggleMargin));
if (!canSave()) {
_untilVariants.back()->setDisabled(true);
}
};
auto addCustomVariant = [addVariant](TimeId until, TimeId from, TimeId to) {
if (!ChannelData::IsRestrictedForever(until) && until > from && until <= to) {
addVariant(
until,
lng_rights_chat_banned_custom_date(
lt_date,
langDayOfMonthFull(ParseDateTime(until).date())));
}
};
auto addCurrentVariant = [this, addCustomVariant](TimeId from, TimeId to) {
auto oldUntil = _oldRights.c_channelBannedRights().vuntil_date.v;
if (oldUntil < _until) {
addCustomVariant(oldUntil, from, to);
}
addCustomVariant(_until, from, to);
if (oldUntil > _until) {
addCustomVariant(oldUntil, from, to);
}
};
addVariant(0, lang(lng_rights_chat_banned_forever));
auto now = unixtime();
auto nextDay = now + kSecondsInDay;
auto nextWeek = now + kSecondsInWeek;
addCurrentVariant(0, nextDay);
addVariant(kUntilOneDay, lng_rights_chat_banned_day(lt_count, 1));
addCurrentVariant(nextDay, nextWeek);
addVariant(kUntilOneWeek, lng_rights_chat_banned_week(lt_count, 1));
addCurrentVariant(nextWeek, INT_MAX);
addVariant(kUntilCustom, lang(lng_rights_chat_banned_custom));
}
TimeId EditRestrictedBox::getRealUntilValue() const {
Expects(_until != kUntilCustom);
if (_until == kUntilOneDay) {
return unixtime() + kSecondsInDay;
} else if (_until == kUntilOneWeek) {
return unixtime() + kSecondsInWeek;
}
Assert(_until >= 0);
return _until;
}

View File

@@ -1,138 +0,0 @@
/*
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 "boxes/abstract_box.h"
namespace Ui {
class FlatLabel;
class LinkButton;
class Checkbox;
class Radiobutton;
class RadiobuttonGroup;
} // namespace Ui
class CalendarBox;
class EditParticipantBox : public BoxContent {
public:
EditParticipantBox(QWidget*, not_null<ChannelData*> channel, not_null<UserData*> user, bool hasAdminRights);
protected:
void prepare() override;
void resizeToContent();
not_null<UserData*> user() const {
return _user;
}
not_null<ChannelData*> channel() const {
return _channel;
}
template <typename Widget>
QPointer<Widget> addControl(object_ptr<Widget> widget, QMargins margin);
void removeControl(QPointer<TWidget> widget);
bool hasAdminRights() const {
return _hasAdminRights;
}
private:
not_null<ChannelData*> _channel;
not_null<UserData*> _user;
bool _hasAdminRights = false;
class Inner;
QPointer<Inner> _inner;
};
class EditAdminBox : public EditParticipantBox {
public:
EditAdminBox(QWidget*, not_null<ChannelData*> channel, not_null<UserData*> user, const MTPChannelAdminRights &rights);
void setSaveCallback(Fn<void(MTPChannelAdminRights, MTPChannelAdminRights)> callback) {
_saveCallback = std::move(callback);
}
protected:
void prepare() override;
private:
using Flag = MTPDchannelAdminRights::Flag;
using Flags = MTPDchannelAdminRights::Flags;
static MTPChannelAdminRights DefaultRights(not_null<ChannelData*> channel);
bool canSave() const {
return !!_saveCallback;
}
void applyDependencies(QPointer<Ui::Checkbox> changed);
void refreshAboutAddAdminsText();
const MTPChannelAdminRights _oldRights;
std::vector<std::pair<Flag, Flag>> _dependencies;
Fn<void(MTPChannelAdminRights, MTPChannelAdminRights)> _saveCallback;
std::map<Flags, QPointer<Ui::Checkbox>> _checkboxes;
QPointer<Ui::FlatLabel> _aboutAddAdmins;
};
// Restricted box works with flags in the opposite way.
// If some flag is set in the rights then the checkbox is unchecked.
class EditRestrictedBox : public EditParticipantBox {
public:
EditRestrictedBox(QWidget*, not_null<ChannelData*> channel, not_null<UserData*> user, bool hasAdminRights, const MTPChannelBannedRights &rights);
void setSaveCallback(Fn<void(MTPChannelBannedRights, MTPChannelBannedRights)> callback) {
_saveCallback = std::move(callback);
}
protected:
void prepare() override;
private:
using Flag = MTPDchannelBannedRights::Flag;
using Flags = MTPDchannelBannedRights::Flags;
static MTPChannelBannedRights DefaultRights(not_null<ChannelData*> channel);
bool canSave() const {
return !!_saveCallback;
}
void applyDependencies(QPointer<Ui::Checkbox> changed);
void showRestrictUntil();
void setRestrictUntil(TimeId until);
bool isUntilForever() {
return ChannelData::IsRestrictedForever(_until);
}
void clearVariants();
void createUntilGroup();
void createUntilVariants();
TimeId getRealUntilValue() const;
const MTPChannelBannedRights _oldRights;
TimeId _until = 0;
std::vector<std::pair<Flag, Flag>> _dependencies;
Fn<void(MTPChannelBannedRights, MTPChannelBannedRights)> _saveCallback;
std::map<Flags, QPointer<Ui::Checkbox>> _checkboxes;
std::shared_ptr<Ui::RadiobuttonGroup> _untilGroup;
QVector<QPointer<Ui::Radiobutton>> _untilVariants;
QPointer<CalendarBox> _restrictUntilBox;
static constexpr auto kUntilOneDay = -1;
static constexpr auto kUntilOneWeek = -2;
static constexpr auto kUntilCustom = -3;
};

View File

@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "apiwrap.h"
#include "auth_session.h"
#include "data/data_user.h"
#include "styles/style_settings.h"
#include "styles/style_boxes.h"

View File

@@ -24,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/confirm_box.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "messenger.h"
#include "core/application.h"
#include "lang/lang_instance.h"
#include "lang/lang_cloud_manager.h"
#include "styles/style_boxes.h"
@@ -1132,7 +1132,7 @@ not_null<Ui::MultiSelect*> LanguageBox::createMultiSelect() {
base::binary_guard LanguageBox::Show() {
auto result = base::binary_guard();
const auto manager = Messenger::Instance().langCloudManager();
const auto manager = Core::App().langCloudManager();
if (manager->languageList().empty()) {
auto guard = std::make_shared<base::binary_guard>();
std::tie(result, *guard) = base::make_binary_guard();

View File

@@ -26,8 +26,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "observer_peer.h"
#include "storage/file_download.h"
#include "data/data_peer_values.h"
#include "data/data_chat.h"
#include "data/data_session.h"
#include "window/themes/window_theme.h"
auto PaintUserpicCallback(
not_null<PeerData*> peer,
bool respectSavedMessagesChat)
-> Fn<void(Painter &p, int x, int y, int outerWidth, int size)> {
if (respectSavedMessagesChat && peer->isSelf()) {
return [](Painter &p, int x, int y, int outerWidth, int size) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
};
}
return [=](Painter &p, int x, int y, int outerWidth, int size) {
peer->paintUserpicLeft(p, x, y, outerWidth, size);
};
}
PeerListBox::PeerListBox(
QWidget*,
std::unique_ptr<PeerListController> controller,
@@ -49,7 +65,7 @@ void PeerListBox::createMultiSelect() {
_select->entity()->setSubmittedCallback([this](Qt::KeyboardModifiers) { content()->submitted(); });
_select->entity()->setQueryChangedCallback([this](const QString &query) { searchQueryChanged(query); });
_select->entity()->setItemRemovedCallback([this](uint64 itemId) {
if (auto peer = App::peerLoaded(itemId)) {
if (auto peer = Auth().data().peerLoaded(itemId)) {
if (auto row = peerListFindRow(peer->id)) {
content()->changeCheckState(row, false, PeerListRow::SetStyle::Animated);
update();
@@ -303,13 +319,16 @@ int PeerListBox::peerListSelectedRowsCount() {
return _select ? _select->entity()->getItemsCount() : 0;
}
std::vector<not_null<PeerData*>> PeerListBox::peerListCollectSelectedRows() {
auto result = std::vector<not_null<PeerData*>> {};
auto items = _select ? _select->entity()->getItems() : QVector<uint64> {};
auto PeerListBox::peerListCollectSelectedRows()
-> std::vector<not_null<PeerData*>> {
auto result = std::vector<not_null<PeerData*>>();
auto items = _select
? _select->entity()->getItems()
: QVector<uint64>();
if (!items.empty()) {
result.reserve(items.size());
for_const (auto itemId, items) {
result.push_back(App::peer(itemId));
for (const auto itemId : items) {
result.push_back(Auth().data().peer(itemId));
}
}
return result;
@@ -746,6 +765,8 @@ void PeerListContent::clearAllContent() {
setSelected(Selected());
setPressed(Selected());
setContexted(Selected());
_mouseSelection = false;
_lastMousePosition = std::nullopt;
_rowsById.clear();
_rowsByPeer.clear();
_filterResults.clear();
@@ -836,7 +857,9 @@ void PeerListContent::refreshRows() {
if (_visibleBottom > 0) {
checkScrollForPreload();
}
updateSelection();
if (_mouseSelection) {
selectByMouse(QCursor::pos());
}
update();
}
@@ -943,29 +966,32 @@ void PeerListContent::enterEventHook(QEvent *e) {
}
void PeerListContent::leaveEventHook(QEvent *e) {
_mouseSelection = false;
setMouseTracking(false);
setSelected(Selected());
if (_mouseSelection) {
setSelected(Selected());
_mouseSelection = false;
_lastMousePosition = std::nullopt;
}
}
void PeerListContent::mouseMoveEvent(QMouseEvent *e) {
handleMouseMove(e->globalPos());
}
void PeerListContent::handleMouseMove(QPoint position) {
if (_mouseSelection || _lastMousePosition != position) {
_lastMousePosition = position;
_mouseSelection = true;
updateSelection();
void PeerListContent::handleMouseMove(QPoint globalPosition) {
if (!_lastMousePosition) {
_lastMousePosition = globalPosition;
return;
} else if (!_mouseSelection
&& *_lastMousePosition == globalPosition) {
return;
}
selectByMouse(globalPosition);
}
void PeerListContent::mousePressEvent(QMouseEvent *e) {
_pressButton = e->button();
_mouseSelection = true;
_lastMousePosition = e->globalPos();
updateSelection();
selectByMouse(e->globalPos());
setPressed(_selected);
if (auto row = getRow(_selected.index)) {
auto updateCallback = [this, row, hint = _selected.index] {
@@ -1157,6 +1183,7 @@ void PeerListContent::selectSkip(int direction) {
return;
}
_mouseSelection = false;
_lastMousePosition = std::nullopt;
auto newSelectedIndex = _selected.index.value + direction;
@@ -1305,7 +1332,6 @@ void PeerListContent::searchQueryChanged(QString query) {
_controller->search(_searchQuery);
}
refreshRows();
restoreSelection();
}
}
@@ -1357,6 +1383,8 @@ void PeerListContent::setSearchQuery(
setSelected(Selected());
setPressed(Selected());
setContexted(Selected());
_mouseSelection = false;
_lastMousePosition = std::nullopt;
_searchQuery = query;
_normalizedSearchQuery = normalizedQuery;
_mentionHighlight = _searchQuery.startsWith('@')
@@ -1403,8 +1431,9 @@ void PeerListContent::setContexted(Selected contexted) {
}
void PeerListContent::restoreSelection() {
_lastMousePosition = QCursor::pos();
updateSelection();
if (_mouseSelection) {
selectByMouse(QCursor::pos());
}
}
auto PeerListContent::saveSelectedData(Selected from)
@@ -1426,11 +1455,11 @@ auto PeerListContent::restoreSelectedData(SelectedSaved from)
return result;
}
void PeerListContent::updateSelection() {
if (!_mouseSelection) return;
auto point = mapFromGlobal(_lastMousePosition);
auto in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePosition));
void PeerListContent::selectByMouse(QPoint globalPosition) {
_mouseSelection = true;
_lastMousePosition = globalPosition;
const auto point = mapFromGlobal(globalPosition);
auto in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(globalPosition));
auto selected = Selected();
auto rowsPointY = point.y() - rowsTop();
selected.index.value = (in && rowsPointY >= 0 && rowsPointY < shownRowsCount() * _rowHeight) ? (rowsPointY / _rowHeight) : -1;

View File

@@ -34,19 +34,10 @@ namespace Notify {
struct PeerUpdate;
} // namespace Notify
inline auto PaintUserpicCallback(
auto PaintUserpicCallback(
not_null<PeerData*> peer,
bool respectSavedMessagesChat)
->Fn<void(Painter &p, int x, int y, int outerWidth, int size)> {
if (respectSavedMessagesChat && peer->isSelf()) {
return [](Painter &p, int x, int y, int outerWidth, int size) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
};
}
return [peer](Painter &p, int x, int y, int outerWidth, int size) {
peer->paintUserpicLeft(p, x, y, outerWidth, size);
};
}
-> Fn<void(Painter &p, int x, int y, int outerWidth, int size)>;
using PeerListRowId = uint64;
class PeerListRow {
@@ -312,6 +303,10 @@ public:
std::unique_ptr<SavedStateBase> state) {
}
rpl::lifetime &lifetime() {
return _lifetime;
}
protected:
not_null<PeerListSearchDelegate*> delegate() const {
return _delegate;
@@ -319,6 +314,7 @@ protected:
private:
PeerListSearchDelegate *_delegate = nullptr;
rpl::lifetime _lifetime;
};
@@ -329,7 +325,8 @@ public:
};
// Search works only with RowId == peer->id.
PeerListController(std::unique_ptr<PeerListSearchController> searchController = nullptr);
PeerListController(
std::unique_ptr<PeerListSearchController> searchController = {});
void setDelegate(not_null<PeerListDelegate*> delegate) {
_delegate = delegate;
@@ -553,7 +550,7 @@ private:
SelectedSaved saveSelectedData(Selected from);
Selected restoreSelectedData(SelectedSaved from);
void updateSelection();
void selectByMouse(QPoint globalPosition);
void loadProfilePhotos();
void checkScrollForPreload();
@@ -587,7 +584,7 @@ private:
void clearSearchRows();
void clearAllContent();
void handleMouseMove(QPoint position);
void handleMouseMove(QPoint globalPosition);
void mousePressReleased(Qt::MouseButton button);
const style::PeerList &_st;
@@ -602,6 +599,7 @@ private:
Selected _pressed;
Selected _contexted;
bool _mouseSelection = false;
std::optional<QPoint> _lastMousePosition;
Qt::MouseButton _pressButton = Qt::LeftButton;
rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
@@ -622,8 +620,6 @@ private:
object_ptr<Ui::FlatLabel> _searchNoResults = { nullptr };
object_ptr<Ui::FlatLabel> _searchLoading = { nullptr };
QPoint _lastMousePosition;
std::vector<std::unique_ptr<PeerListRow>> _searchRows;
base::Timer _repaintByStatus;
base::unique_qptr<Ui::PopupMenu> _contextMenu;

View File

@@ -7,43 +7,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/peer_list_controllers.h"
#include "styles/style_boxes.h"
#include "styles/style_profile.h"
#include "boxes/confirm_box.h"
#include "observer_peer.h"
#include "ui/widgets/checkbox.h"
#include "auth_session.h"
#include "data/data_session.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "apiwrap.h"
#include "mainwidget.h"
#include "lang/lang_keys.h"
#include "history/history.h"
#include "dialogs/dialogs_indexed_list.h"
#include "styles/style_boxes.h"
#include "styles/style_profile.h"
namespace {
base::flat_set<not_null<UserData*>> GetAlreadyInFromPeer(PeerData *peer) {
if (!peer) {
return {};
}
if (auto chat = peer->asChat()) {
auto participants = (
chat->participants
) | ranges::view::transform([](auto &&pair) -> not_null<UserData*> {
return pair.first;
});
return { participants.begin(), participants.end() };
} else if (auto channel = peer->asChannel()) {
if (channel->isMegagroup()) {
auto &participants = channel->mgInfo->lastParticipants;
return { participants.cbegin(), participants.cend() };
}
}
return {};
}
void ShareBotGame(not_null<UserData*> bot, not_null<PeerData*> chat) {
const auto history = App::historyLoaded(chat);
const auto history = chat->owner().historyLoaded(chat);
const auto randomId = rand_value<uint64>();
const auto requestId = MTP::send(
MTPmessages_SendMedia(
@@ -71,27 +54,10 @@ void ShareBotGame(not_null<UserData*> bot, not_null<PeerData*> chat) {
}
void AddBotToGroup(not_null<UserData*> bot, not_null<PeerData*> chat) {
if (auto &info = bot->botInfo) {
if (!info->startGroupToken.isEmpty()) {
MTP::send(
MTPmessages_StartBot(
bot->inputUser,
chat->input,
MTP_long(rand_value<uint64>()),
MTP_string(info->startGroupToken)),
App::main()->rpcDone(&MainWidget::sentUpdatesReceived),
App::main()->rpcFail(
&MainWidget::addParticipantFail,
{ bot, chat }));
} else {
App::main()->addParticipants(
chat,
{ 1, bot });
}
if (bot->botInfo && !bot->botInfo->startGroupToken.isEmpty()) {
Auth().api().sendBotStart(bot, chat);
} else {
App::main()->addParticipants(
chat,
{ 1, bot });
Auth().api().addChatParticipants(chat, { 1, bot });
}
Ui::hideLayer();
Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
@@ -127,44 +93,6 @@ void AddBotToGroup(not_null<UserData*> bot, not_null<PeerData*> chat) {
// return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition;
//}
class EditChatAdminsBoxController::LabeledCheckbox : public Ui::RpWidget {
public:
LabeledCheckbox(
QWidget *parent,
const QString &text,
bool checked = false,
const style::Checkbox &st = st::defaultCheckbox,
const style::Check &checkSt = st::defaultCheck);
bool checked() const {
return _checkbox->checked();
}
rpl::producer<bool> checkedChanges() const {
return _checkbox->checkedChanges();
}
rpl::producer<bool> checkedValue() const {
return _checkbox->checkedValue();
}
void setLabelText(
bool checked,
const style::TextStyle &st,
const QString &text,
const TextParseOptions &options = _defaultOptions,
int minResizeWidth = QFIXED_MAX);
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
object_ptr<Ui::Checkbox> _checkbox;
Text _labelUnchecked;
Text _labelChecked;
int _labelWidth = 0;
};
void PeerListRowWithLink::setActionLink(const QString &action) {
_action = action;
refreshActionLink();
@@ -254,8 +182,8 @@ void PeerListGlobalSearchController::searchDone(
auto &contacts = result.c_contacts_found();
auto query = _query;
if (requestId) {
App::feedUsers(contacts.vusers);
App::feedChats(contacts.vchats);
Auth().data().processUsers(contacts.vusers);
Auth().data().processChats(contacts.vchats);
auto it = _queries.find(requestId);
if (it != _queries.cend()) {
query = it->second;
@@ -265,7 +193,7 @@ void PeerListGlobalSearchController::searchDone(
}
const auto feedList = [&](const MTPVector<MTPPeer> &list) {
for (const auto &mtpPeer : list.v) {
if (const auto peer = App::peerLoaded(peerFromMTP(mtpPeer))) {
if (const auto peer = Auth().data().peerLoaded(peerFromMTP(mtpPeer))) {
delegate()->peerListSearchAddRow(peer);
}
}
@@ -327,7 +255,7 @@ void ChatsListBoxController::rebuildRows() {
};
auto added = 0;
if (respectSavedMessagesChat()) {
if (appendRow(App::history(Auth().user()))) {
if (appendRow(Auth().data().history(Auth().user()))) {
++added;
}
}
@@ -364,7 +292,7 @@ QString ChatsListBoxController::emptyBoxText() const {
}
std::unique_ptr<PeerListRow> ChatsListBoxController::createSearchRow(not_null<PeerData*> peer) {
return createRow(App::history(peer));
return createRow(peer->owner().history(peer));
}
bool ChatsListBoxController::appendRow(not_null<History*> history) {
@@ -379,7 +307,9 @@ bool ChatsListBoxController::appendRow(not_null<History*> history) {
return false;
}
ContactsBoxController::ContactsBoxController(std::unique_ptr<PeerListSearchController> searchController) : PeerListController(std::move(searchController)) {
ContactsBoxController::ContactsBoxController(
std::unique_ptr<PeerListSearchController> searchController)
: PeerListController(std::move(searchController)) {
}
void ContactsBoxController::prepare() {
@@ -454,344 +384,6 @@ std::unique_ptr<PeerListRow> ContactsBoxController::createRow(not_null<UserData*
return std::make_unique<PeerListRow>(user);
}
AddParticipantsBoxController::AddParticipantsBoxController(PeerData *peer)
: ContactsBoxController(std::make_unique<PeerListGlobalSearchController>())
, _peer(peer)
, _alreadyIn(GetAlreadyInFromPeer(peer)) {
}
AddParticipantsBoxController::AddParticipantsBoxController(
not_null<ChannelData*> channel,
base::flat_set<not_null<UserData*>> &&alreadyIn)
: ContactsBoxController(std::make_unique<PeerListGlobalSearchController>())
, _peer(channel)
, _alreadyIn(std::move(alreadyIn)) {
}
void AddParticipantsBoxController::rowClicked(not_null<PeerListRow*> row) {
auto count = fullCount();
auto limit = (_peer && _peer->isMegagroup()) ? Global::MegagroupSizeMax() : Global::ChatSizeMax();
if (count < limit || row->checked()) {
delegate()->peerListSetRowChecked(row, !row->checked());
updateTitle();
} else if (auto channel = _peer ? _peer->asChannel() : nullptr) {
if (!_peer->isMegagroup()) {
Ui::show(
Box<MaxInviteBox>(_peer->asChannel()),
LayerOption::KeepOther);
}
} else if (count >= Global::ChatSizeMax() && count < Global::MegagroupSizeMax()) {
Ui::show(
Box<InformBox>(lng_profile_add_more_after_upgrade(lt_count, Global::MegagroupSizeMax())),
LayerOption::KeepOther);
}
}
void AddParticipantsBoxController::itemDeselectedHook(not_null<PeerData*> peer) {
updateTitle();
}
void AddParticipantsBoxController::prepareViewHook() {
updateTitle();
}
int AddParticipantsBoxController::alreadyInCount() const {
if (!_peer) {
return 1; // self
}
if (auto chat = _peer->asChat()) {
return qMax(chat->count, 1);
} else if (auto channel = _peer->asChannel()) {
return qMax(channel->membersCount(), int(_alreadyIn.size()));
}
Unexpected("User in AddParticipantsBoxController::alreadyInCount");
}
bool AddParticipantsBoxController::isAlreadyIn(not_null<UserData*> user) const {
if (!_peer) {
return false;
}
if (auto chat = _peer->asChat()) {
return chat->participants.contains(user);
} else if (auto channel = _peer->asChannel()) {
return _alreadyIn.contains(user)
|| (channel->isMegagroup() && base::contains(channel->mgInfo->lastParticipants, user));
}
Unexpected("User in AddParticipantsBoxController::isAlreadyIn");
}
int AddParticipantsBoxController::fullCount() const {
return alreadyInCount() + delegate()->peerListSelectedRowsCount();
}
std::unique_ptr<PeerListRow> AddParticipantsBoxController::createRow(not_null<UserData*> user) {
if (user->isSelf()) {
return nullptr;
}
auto result = std::make_unique<PeerListRow>(user);
if (isAlreadyIn(user)) {
result->setDisabledState(PeerListRow::State::DisabledChecked);
}
return result;
}
void AddParticipantsBoxController::updateTitle() {
auto additional = (_peer && _peer->isChannel() && !_peer->isMegagroup())
? QString() :
QString("%1 / %2").arg(fullCount()).arg(Global::MegagroupSizeMax());
delegate()->peerListSetTitle(langFactory(lng_profile_add_participant));
delegate()->peerListSetAdditionalTitle([additional] { return additional; });
}
void AddParticipantsBoxController::Start(not_null<ChatData*> chat) {
auto initBox = [chat](not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_participant_invite), [box, chat] {
auto rows = box->peerListCollectSelectedRows();
if (!rows.empty()) {
auto users = std::vector<not_null<UserData*>>();
for (auto peer : rows) {
auto user = peer->asUser();
Assert(user != nullptr);
Assert(!user->isSelf());
users.push_back(peer->asUser());
}
App::main()->addParticipants(chat, users);
Ui::showPeerHistory(chat, ShowAtTheEndMsgId);
}
});
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
};
Ui::show(Box<PeerListBox>(std::make_unique<AddParticipantsBoxController>(chat), std::move(initBox)));
}
void AddParticipantsBoxController::Start(
not_null<ChannelData*> channel,
base::flat_set<not_null<UserData*>> &&alreadyIn,
bool justCreated) {
auto initBox = [channel, justCreated](not_null<PeerListBox*> box) {
auto subscription = std::make_shared<rpl::lifetime>();
box->addButton(langFactory(lng_participant_invite), [box, channel, subscription] {
auto rows = box->peerListCollectSelectedRows();
if (!rows.empty()) {
auto users = std::vector<not_null<UserData*>>();
for (auto peer : rows) {
auto user = peer->asUser();
Assert(user != nullptr);
Assert(!user->isSelf());
users.push_back(peer->asUser());
}
App::main()->addParticipants(channel, users);
if (channel->isMegagroup()) {
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
} else {
box->closeBox();
}
}
});
box->addButton(langFactory(justCreated ? lng_create_group_skip : lng_cancel), [box] { box->closeBox(); });
if (justCreated) {
box->boxClosing() | rpl::start_with_next([=] {
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
}, *subscription);
}
};
Ui::show(Box<PeerListBox>(std::make_unique<AddParticipantsBoxController>(channel, std::move(alreadyIn)), std::move(initBox)));
}
void AddParticipantsBoxController::Start(
not_null<ChannelData*> channel,
base::flat_set<not_null<UserData*>> &&alreadyIn) {
Start(channel, std::move(alreadyIn), false);
}
void AddParticipantsBoxController::Start(not_null<ChannelData*> channel) {
Start(channel, {}, true);
}
EditChatAdminsBoxController::LabeledCheckbox::LabeledCheckbox(
QWidget *parent,
const QString &text,
bool checked,
const style::Checkbox &st,
const style::Check &checkSt)
: RpWidget(parent)
, _checkbox(this, text, checked, st, checkSt) {
}
void EditChatAdminsBoxController::LabeledCheckbox::setLabelText(
bool checked,
const style::TextStyle &st,
const QString &text,
const TextParseOptions &options,
int minResizeWidth) {
auto &label = (checked ? _labelChecked : _labelUnchecked);
label = Text(st, text, options, minResizeWidth);
}
int EditChatAdminsBoxController::LabeledCheckbox::resizeGetHeight(int newWidth) {
_labelWidth = newWidth - st::contactsPadding.left() - st::contactsPadding.right();
_checkbox->resizeToNaturalWidth(_labelWidth);
_checkbox->moveToLeft(st::contactsPadding.left(), st::contactsAllAdminsTop);
auto labelHeight = qMax(
_labelChecked.countHeight(_labelWidth),
_labelUnchecked.countHeight(_labelWidth));
return st::contactsAboutTop + labelHeight + st::contactsAboutBottom;
}
void EditChatAdminsBoxController::LabeledCheckbox::paintEvent(QPaintEvent *e) {
Painter p(this);
auto infoTop = _checkbox->bottomNoMargins() + st::contactsAllAdminsTop - st::lineWidth;
auto infoRect = rtlrect(0, infoTop, width(), height() - infoTop - st::contactsPadding.bottom(), width());
p.fillRect(infoRect, st::contactsAboutBg);
auto dividerFillTop = rtlrect(0, infoRect.y(), width(), st::profileDividerTop.height(), width());
st::profileDividerTop.fill(p, dividerFillTop);
auto dividerFillBottom = rtlrect(0, infoRect.y() + infoRect.height() - st::profileDividerBottom.height(), width(), st::profileDividerBottom.height(), width());
st::profileDividerBottom.fill(p, dividerFillBottom);
p.setPen(st::contactsAboutFg);
(checked() ? _labelChecked : _labelUnchecked).draw(p, st::contactsPadding.left(), st::contactsAboutTop, _labelWidth);
}
EditChatAdminsBoxController::EditChatAdminsBoxController(not_null<ChatData*> chat)
: PeerListController()
, _chat(chat) {
}
bool EditChatAdminsBoxController::allAreAdmins() const {
return _allAdmins->checked();
}
void EditChatAdminsBoxController::prepare() {
createAllAdminsCheckbox();
setSearchNoResultsText(lang(lng_blocked_list_not_found));
delegate()->peerListSetSearchMode(allAreAdmins() ? PeerListSearchMode::Disabled : PeerListSearchMode::Enabled);
delegate()->peerListSetTitle(langFactory(lng_channel_admins));
rebuildRows();
if (!delegate()->peerListFullRowsCount()) {
Auth().api().requestFullPeer(_chat);
_adminsUpdatedSubscription = subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(
Notify::PeerUpdate::Flag::AdminsChanged, [this](
const Notify::PeerUpdate &update) {
if (update.peer == _chat) {
rebuildRows();
if (delegate()->peerListFullRowsCount()) {
unsubscribe(_adminsUpdatedSubscription);
}
}
}));
}
_allAdmins->checkedChanges(
) | rpl::start_with_next([=](bool checked) {
delegate()->peerListSetSearchMode(checked ? PeerListSearchMode::Disabled : PeerListSearchMode::Enabled);
for (auto i = 0, count = delegate()->peerListFullRowsCount(); i != count; ++i) {
auto row = delegate()->peerListRowAt(i);
auto user = row->peer()->asUser();
if (checked || user->id == peerFromUser(_chat->creator)) {
row->setDisabledState(PeerListRow::State::DisabledChecked);
} else {
row->setDisabledState(PeerListRow::State::Active);
}
}
}, _allAdmins->lifetime());
}
void EditChatAdminsBoxController::createAllAdminsCheckbox() {
auto labelWidth = st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.right();
auto checkbox = object_ptr<LabeledCheckbox>(nullptr, lang(lng_chat_all_members_admins), !_chat->adminsEnabled(), st::defaultBoxCheckbox);
checkbox->setLabelText(true, st::defaultTextStyle, lang(lng_chat_about_all_admins), _defaultOptions, labelWidth);
checkbox->setLabelText(false, st::defaultTextStyle, lang(lng_chat_about_admins), _defaultOptions, labelWidth);
_allAdmins = checkbox;
delegate()->peerListSetAboveWidget(std::move(checkbox));
}
void EditChatAdminsBoxController::rebuildRows() {
if (_chat->participants.empty()) {
return;
}
auto allAdmins = allAreAdmins();
auto admins = std::vector<not_null<UserData*>>();
auto others = admins;
admins.reserve(allAdmins ? _chat->participants.size() : _chat->admins.size());
others.reserve(_chat->participants.size());
for (const auto [user, version] : _chat->participants) {
if (user->id == peerFromUser(_chat->creator)) continue;
if (_chat->admins.contains(user)) {
admins.push_back(user);
} else {
others.push_back(user);
}
}
if (!admins.empty()) {
delegate()->peerListAddSelectedRows(admins);
}
if (allAdmins) {
admins.insert(admins.end(), others.begin(), others.end());
others.clear();
}
auto sortByName = [](not_null<UserData*> a, auto b) {
return (a->name.compare(b->name, Qt::CaseInsensitive) < 0);
};
ranges::sort(admins, sortByName);
ranges::sort(others, sortByName);
auto addOne = [this](not_null<UserData*> user) {
if (auto row = createRow(user)) {
delegate()->peerListAppendRow(std::move(row));
}
};
if (auto creator = App::userLoaded(_chat->creator)) {
if (_chat->participants.contains(creator)) {
addOne(creator);
}
}
ranges::for_each(admins, addOne);
ranges::for_each(others, addOne);
delegate()->peerListRefreshRows();
}
std::unique_ptr<PeerListRow> EditChatAdminsBoxController::createRow(not_null<UserData*> user) {
auto result = std::make_unique<PeerListRow>(user);
if (allAreAdmins() || user->id == peerFromUser(_chat->creator)) {
result->setDisabledState(PeerListRow::State::DisabledChecked);
}
return result;
}
void EditChatAdminsBoxController::rowClicked(not_null<PeerListRow*> row) {
delegate()->peerListSetRowChecked(row, !row->checked());
}
void EditChatAdminsBoxController::Start(not_null<ChatData*> chat) {
auto controller = std::make_unique<EditChatAdminsBoxController>(chat);
auto initBox = [chat, controller = controller.get()](not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_settings_save), [box, chat, controller] {
auto rows = box->peerListCollectSelectedRows();
auto users = std::vector<not_null<UserData*>>();
for (auto peer : rows) {
auto user = peer->asUser();
Assert(user != nullptr);
Assert(!user->isSelf());
users.push_back(peer->asUser());
}
Auth().api().editChatAdmins(chat, !controller->allAreAdmins(), { users.cbegin(), users.cend() });
box->closeBox();
});
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
};
Ui::show(
Box<PeerListBox>(std::move(controller), std::move(initBox)),
LayerOption::KeepOther);
}
void AddBotToGroupBoxController::Start(not_null<UserData*> bot) {
auto initBox = [=](not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
@@ -830,7 +422,7 @@ void AddBotToGroupBoxController::shareBotGame(not_null<PeerData*> chat) {
}
void AddBotToGroupBoxController::addBotToGroup(not_null<PeerData*> chat) {
if (auto megagroup = chat->asMegagroup()) {
if (const auto megagroup = chat->asMegagroup()) {
if (!megagroup->canAddMembers()) {
Ui::show(
Box<InformBox>(lang(lng_error_cant_add_member)),
@@ -847,30 +439,26 @@ void AddBotToGroupBoxController::addBotToGroup(not_null<PeerData*> chat) {
LayerOption::KeepOther);
}
std::unique_ptr<ChatsListBoxController::Row> AddBotToGroupBoxController::createRow(not_null<History*> history) {
auto AddBotToGroupBoxController::createRow(not_null<History*> history)
-> std::unique_ptr<ChatsListBoxController::Row> {
if (!needToCreateRow(history->peer)) {
return nullptr;
}
return std::make_unique<Row>(history);
}
bool AddBotToGroupBoxController::needToCreateRow(not_null<PeerData*> peer) const {
bool AddBotToGroupBoxController::needToCreateRow(
not_null<PeerData*> peer) const {
if (sharingBotGame()) {
if (!peer->canWrite()) {
if (!peer->canWrite()
|| peer->amRestricted(ChatRestriction::f_send_games)) {
return false;
}
if (auto group = peer->asMegagroup()) {
if (group->restricted(ChannelRestriction::f_send_games)) {
return false;
}
}
return true;
}
if (auto chat = peer->asChat()) {
if (chat->canEdit()) {
return true;
}
} else if (auto group = peer->asMegagroup()) {
if (const auto chat = peer->asChat()) {
return chat->canAddMembers();
} else if (const auto group = peer->asMegagroup()) {
return group->canAddMembers();
}
return false;

View File

@@ -114,9 +114,13 @@ private:
};
class ContactsBoxController : public PeerListController, protected base::Subscriber {
class ContactsBoxController
: public PeerListController
, protected base::Subscriber {
public:
ContactsBoxController(std::unique_ptr<PeerListSearchController> searchController = std::make_unique<PeerListGlobalSearchController>());
ContactsBoxController(
std::unique_ptr<PeerListSearchController> searchController
= std::make_unique<PeerListGlobalSearchController>());
void prepare() override final;
std::unique_ptr<PeerListRow> createSearchRow(not_null<PeerData*> peer) override final;
@@ -136,68 +140,6 @@ private:
};
class EditChatAdminsBoxController : public PeerListController, private base::Subscriber {
public:
static void Start(not_null<ChatData*> chat);
EditChatAdminsBoxController(not_null<ChatData*> chat);
bool allAreAdmins() const;
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
private:
void createAllAdminsCheckbox();
void rebuildRows();
std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user);
not_null<ChatData*> _chat;
int _adminsUpdatedSubscription = 0;
class LabeledCheckbox;
QPointer<LabeledCheckbox> _allAdmins;
};
class AddParticipantsBoxController : public ContactsBoxController {
public:
static void Start(not_null<ChatData*> chat);
static void Start(not_null<ChannelData*> channel);
static void Start(
not_null<ChannelData*> channel,
base::flat_set<not_null<UserData*>> &&alreadyIn);
AddParticipantsBoxController(PeerData *peer);
AddParticipantsBoxController(
not_null<ChannelData*> channel,
base::flat_set<not_null<UserData*>> &&alreadyIn);
using ContactsBoxController::ContactsBoxController;
void rowClicked(not_null<PeerListRow*> row) override;
void itemDeselectedHook(not_null<PeerData*> peer) override;
protected:
void prepareViewHook() override;
std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user) override;
private:
static void Start(
not_null<ChannelData*> channel,
base::flat_set<not_null<UserData*>> &&alreadyIn,
bool justCreated);
int alreadyInCount() const;
bool isAlreadyIn(not_null<UserData*> user) const;
int fullCount() const;
void updateTitle();
PeerData *_peer = nullptr;
base::flat_set<not_null<UserData*>> _alreadyIn;
};
class AddBotToGroupBoxController
: public ChatsListBoxController
, public base::has_weak_ptr {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,177 @@
/*
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 "boxes/peer_list_controllers.h"
#include "boxes/peers/edit_participants_box.h"
class AddParticipantsBoxController : public ContactsBoxController {
public:
static void Start(not_null<ChatData*> chat);
static void Start(not_null<ChannelData*> channel);
static void Start(
not_null<ChannelData*> channel,
base::flat_set<not_null<UserData*>> &&alreadyIn);
AddParticipantsBoxController();
AddParticipantsBoxController(not_null<PeerData*> peer);
AddParticipantsBoxController(
not_null<PeerData*> peer,
base::flat_set<not_null<UserData*>> &&alreadyIn);
void rowClicked(not_null<PeerListRow*> row) override;
void itemDeselectedHook(not_null<PeerData*> peer) override;
protected:
void prepareViewHook() override;
std::unique_ptr<PeerListRow> createRow(
not_null<UserData*> user) override;
private:
static void Start(
not_null<ChannelData*> channel,
base::flat_set<not_null<UserData*>> &&alreadyIn,
bool justCreated);
bool inviteSelectedUsers(not_null<PeerListBox*> box) const;
void subscribeToMigration();
int alreadyInCount() const;
bool isAlreadyIn(not_null<UserData*> user) const;
int fullCount() const;
void updateTitle();
PeerData *_peer = nullptr;
base::flat_set<not_null<UserData*>> _alreadyIn;
};
// Adding an admin, banned or restricted user from channel members
// with search + contacts search + global search.
class AddSpecialBoxController
: public PeerListController
, private base::Subscriber
, private MTP::Sender
, public base::has_weak_ptr {
public:
using Role = ParticipantsBoxController::Role;
using AdminDoneCallback = Fn<void(
not_null<UserData*> user,
const MTPChatAdminRights &adminRights)>;
using BannedDoneCallback = Fn<void(
not_null<UserData*> user,
const MTPChatBannedRights &bannedRights)>;
AddSpecialBoxController(
not_null<PeerData*> peer,
Role role,
AdminDoneCallback adminDoneCallback,
BannedDoneCallback bannedDoneCallback);
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
void loadMoreRows() override;
std::unique_ptr<PeerListRow> createSearchRow(
not_null<PeerData*> peer) override;
private:
template <typename Callback>
bool checkInfoLoaded(not_null<UserData*> user, Callback callback);
void prepareChatRows(not_null<ChatData*> chat);
void rebuildChatRows(not_null<ChatData*> chat);
void showAdmin(not_null<UserData*> user, bool sure = false);
void editAdminDone(
not_null<UserData*> user,
const MTPChatAdminRights &rights);
void showRestricted(not_null<UserData*> user, bool sure = false);
void editRestrictedDone(
not_null<UserData*> user,
const MTPChatBannedRights &rights);
void kickUser(not_null<UserData*> user, bool sure = false);
bool appendRow(not_null<UserData*> user);
bool prependRow(not_null<UserData*> user);
std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user) const;
void subscribeToMigration();
void migrate(not_null<ChannelData*> channel);
not_null<PeerData*> _peer;
Role _role = Role::Admins;
int _offset = 0;
mtpRequestId _loadRequestId = 0;
bool _allLoaded = false;
ParticipantsAdditionalData _additional;
std::unique_ptr<ParticipantsOnlineSorter> _onlineSorter;
BoxPointer _editBox;
AdminDoneCallback _adminDoneCallback;
BannedDoneCallback _bannedDoneCallback;
};
// Finds chat/channel members, then contacts, then global search results.
class AddSpecialBoxSearchController
: public PeerListSearchController
, private MTP::Sender
, private base::Subscriber {
public:
using Role = ParticipantsBoxController::Role;
AddSpecialBoxSearchController(
not_null<PeerData*> peer,
not_null<ParticipantsAdditionalData*> additional);
void searchQuery(const QString &query) override;
bool isLoading() override;
bool loadMoreRows() override;
private:
struct CacheEntry {
MTPchannels_ChannelParticipants result;
int requestedCount = 0;
};
struct Query {
QString text;
int offset = 0;
};
void searchOnServer();
bool searchParticipantsInCache();
void searchParticipantsDone(
mtpRequestId requestId,
const MTPchannels_ChannelParticipants &result,
int requestedCount);
bool searchGlobalInCache();
void searchGlobalDone(
mtpRequestId requestId,
const MTPcontacts_Found &result);
void requestParticipants();
void addChatMembers(not_null<ChatData*> chat);
void addChatsContacts();
void requestGlobal();
void subscribeToMigration();
not_null<PeerData*> _peer;
not_null<ParticipantsAdditionalData*> _additional;
base::Timer _timer;
QString _query;
mtpRequestId _requestId = 0;
int _offset = 0;
bool _participantsLoaded = false;
bool _chatsContactsAdded = false;
bool _chatMembersAdded = false;
bool _globalLoaded = false;
std::map<QString, CacheEntry> _participantsCache;
std::map<mtpRequestId, Query> _participantsQueries;
std::map<QString, MTPcontacts_Found> _globalCache;
std::map<mtpRequestId, QString> _globalQueries;
};

View File

@@ -0,0 +1,510 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/peers/edit_participant_box.h"
#include "lang/lang_keys.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/text_options.h"
#include "ui/special_buttons.h"
#include "boxes/calendar_box.h"
#include "boxes/peers/edit_peer_permissions_box.h"
#include "data/data_peer_values.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "styles/style_boxes.h"
namespace {
constexpr auto kMaxRestrictDelayDays = 366;
constexpr auto kSecondsInDay = 24 * 60 * 60;
constexpr auto kSecondsInWeek = 7 * kSecondsInDay;
} // namespace
class EditParticipantBox::Inner : public Ui::RpWidget {
public:
Inner(
QWidget *parent,
not_null<PeerData*> peer,
not_null<UserData*> user,
bool hasAdminRights);
template <typename Widget>
Widget *addControl(object_ptr<Widget> widget, QMargins margin);
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
not_null<PeerData*> _peer;
not_null<UserData*> _user;
object_ptr<Ui::UserpicButton> _userPhoto;
Text _userName;
bool _hasAdminRights = false;
object_ptr<Ui::VerticalLayout> _rows;
};
EditParticipantBox::Inner::Inner(
QWidget *parent,
not_null<PeerData*> peer,
not_null<UserData*> user,
bool hasAdminRights)
: RpWidget(parent)
, _peer(peer)
, _user(user)
, _userPhoto(
this,
_user,
Ui::UserpicButton::Role::Custom,
st::rightsPhotoButton)
, _hasAdminRights(hasAdminRights)
, _rows(this) {
_rows->heightValue(
) | rpl::start_with_next([=] {
resizeToWidth(width());
}, lifetime());
_userPhoto->setPointerCursor(false);
_userName.setText(
st::rightsNameStyle,
App::peerName(_user),
Ui::NameTextOptions());
}
template <typename Widget>
Widget *EditParticipantBox::Inner::addControl(
object_ptr<Widget> widget,
QMargins margin) {
return _rows->add(std::move(widget), margin);
}
int EditParticipantBox::Inner::resizeGetHeight(int newWidth) {
_userPhoto->moveToLeft(
st::rightsPhotoMargin.left(),
st::rightsPhotoMargin.top());
const auto rowsTop = st::rightsPhotoMargin.top()
+ st::rightsPhotoButton.size.height()
+ st::rightsPhotoMargin.bottom();
_rows->resizeToWidth(newWidth);
_rows->moveToLeft(0, rowsTop, newWidth);
return rowsTop + _rows->heightNoMargins();
}
void EditParticipantBox::Inner::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(e->rect(), st::boxBg);
p.setPen(st::contactsNameFg);
auto namex = st::rightsPhotoMargin.left()
+ st::rightsPhotoButton.size .width()
+ st::rightsPhotoMargin.right();
auto namew = width() - namex - st::rightsPhotoMargin.right();
_userName.drawLeftElided(
p,
namex,
st::rightsPhotoMargin.top() + st::rightsNameTop,
namew,
width());
auto statusText = [this] {
if (_user->botInfo) {
const auto seesAllMessages = _user->botInfo->readsAllHistory
|| _hasAdminRights;
return lang(seesAllMessages
? lng_status_bot_reads_all
: lng_status_bot_not_reads_all);
}
return Data::OnlineText(_user->onlineTill, unixtime());
};
p.setFont(st::contactsStatusFont);
p.setPen(st::contactsStatusFg);
p.drawTextLeft(
namex,
st::rightsPhotoMargin.top() + st::rightsStatusTop,
width(),
statusText());
}
EditParticipantBox::EditParticipantBox(
QWidget*,
not_null<PeerData*> peer,
not_null<UserData*> user,
bool hasAdminRights)
: _peer(peer)
, _user(user)
, _hasAdminRights(hasAdminRights) {
}
void EditParticipantBox::prepare() {
_inner = setInnerWidget(object_ptr<Inner>(
this,
_peer,
_user,
hasAdminRights()));
setDimensionsToContent(st::boxWideWidth, _inner);
}
template <typename Widget>
Widget *EditParticipantBox::addControl(
object_ptr<Widget> widget,
QMargins margin) {
Expects(_inner != nullptr);
return _inner->addControl(std::move(widget), margin);
}
EditAdminBox::EditAdminBox(
QWidget*,
not_null<PeerData*> peer,
not_null<UserData*> user,
const MTPChatAdminRights &rights)
: EditParticipantBox(
nullptr,
peer,
user,
(rights.c_chatAdminRights().vflags.v != 0))
, _oldRights(rights) {
}
MTPChatAdminRights EditAdminBox::Defaults(not_null<PeerData*> peer) {
const auto defaultRights = peer->isChat()
? ChatData::DefaultAdminRights()
: peer->isMegagroup()
? (Flag::f_change_info
| Flag::f_delete_messages
| Flag::f_ban_users
| Flag::f_invite_users
| Flag::f_pin_messages)
: (Flag::f_change_info
| Flag::f_post_messages
| Flag::f_edit_messages
| Flag::f_delete_messages
| Flag::f_invite_users);
return MTP_chatAdminRights(MTP_flags(defaultRights));
}
void EditAdminBox::prepare() {
using namespace rpl::mappers;
EditParticipantBox::prepare();
auto hadRights = _oldRights.c_chatAdminRights().vflags.v;
setTitle(langFactory(hadRights
? lng_rights_edit_admin
: lng_channel_add_admin));
addControl(
object_ptr<BoxContentDivider>(this),
st::rightsDividerMargin);
const auto chat = peer()->asChat();
const auto channel = peer()->asChannel();
const auto prepareRights = hadRights ? _oldRights : Defaults(peer());
const auto disabledByDefaults = (channel && !channel->isMegagroup())
? MTPDchatAdminRights::Flags(0)
: DisabledByDefaultRestrictions(peer());
const auto filterByMyRights = canSave()
&& !hadRights
&& channel
&& !channel->amCreator();
const auto prepareFlags = disabledByDefaults
| (prepareRights.c_chatAdminRights().vflags.v
& (filterByMyRights ? channel->adminRights() : ~Flag(0)));
const auto disabledMessages = [&] {
auto result = std::map<Flags, QString>();
if (!canSave()) {
result.emplace(
~Flags(0),
lang(lng_rights_about_admin_cant_edit));
} else {
result.emplace(
disabledByDefaults,
lang(lng_rights_permission_for_all));
if (const auto channel = peer()->asChannel()) {
if (!channel->amCreator()) {
result.emplace(
~channel->adminRights(),
lang(lng_rights_permission_cant_edit));
}
}
}
return result;
}();
const auto anyoneCanAddMembers = chat
? chat->anyoneCanAddMembers()
: channel->anyoneCanAddMembers();
auto [checkboxes, getChecked, changes] = CreateEditAdminRights(
this,
lng_rights_edit_admin_header,
prepareFlags,
disabledMessages,
peer()->isChat() || peer()->isMegagroup(),
anyoneCanAddMembers);
addControl(std::move(checkboxes), QMargins());
_aboutAddAdmins = addControl(
object_ptr<Ui::FlatLabel>(this, st::boxLabel),
st::rightsAboutMargin);
rpl::single(
getChecked()
) | rpl::then(std::move(
changes
)) | rpl::map(
(_1 & Flag::f_add_admins) != 0
) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](bool checked) {
refreshAboutAddAdminsText(checked);
}, lifetime());
if (canSave()) {
addButton(langFactory(lng_settings_save), [=, value = getChecked] {
if (!_saveCallback) {
return;
}
const auto newFlags = value()
& ((!channel || channel->amCreator())
? ~Flags(0)
: channel->adminRights());
_saveCallback(
_oldRights,
MTP_chatAdminRights(MTP_flags(newFlags)));
});
addButton(langFactory(lng_cancel), [this] { closeBox(); });
} else {
addButton(langFactory(lng_box_ok), [this] { closeBox(); });
}
}
void EditAdminBox::refreshAboutAddAdminsText(bool canAddAdmins) {
_aboutAddAdmins->setText([&] {
if (!canSave()) {
return lang(lng_rights_about_admin_cant_edit);
} else if (canAddAdmins) {
return lang(lng_rights_about_add_admins_yes);
}
return lang(lng_rights_about_add_admins_no);
}());
}
EditRestrictedBox::EditRestrictedBox(
QWidget*,
not_null<PeerData*> peer,
not_null<UserData*> user,
bool hasAdminRights,
const MTPChatBannedRights &rights)
: EditParticipantBox(nullptr, peer, user, hasAdminRights)
, _oldRights(rights) {
}
void EditRestrictedBox::prepare() {
EditParticipantBox::prepare();
setTitle(langFactory(lng_rights_user_restrictions));
addControl(
object_ptr<BoxContentDivider>(this),
st::rightsDividerMargin);
const auto chat = peer()->asChat();
const auto channel = peer()->asChannel();
const auto defaultRestrictions = chat
? chat->defaultRestrictions()
: channel->defaultRestrictions();
const auto prepareRights = (_oldRights.c_chatBannedRights().vflags.v
? _oldRights
: Defaults(peer()));
const auto prepareFlags = prepareRights.c_chatBannedRights().vflags.v
| defaultRestrictions
| ((channel && channel->isPublic())
? (Flag::f_change_info | Flag::f_pin_messages)
: Flags(0));
const auto disabledMessages = [&] {
auto result = std::map<Flags, QString>();
if (!canSave()) {
result.emplace(
~Flags(0),
lang(lng_rights_about_restriction_cant_edit));
} else {
const auto disabled = defaultRestrictions
| ((channel && channel->isPublic())
? (Flag::f_change_info | Flag::f_pin_messages)
: Flags(0));
result.emplace(
disabled,
lang(lng_rights_restriction_for_all));
}
return result;
}();
auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
this,
lng_rights_user_restrictions_header,
prepareFlags,
disabledMessages);
addControl(std::move(checkboxes), QMargins());
_until = prepareRights.c_chatBannedRights().vuntil_date.v;
addControl(object_ptr<BoxContentDivider>(this), st::rightsUntilMargin);
addControl(
object_ptr<Ui::FlatLabel>(
this,
lang(lng_rights_chat_banned_until_header),
Ui::FlatLabel::InitType::Simple,
st::rightsHeaderLabel),
st::rightsHeaderMargin);
setRestrictUntil(_until);
//addControl(
// object_ptr<Ui::LinkButton>(
// this,
// lang(lng_rights_chat_banned_block),
// st::boxLinkButton));
if (canSave()) {
const auto save = [=, value = getRestrictions] {
if (!_saveCallback) {
return;
}
_saveCallback(
_oldRights,
MTP_chatBannedRights(
MTP_flags(value()),
MTP_int(getRealUntilValue())));
};
addButton(langFactory(lng_settings_save), save);
addButton(langFactory(lng_cancel), [=] { closeBox(); });
} else {
addButton(langFactory(lng_box_ok), [=] { closeBox(); });
}
}
MTPChatBannedRights EditRestrictedBox::Defaults(not_null<PeerData*> peer) {
return MTP_chatBannedRights(MTP_flags(0), MTP_int(0));
}
void EditRestrictedBox::showRestrictUntil() {
auto tomorrow = QDate::currentDate().addDays(1);
auto highlighted = isUntilForever()
? tomorrow
: ParseDateTime(getRealUntilValue()).date();
auto month = highlighted;
_restrictUntilBox = Ui::show(
Box<CalendarBox>(
month,
highlighted,
[this](const QDate &date) {
setRestrictUntil(
static_cast<int>(QDateTime(date).toTime_t()));
}),
LayerOption::KeepOther);
_restrictUntilBox->setMaxDate(
QDate::currentDate().addDays(kMaxRestrictDelayDays));
_restrictUntilBox->setMinDate(tomorrow);
_restrictUntilBox->addLeftButton(
langFactory(lng_rights_chat_banned_forever),
[=] { setRestrictUntil(0); });
}
void EditRestrictedBox::setRestrictUntil(TimeId until) {
_until = until;
if (_restrictUntilBox) {
_restrictUntilBox->closeBox();
}
_untilVariants.clear();
createUntilGroup();
createUntilVariants();
}
bool EditRestrictedBox::isUntilForever() const {
return ChannelData::IsRestrictedForever(_until);
}
void EditRestrictedBox::createUntilGroup() {
_untilGroup = std::make_shared<Ui::RadiobuttonGroup>(
isUntilForever() ? 0 : _until);
_untilGroup->setChangedCallback([this](int value) {
if (value == kUntilCustom) {
_untilGroup->setValue(_until);
showRestrictUntil();
} else if (_until != value) {
_until = value;
}
});
}
void EditRestrictedBox::createUntilVariants() {
auto addVariant = [&](int value, const QString &text) {
if (!canSave() && _untilGroup->value() != value) {
return;
}
_untilVariants.emplace_back(
addControl(
object_ptr<Ui::Radiobutton>(
this,
_untilGroup,
value,
text,
st::defaultBoxCheckbox),
st::rightsToggleMargin));
if (!canSave()) {
_untilVariants.back()->setDisabled(true);
}
};
auto addCustomVariant = [&](TimeId until, TimeId from, TimeId to) {
if (!ChannelData::IsRestrictedForever(until)
&& until > from
&& until <= to) {
addVariant(
until,
lng_rights_chat_banned_custom_date(
lt_date,
langDayOfMonthFull(ParseDateTime(until).date())));
}
};
auto addCurrentVariant = [&](TimeId from, TimeId to) {
auto oldUntil = _oldRights.c_chatBannedRights().vuntil_date.v;
if (oldUntil < _until) {
addCustomVariant(oldUntil, from, to);
}
addCustomVariant(_until, from, to);
if (oldUntil > _until) {
addCustomVariant(oldUntil, from, to);
}
};
addVariant(0, lang(lng_rights_chat_banned_forever));
auto now = unixtime();
auto nextDay = now + kSecondsInDay;
auto nextWeek = now + kSecondsInWeek;
addCurrentVariant(0, nextDay);
addVariant(kUntilOneDay, lng_rights_chat_banned_day(lt_count, 1));
addCurrentVariant(nextDay, nextWeek);
addVariant(kUntilOneWeek, lng_rights_chat_banned_week(lt_count, 1));
addCurrentVariant(nextWeek, INT_MAX);
addVariant(kUntilCustom, lang(lng_rights_chat_banned_custom));
}
TimeId EditRestrictedBox::getRealUntilValue() const {
Expects(_until != kUntilCustom);
if (_until == kUntilOneDay) {
return unixtime() + kSecondsInDay;
} else if (_until == kUntilOneWeek) {
return unixtime() + kSecondsInWeek;
}
Assert(_until >= 0);
return _until;
}

View File

@@ -0,0 +1,140 @@
/*
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 "boxes/abstract_box.h"
#include "base/unique_qptr.h"
namespace Ui {
class FlatLabel;
class LinkButton;
class Checkbox;
class Radiobutton;
class RadiobuttonGroup;
} // namespace Ui
class CalendarBox;
class EditParticipantBox : public BoxContent {
public:
EditParticipantBox(
QWidget*,
not_null<PeerData*> peer,
not_null<UserData*> user,
bool hasAdminRights);
protected:
void prepare() override;
not_null<UserData*> user() const {
return _user;
}
not_null<PeerData*> peer() const {
return _peer;
}
template <typename Widget>
Widget *addControl(object_ptr<Widget> widget, QMargins margin = {});
bool hasAdminRights() const {
return _hasAdminRights;
}
private:
not_null<PeerData*> _peer;
not_null<UserData*> _user;
bool _hasAdminRights = false;
class Inner;
QPointer<Inner> _inner;
};
class EditAdminBox : public EditParticipantBox {
public:
EditAdminBox(
QWidget*,
not_null<PeerData*> peer,
not_null<UserData*> user,
const MTPChatAdminRights &rights);
void setSaveCallback(
Fn<void(MTPChatAdminRights, MTPChatAdminRights)> callback) {
_saveCallback = std::move(callback);
}
protected:
void prepare() override;
private:
using Flag = MTPDchatAdminRights::Flag;
using Flags = MTPDchatAdminRights::Flags;
static MTPChatAdminRights Defaults(not_null<PeerData*> peer);
bool canSave() const {
return !!_saveCallback;
}
void refreshAboutAddAdminsText(bool canAddAdmins);
const MTPChatAdminRights _oldRights;
Fn<void(MTPChatAdminRights, MTPChatAdminRights)> _saveCallback;
QPointer<Ui::FlatLabel> _aboutAddAdmins;
};
// Restricted box works with flags in the opposite way.
// If some flag is set in the rights then the checkbox is unchecked.
class EditRestrictedBox : public EditParticipantBox {
public:
EditRestrictedBox(
QWidget*,
not_null<PeerData*> peer,
not_null<UserData*> user,
bool hasAdminRights,
const MTPChatBannedRights &rights);
void setSaveCallback(
Fn<void(MTPChatBannedRights, MTPChatBannedRights)> callback) {
_saveCallback = std::move(callback);
}
protected:
void prepare() override;
private:
using Flag = MTPDchatBannedRights::Flag;
using Flags = MTPDchatBannedRights::Flags;
static MTPChatBannedRights Defaults(not_null<PeerData*> peer);
bool canSave() const {
return !!_saveCallback;
}
void showRestrictUntil();
void setRestrictUntil(TimeId until);
bool isUntilForever() const;
void createUntilGroup();
void createUntilVariants();
TimeId getRealUntilValue() const;
const MTPChatBannedRights _oldRights;
TimeId _until = 0;
Fn<void(MTPChatBannedRights, MTPChatBannedRights)> _saveCallback;
std::shared_ptr<Ui::RadiobuttonGroup> _untilGroup;
std::vector<base::unique_qptr<Ui::Radiobutton>> _untilVariants;
QPointer<CalendarBox> _restrictUntilBox;
static constexpr auto kUntilOneDay = -1;
static constexpr auto kUntilOneWeek = -2;
static constexpr auto kUntilCustom = -3;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,297 @@
/*
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 <rpl/variable.h>
#include "boxes/peer_list_box.h"
#include "mtproto/sender.h"
#include "base/timer.h"
#include "base/weak_ptr.h"
#include "info/profile/info_profile_members_controllers.h"
namespace Window {
class Navigation;
} // namespace Window
Fn<void(
const MTPChatAdminRights &oldRights,
const MTPChatAdminRights &newRights)> SaveAdminCallback(
not_null<PeerData*> peer,
not_null<UserData*> user,
Fn<void(const MTPChatAdminRights &newRights)> onDone,
Fn<void()> onFail);
Fn<void(
const MTPChatBannedRights &oldRights,
const MTPChatBannedRights &newRights)> SaveRestrictedCallback(
not_null<PeerData*> peer,
not_null<UserData*> user,
Fn<void(const MTPChatBannedRights &newRights)> onDone,
Fn<void()> onFail);
void SubscribeToMigration(
not_null<PeerData*> peer,
rpl::lifetime &lifetime,
Fn<void(not_null<ChannelData*>)> migrate);
enum class ParticipantsRole {
Profile,
Members,
Admins,
Restricted,
Kicked,
};
class ParticipantsOnlineSorter : private base::Subscriber {
public:
ParticipantsOnlineSorter(
not_null<PeerData*> peer,
not_null<PeerListDelegate*> delegate);
void sort();
rpl::producer<int> onlineCountValue() const;
private:
void sortDelayed();
void refreshOnlineCount();
not_null<PeerData*> _peer;
not_null<PeerListDelegate*> _delegate;
base::Timer _sortByOnlineTimer;
rpl::variable<int> _onlineCount = 0;
};
class ParticipantsAdditionalData {
public:
using Role = ParticipantsRole;
ParticipantsAdditionalData(not_null<PeerData*> peer, Role role);
UserData *applyParticipant(const MTPChannelParticipant &data);
UserData *applyParticipant(
const MTPChannelParticipant &data,
Role overrideRole);
void setExternal(not_null<UserData*> user);
void checkForLoaded(not_null<UserData*> user);
void fillFromPeer();
[[nodiscard]] bool infoLoaded(not_null<UserData*> user) const;
[[nodiscard]] bool canEditAdmin(not_null<UserData*> user) const;
[[nodiscard]] bool canAddOrEditAdmin(not_null<UserData*> user) const;
[[nodiscard]] bool canRestrictUser(not_null<UserData*> user) const;
[[nodiscard]] std::optional<MTPChatAdminRights> adminRights(
not_null<UserData*> user) const;
[[nodiscard]] std::optional<MTPChatBannedRights> restrictedRights(
not_null<UserData*> user) const;
[[nodiscard]] bool isCreator(not_null<UserData*> user) const;
[[nodiscard]] bool isExternal(not_null<UserData*> user) const;
[[nodiscard]] bool isKicked(not_null<UserData*> user) const;
[[nodiscard]] UserData *adminPromotedBy(not_null<UserData*> user) const;
[[nodiscard]] UserData *restrictedBy(not_null<UserData*> user) const;
void migrate(not_null<ChannelData*> channel);
private:
UserData *applyCreator(const MTPDchannelParticipantCreator &data);
UserData *applyAdmin(const MTPDchannelParticipantAdmin &data);
UserData *applyRegular(MTPint userId);
UserData *applyBanned(const MTPDchannelParticipantBanned &data);
void fillFromChat(not_null<ChatData*> chat);
void fillFromChannel(not_null<ChannelData*> channel);
not_null<PeerData*> _peer;
Role _role = Role::Members;
UserData *_creator = nullptr;
// Data for chats.
base::flat_set<not_null<UserData*>> _members;
base::flat_set<not_null<UserData*>> _admins;
// Data for channels.
base::flat_map<not_null<UserData*>, MTPChatAdminRights> _adminRights;
base::flat_set<not_null<UserData*>> _adminCanEdit;
base::flat_map<not_null<UserData*>, not_null<UserData*>> _adminPromotedBy;
std::map<not_null<UserData*>, MTPChatBannedRights> _restrictedRights;
std::set<not_null<UserData*>> _kicked;
std::map<not_null<UserData*>, not_null<UserData*>> _restrictedBy;
std::set<not_null<UserData*>> _external;
std::set<not_null<UserData*>> _infoNotLoaded;
};
// Viewing admins, banned or restricted users list with search.
class ParticipantsBoxController
: public PeerListController
, private base::Subscriber
, private MTP::Sender
, public base::has_weak_ptr {
public:
using Role = ParticipantsRole;
static void Start(
not_null<Window::Navigation*> navigation,
not_null<PeerData*> peer,
Role role);
ParticipantsBoxController(
not_null<Window::Navigation*> navigation,
not_null<PeerData*> peer,
Role role);
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
void rowActionClicked(not_null<PeerListRow*> row) override;
base::unique_qptr<Ui::PopupMenu> rowContextMenu(
QWidget *parent,
not_null<PeerListRow*> row) override;
void loadMoreRows() override;
void peerListSearchAddRow(not_null<PeerData*> peer) override;
std::unique_ptr<PeerListRow> createSearchRow(
not_null<PeerData*> peer) override;
std::unique_ptr<PeerListRow> createRestoredRow(
not_null<PeerData*> peer) override;
std::unique_ptr<PeerListState> saveState() const override;
void restoreState(std::unique_ptr<PeerListState> state) override;
rpl::producer<int> onlineCountValue() const override;
protected:
virtual std::unique_ptr<PeerListRow> createRow(
not_null<UserData*> user) const;
private:
using Row = Info::Profile::MemberListRow;
using Type = Row::Type;
using Rights = Row::Rights;
struct SavedState : SavedStateBase {
explicit SavedState(const ParticipantsAdditionalData &additional);
using SearchStateBase = PeerListSearchController::SavedStateBase;
std::unique_ptr<SearchStateBase> searchState;
int offset = 0;
bool allLoaded = false;
bool wasLoading = false;
ParticipantsAdditionalData additional;
rpl::lifetime lifetime;
};
static std::unique_ptr<PeerListSearchController> CreateSearchController(
not_null<PeerData*> peer,
Role role,
not_null<ParticipantsAdditionalData*> additional);
void prepareChatRows(not_null<ChatData*> chat);
void rebuildChatRows(not_null<ChatData*> chat);
void rebuildChatParticipants(not_null<ChatData*> chat);
void rebuildChatAdmins(not_null<ChatData*> chat);
void chatListReady();
void rebuildRowTypes();
void addNewItem();
void addNewParticipants();
void refreshDescription();
void setupListChangeViewers();
void showAdmin(not_null<UserData*> user);
void editAdminDone(
not_null<UserData*> user,
const MTPChatAdminRights &rights);
void showRestricted(not_null<UserData*> user);
void editRestrictedDone(
not_null<UserData*> user,
const MTPChatBannedRights &rights);
void removeKicked(not_null<PeerListRow*> row, not_null<UserData*> user);
void removeKickedWithRow(not_null<UserData*> user);
void removeKicked(not_null<UserData*> user);
void kickMember(not_null<UserData*> user);
void kickMemberSure(not_null<UserData*> user);
void unkickMember(not_null<UserData*> user);
void removeAdmin(not_null<UserData*> user);
void removeAdminSure(not_null<UserData*> user);
bool appendRow(not_null<UserData*> user);
bool prependRow(not_null<UserData*> user);
bool removeRow(not_null<UserData*> user);
void refreshCustomStatus(not_null<PeerListRow*> row) const;
bool feedMegagroupLastParticipants();
Type computeType(not_null<UserData*> user) const;
void recomputeTypeFor(not_null<UserData*> user);
void subscribeToMigration();
void migrate(not_null<ChannelData*> channel);
not_null<Window::Navigation*> _navigation;
not_null<PeerData*> _peer;
Role _role = Role::Admins;
int _offset = 0;
mtpRequestId _loadRequestId = 0;
bool _allLoaded = false;
ParticipantsAdditionalData _additional;
std::unique_ptr<ParticipantsOnlineSorter> _onlineSorter;
BoxPointer _editBox;
BoxPointer _addBox;
};
// Members, banned and restricted users server side search.
class ParticipantsBoxSearchController
: public PeerListSearchController
, private MTP::Sender {
public:
using Role = ParticipantsBoxController::Role;
ParticipantsBoxSearchController(
not_null<ChannelData*> channel,
Role role,
not_null<ParticipantsAdditionalData*> additional);
void searchQuery(const QString &query) override;
bool isLoading() override;
bool loadMoreRows() override;
std::unique_ptr<SavedStateBase> saveState() const override;
void restoreState(std::unique_ptr<SavedStateBase> state) override;
private:
struct SavedState : SavedStateBase {
QString query;
int offset = 0;
bool allLoaded = false;
bool wasLoading = false;
};
struct CacheEntry {
MTPchannels_ChannelParticipants result;
int requestedCount = 0;
};
struct Query {
QString text;
int offset = 0;
};
void searchOnServer();
bool searchInCache();
void searchDone(
mtpRequestId requestId,
const MTPchannels_ChannelParticipants &result,
int requestedCount);
not_null<ChannelData*> _channel;
Role _role = Role::Restricted;
not_null<ParticipantsAdditionalData*> _additional;
base::Timer _timer;
QString _query;
mtpRequestId _requestId = 0;
int _offset = 0;
bool _allLoaded = false;
std::map<QString, CacheEntry> _cache;
std::map<mtpRequestId, Query> _queries;
};

View File

@@ -23,13 +23,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/add_contact_box.h"
#include "boxes/stickers_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/peers/edit_participants_box.h"
#include "data/data_peer.h"
#include "data/data_chat.h"
#include "data/data_channel.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "mtproto/sender.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "messenger.h"
#include "core/application.h"
#include "apiwrap.h"
#include "application.h"
#include "auth_session.h"
#include "observer_peer.h"
#include "styles/style_boxes.h"
@@ -58,10 +61,6 @@ private:
Public,
Private,
};
enum class Invites {
Everyone,
OnlyAdmins,
};
enum class HistoryVisibility {
Visible,
Hidden,
@@ -90,7 +89,6 @@ private:
std::shared_ptr<Ui::RadioenumGroup<HistoryVisibility>> historyVisibility;
Ui::SlideWrap<Ui::RpWidget> *historyVisibilityWrap = nullptr;
std::shared_ptr<Ui::RadioenumGroup<Invites>> invites;
Ui::Checkbox *signatures = nullptr;
};
struct Saving {
@@ -99,7 +97,6 @@ private:
std::optional<QString> description;
std::optional<bool> hiddenPreHistory;
std::optional<bool> signatures;
std::optional<bool> everyoneInvites;
};
Fn<QString()> computeTitle() const;
@@ -113,13 +110,11 @@ private:
object_ptr<Ui::RpWidget> createInviteLinkEdit();
object_ptr<Ui::RpWidget> createHistoryVisibilityEdit();
object_ptr<Ui::RpWidget> createSignaturesEdit();
object_ptr<Ui::RpWidget> createInvitesEdit();
object_ptr<Ui::RpWidget> createStickersEdit();
object_ptr<Ui::RpWidget> createManageAdminsButton();
object_ptr<Ui::RpWidget> createUpgradeButton();
object_ptr<Ui::RpWidget> createDeleteButton();
QString inviteLinkText() const;
void observeInviteLink();
void submitTitle();
void submitDescription();
@@ -150,7 +145,6 @@ private:
bool validateTitle(Saving &to) const;
bool validateDescription(Saving &to) const;
bool validateHistoryVisibility(Saving &to) const;
bool validateInvites(Saving &to) const;
bool validateSignatures(Saving &to) const;
void save();
@@ -158,13 +152,15 @@ private:
void saveTitle();
void saveDescription();
void saveHistoryVisibility();
void saveInvites();
void saveSignatures();
void savePhoto();
void pushSaveStage(FnMut<void()> &&lambda);
void continueSave();
void cancelSave();
void subscribeToMigration();
void migrate(not_null<ChannelData*> channel);
not_null<BoxContent*> _box;
not_null<PeerData*> _peer;
bool _isGroup = false;
@@ -179,6 +175,8 @@ private:
std::deque<FnMut<void()>> _saveStagesQueue;
Saving _savingData;
rpl::lifetime _lifetime;
};
Controller::Controller(
@@ -187,7 +185,7 @@ Controller::Controller(
: _box(box)
, _peer(peer)
, _isGroup(_peer->isChat() || _peer->isMegagroup())
, _checkUsernameTimer([this] { checkUsernameAvailability(); }) {
, _checkUsernameTimer([=] { checkUsernameAvailability(); }) {
_box->setTitle(computeTitle());
_box->addButton(langFactory(lng_settings_save), [this] {
save();
@@ -195,6 +193,21 @@ Controller::Controller(
_box->addButton(langFactory(lng_cancel), [this] {
_box->closeBox();
});
subscribeToMigration();
_peer->updateFull();
}
void Controller::subscribeToMigration() {
SubscribeToMigration(
_peer,
_lifetime,
[=](not_null<ChannelData*> channel) { migrate(channel); });
}
void Controller::migrate(not_null<ChannelData*> channel) {
_peer = channel;
observeInviteLink();
_peer->updateFull();
}
Fn<QString()> Controller::computeTitle() const {
@@ -215,10 +228,7 @@ object_ptr<Ui::VerticalLayout> Controller::createContent() {
_wrap->add(createInviteLinkEdit());
_wrap->add(createHistoryVisibilityEdit());
_wrap->add(createSignaturesEdit());
_wrap->add(createInvitesEdit());
_wrap->add(createStickersEdit());
_wrap->add(createManageAdminsButton());
_wrap->add(createUpgradeButton());
_wrap->add(createDeleteButton());
return result;
@@ -233,11 +243,11 @@ void Controller::setFocus() {
object_ptr<Ui::RpWidget> Controller::createPhotoAndTitleEdit() {
Expects(_wrap != nullptr);
auto canEdit = [&] {
if (auto channel = _peer->asChannel()) {
const auto canEdit = [&] {
if (const auto channel = _peer->asChannel()) {
return channel->canEditInformation();
} else if (auto chat = _peer->asChat()) {
return chat->canEdit();
} else if (const auto chat = _peer->asChat()) {
return chat->canEditInformation();
}
return false;
}();
@@ -319,11 +329,6 @@ object_ptr<Ui::RpWidget> Controller::createTitleEdit() {
object_ptr<Ui::RpWidget> Controller::createDescriptionEdit() {
Expects(_wrap != nullptr);
auto channel = _peer->asChannel();
if (!channel || !channel->canEditInformation()) {
return nullptr;
}
auto result = object_ptr<Ui::PaddingWrap<Ui::InputField>>(
_wrap,
object_ptr<Ui::InputField>(
@@ -331,7 +336,7 @@ object_ptr<Ui::RpWidget> Controller::createDescriptionEdit() {
st::editPeerDescription,
Ui::InputField::Mode::MultiLine,
langFactory(lng_create_group_description),
channel->about()),
_peer->about()),
st::editPeerDescriptionMargins);
result->entity()->setMaxLength(kMaxChannelDescription);
result->entity()->setInstantReplaces(Ui::InstantReplaces::Default());
@@ -353,8 +358,15 @@ object_ptr<Ui::RpWidget> Controller::createDescriptionEdit() {
object_ptr<Ui::RpWidget> Controller::createPrivaciesEdit() {
Expects(_wrap != nullptr);
auto channel = _peer->asChannel();
if (!channel || !channel->canEditUsername()) {
const auto canEditUsername = [&] {
if (const auto chat = _peer->asChat()) {
return chat->canEditUsername();
} else if (const auto channel = _peer->asChannel()) {
return channel->canEditUsername();
}
Unexpected("Peer type in Controller::createPrivaciesEdit.");
}();
if (!canEditUsername) {
return nullptr;
}
auto result = object_ptr<Ui::PaddingWrap<Ui::VerticalLayout>>(
@@ -363,8 +375,10 @@ object_ptr<Ui::RpWidget> Controller::createPrivaciesEdit() {
st::editPeerPrivaciesMargins);
auto container = result->entity();
const auto isPublic = _peer->isChannel()
&& _peer->asChannel()->isPublic();
_controls.privacy = std::make_shared<Ui::RadioenumGroup<Privacy>>(
channel->isPublic() ? Privacy::Public : Privacy::Private);
isPublic ? Privacy::Public : Privacy::Private);
auto addButton = [&](
Privacy value,
LangKey groupTextKey,
@@ -408,7 +422,7 @@ object_ptr<Ui::RpWidget> Controller::createPrivaciesEdit() {
_controls.privacy->setChangedCallback([this](Privacy value) {
privacyChanged(value);
});
if (!channel->isPublic()) {
if (!isPublic) {
checkUsernameAvailability();
}
@@ -418,8 +432,8 @@ object_ptr<Ui::RpWidget> Controller::createPrivaciesEdit() {
object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
Expects(_wrap != nullptr);
auto channel = _peer->asChannel();
Assert(channel != nullptr);
const auto channel = _peer->asChannel();
const auto username = channel ? channel->username : QString();
auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
_wrap,
@@ -441,7 +455,7 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
container,
st::setupChannelLink,
Fn<QString()>(),
channel->username,
username,
true));
_controls.username->heightValue(
) | rpl::start_with_next([placeholder](int height) {
@@ -513,9 +527,6 @@ void Controller::checkUsernameAvailability() {
if (!_controls.username) {
return;
}
auto channel = _peer->asChannel();
Assert(channel != nullptr);
auto initial = (_controls.privacy->value() != Privacy::Public);
auto checking = initial
? qsl(".bad.")
@@ -526,15 +537,17 @@ void Controller::checkUsernameAvailability() {
if (_checkUsernameRequestId) {
request(_checkUsernameRequestId).cancel();
}
const auto channel = _peer->migrateToOrMe()->asChannel();
const auto username = channel ? channel->username : QString();
_checkUsernameRequestId = request(MTPchannels_CheckUsername(
channel->inputChannel,
channel ? channel->inputChannel : MTP_inputChannelEmpty(),
MTP_string(checking)
)).done([=](const MTPBool &result) {
_checkUsernameRequestId = 0;
if (initial) {
return;
}
if (!mtpIsTrue(result) && checking != channel->username) {
if (!mtpIsTrue(result) && checking != username) {
showUsernameError(
Lang::Viewer(lng_create_channel_link_occupied));
} else {
@@ -542,7 +555,7 @@ void Controller::checkUsernameAvailability() {
}
}).fail([=](const RPCError &error) {
_checkUsernameRequestId = 0;
auto type = error.type();
const auto &type = error.type();
_usernameState = UsernameState::Normal;
if (type == qstr("CHANNEL_PUBLIC_GROUP_NA")) {
_usernameState = UsernameState::NotAvailable;
@@ -562,7 +575,7 @@ void Controller::checkUsernameAvailability() {
showUsernameError(
Lang::Viewer(lng_create_channel_link_invalid));
} else if (type == qstr("USERNAME_OCCUPIED")
&& checking != channel->username) {
&& checking != username) {
showUsernameError(
Lang::Viewer(lng_create_channel_link_occupied));
}
@@ -651,10 +664,10 @@ void Controller::revokeInviteLink() {
void Controller::exportInviteLink(const QString &confirmation) {
auto boxPointer = std::make_shared<QPointer<ConfirmBox>>();
auto callback = crl::guard(this, [=] {
if (auto strong = *boxPointer) {
if (const auto strong = *boxPointer) {
strong->closeBox();
}
Auth().api().exportInviteLink(_peer);
_peer->session().api().exportInviteLink(_peer->migrateToOrMe());
});
auto box = Box<ConfirmBox>(
confirmation,
@@ -663,13 +676,12 @@ void Controller::exportInviteLink(const QString &confirmation) {
}
bool Controller::canEditInviteLink() const {
if (auto channel = _peer->asChannel()) {
if (channel->canEditUsername()) {
return true;
}
return (!channel->isPublic() && channel->canAddMembers());
} else if (auto chat = _peer->asChat()) {
return !chat->inviteLink().isEmpty() || chat->amCreator();
if (const auto channel = _peer->asChannel()) {
return channel->amCreator()
|| (channel->adminRights() & ChatAdminRight::f_invite_users);
} else if (const auto chat = _peer->asChat()) {
return chat->amCreator()
|| (chat->adminRights() & ChatAdminRight::f_invite_users);
}
return false;
}
@@ -680,14 +692,27 @@ bool Controller::inviteLinkShown() const {
}
QString Controller::inviteLinkText() const {
if (auto channel = _peer->asChannel()) {
if (const auto channel = _peer->asChannel()) {
return channel->inviteLink();
} else if (auto chat = _peer->asChat()) {
} else if (const auto chat = _peer->asChat()) {
return chat->inviteLink();
}
return QString();
}
void Controller::observeInviteLink() {
if (!_controls.editInviteLinkWrap) {
return;
}
Notify::PeerUpdateValue(
_peer,
Notify::PeerUpdate::Flag::InviteLinkChanged
) | rpl::start_with_next([=] {
refreshCreateInviteLink();
refreshEditInviteLink();
}, _controls.editInviteLinkWrap->lifetime());
}
object_ptr<Ui::RpWidget> Controller::createInviteLinkEdit() {
Expects(_wrap != nullptr);
@@ -717,7 +742,7 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkEdit() {
_controls.inviteLink->setContextCopyText(QString());
_controls.inviteLink->setBreakEverywhere(true);
_controls.inviteLink->setClickHandlerFilter([=](auto&&...) {
Application::clipboard()->setText(inviteLinkText());
QApplication::clipboard()->setText(inviteLinkText());
Ui::Toast::Show(lang(lng_group_invite_copied));
return false;
});
@@ -729,14 +754,9 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkEdit() {
container,
lang(lng_group_invite_create_new),
st::editPeerInviteLinkButton)
)->addClickHandler([this] { revokeInviteLink(); });
)->addClickHandler([=] { revokeInviteLink(); });
Notify::PeerUpdateValue(
_peer,
Notify::PeerUpdate::Flag::InviteLinkChanged
) | rpl::start_with_next([this] {
refreshEditInviteLink();
}, _controls.editInviteLinkWrap->lifetime());
observeInviteLink();
return std::move(result);
}
@@ -796,12 +816,7 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkCreate() {
});
_controls.createInviteLinkWrap = result.data();
Notify::PeerUpdateValue(
_peer,
Notify::PeerUpdate::Flag::InviteLinkChanged
) | rpl::start_with_next([this] {
refreshCreateInviteLink();
}, _controls.createInviteLinkWrap->lifetime());
observeInviteLink();
return std::move(result);
}
@@ -815,13 +830,19 @@ void Controller::refreshCreateInviteLink() {
object_ptr<Ui::RpWidget> Controller::createHistoryVisibilityEdit() {
Expects(_wrap != nullptr);
auto channel = _peer->asChannel();
if (!channel
|| !channel->canEditPreHistoryHidden()
|| !channel->isMegagroup()
|| (channel->isPublic() && !channel->canEditUsername())) {
const auto canEdit = [&] {
if (const auto chat = _peer->asChat()) {
return chat->canEditPreHistoryHidden();
} else if (const auto channel = _peer->asChannel()) {
return channel->canEditPreHistoryHidden();
}
Unexpected("User in Controller::createHistoryVisibilityEdit.");
}();
if (!canEdit) {
return nullptr;
}
const auto channel = _peer->asChannel();
auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
_wrap,
object_ptr<Ui::VerticalLayout>(_wrap),
@@ -831,7 +852,7 @@ object_ptr<Ui::RpWidget> Controller::createHistoryVisibilityEdit() {
_controls.historyVisibility
= std::make_shared<Ui::RadioenumGroup<HistoryVisibility>>(
channel->hiddenPreHistory()
(!channel || channel->hiddenPreHistory())
? HistoryVisibility::Hidden
: HistoryVisibility::Visible);
auto addButton = [&](
@@ -867,7 +888,9 @@ object_ptr<Ui::RpWidget> Controller::createHistoryVisibilityEdit() {
addButton(
HistoryVisibility::Hidden,
lng_manage_history_visibility_hidden,
lng_manage_history_visibility_hidden_about);
(_peer->isChat()
? lng_manage_history_visibility_hidden_legacy
: lng_manage_history_visibility_hidden_about));
refreshHistoryVisibility();
@@ -914,57 +937,6 @@ object_ptr<Ui::RpWidget> Controller::createSignaturesEdit() {
return std::move(result);
}
object_ptr<Ui::RpWidget> Controller::createInvitesEdit() {
Expects(_wrap != nullptr);
auto channel = _peer->asChannel();
if (!channel
|| !channel->canEditInvites()
|| !channel->isMegagroup()) {
return nullptr;
}
auto result = object_ptr<Ui::PaddingWrap<Ui::VerticalLayout>>(
_wrap,
object_ptr<Ui::VerticalLayout>(_wrap),
st::editPeerInvitesMargins);
auto container = result->entity();
container->add(object_ptr<Ui::FlatLabel>(
container,
Lang::Viewer(lng_edit_group_who_invites),
st::editPeerSectionLabel));
_controls.invites = std::make_shared<Ui::RadioenumGroup<Invites>>(
channel->anyoneCanAddMembers()
? Invites::Everyone
: Invites::OnlyAdmins);
auto addButton = [&](
Invites value,
LangKey textKey) {
container->add(object_ptr<Ui::FixedHeightWidget>(
container,
st::editPeerInvitesTopSkip + st::editPeerInvitesSkip));
container->add(object_ptr<Ui::Radioenum<Invites>>(
container,
_controls.invites,
value,
lang(textKey),
st::defaultBoxCheckbox));
};
addButton(
Invites::Everyone,
lng_edit_group_invites_everybody);
addButton(
Invites::OnlyAdmins,
lng_edit_group_invites_only_admins);
container->add(object_ptr<Ui::FixedHeightWidget>(
container,
st::editPeerInvitesSkip));
return std::move(result);
}
object_ptr<Ui::RpWidget> Controller::createStickersEdit() {
Expects(_wrap != nullptr);
@@ -999,53 +971,13 @@ object_ptr<Ui::RpWidget> Controller::createStickersEdit() {
_wrap,
lang(lng_group_stickers_add),
st::editPeerInviteLinkButton)
)->addClickHandler([channel] {
)->addClickHandler([=] {
Ui::show(Box<StickersBox>(channel), LayerOption::KeepOther);
});
return std::move(result);
}
object_ptr<Ui::RpWidget> Controller::createManageAdminsButton() {
Expects(_wrap != nullptr);
auto chat = _peer->asChat();
if (!chat || !chat->amCreator() || chat->isDeactivated()) {
return nullptr;
}
auto result = object_ptr<Ui::PaddingWrap<Ui::LinkButton>>(
_wrap,
object_ptr<Ui::LinkButton>(
_wrap,
lang(lng_profile_manage_admins),
st::editPeerInviteLinkButton),
st::editPeerDeleteButtonMargins);
result->entity()->addClickHandler([=] {
EditChatAdminsBoxController::Start(chat);
});
return std::move(result);
}
object_ptr<Ui::RpWidget> Controller::createUpgradeButton() {
Expects(_wrap != nullptr);
auto chat = _peer->asChat();
if (!chat || !chat->amCreator() || chat->isDeactivated()) {
return nullptr;
}
auto result = object_ptr<Ui::PaddingWrap<Ui::LinkButton>>(
_wrap,
object_ptr<Ui::LinkButton>(
_wrap,
lang(lng_profile_migrate_button),
st::editPeerInviteLinkButton),
st::editPeerDeleteButtonMargins);
result->entity()->addClickHandler([=] {
Ui::show(Box<ConvertToSupergroupBox>(chat), LayerOption::KeepOther);
});
return std::move(result);
}
object_ptr<Ui::RpWidget> Controller::createDeleteButton() {
Expects(_wrap != nullptr);
@@ -1099,7 +1031,6 @@ std::optional<Controller::Saving> Controller::validate() const {
&& validateTitle(result)
&& validateDescription(result)
&& validateHistoryVisibility(result)
&& validateInvites(result)
&& validateSignatures(result)) {
return result;
}
@@ -1155,15 +1086,6 @@ bool Controller::validateHistoryVisibility(Saving &to) const {
return true;
}
bool Controller::validateInvites(Saving &to) const {
if (!_controls.invites) {
return true;
}
to.everyoneInvites
= (_controls.invites->value() == Invites::Everyone);
return true;
}
bool Controller::validateSignatures(Saving &to) const {
if (!_controls.signatures) {
return true;
@@ -1184,7 +1106,6 @@ void Controller::save() {
pushSaveStage([this] { saveTitle(); });
pushSaveStage([this] { saveDescription(); });
pushSaveStage([this] { saveHistoryVisibility(); });
pushSaveStage([this] { saveInvites(); });
pushSaveStage([this] { saveSignatures(); });
pushSaveStage([this] { savePhoto(); });
continueSave();
@@ -1208,12 +1129,24 @@ void Controller::cancelSave() {
}
void Controller::saveUsername() {
auto channel = _peer->asChannel();
if (!_savingData.username
|| !channel
|| *_savingData.username == channel->username) {
const auto channel = _peer->asChannel();
const auto username = (channel ? channel->username : QString());
if (!_savingData.username || *_savingData.username == username) {
return continueSave();
} else if (!channel) {
const auto saveForChannel = [=](not_null<ChannelData*> channel) {
if (_peer->asChannel() == channel) {
saveUsername();
} else {
cancelSave();
}
};
_peer->session().api().migrateChat(
_peer->asChat(),
crl::guard(this, saveForChannel));
return;
}
request(MTPchannels_UpdateUsername(
channel->inputChannel,
MTP_string(*_savingData.username)
@@ -1223,7 +1156,7 @@ void Controller::saveUsername() {
*_savingData.username);
continueSave();
}).fail([=](const RPCError &error) {
auto type = error.type();
const auto &type = error.type();
if (type == qstr("USERNAME_NOT_MODIFIED")) {
channel->setName(
TextUtilities::SingleLine(channel->name),
@@ -1231,7 +1164,7 @@ void Controller::saveUsername() {
continueSave();
return;
}
auto errorKey = [&] {
const auto errorKey = [&] {
if (type == qstr("USERNAME_INVALID")) {
return lng_create_channel_link_invalid;
} else if (type == qstr("USERNAME_OCCUPIED")
@@ -1252,17 +1185,17 @@ void Controller::saveTitle() {
return continueSave();
}
auto onDone = [this](const MTPUpdates &result) {
Auth().api().applyUpdates(result);
const auto onDone = [=](const MTPUpdates &result) {
_peer->session().api().applyUpdates(result);
continueSave();
};
auto onFail = [this](const RPCError &error) {
auto type = error.type();
const auto onFail = [=](const RPCError &error) {
const auto &type = error.type();
if (type == qstr("CHAT_NOT_MODIFIED")
|| type == qstr("CHAT_TITLE_NOT_MODIFIED")) {
if (auto channel = _peer->asChannel()) {
if (const auto channel = _peer->asChannel()) {
channel->setName(*_savingData.title, channel->username);
} else if (auto chat = _peer->asChat()) {
} else if (const auto chat = _peer->asChat()) {
chat->setName(*_savingData.title);
}
continueSave();
@@ -1275,14 +1208,14 @@ void Controller::saveTitle() {
cancelSave();
};
if (auto channel = _peer->asChannel()) {
if (const auto channel = _peer->asChannel()) {
request(MTPchannels_EditTitle(
channel->inputChannel,
MTP_string(*_savingData.title)
)).done(std::move(onDone)
).fail(std::move(onFail)
).send();
} else if (auto chat = _peer->asChat()) {
} else if (const auto chat = _peer->asChat()) {
request(MTPmessages_EditChatTitle(
chat->inputChat,
MTP_string(*_savingData.title)
@@ -1295,23 +1228,22 @@ void Controller::saveTitle() {
}
void Controller::saveDescription() {
auto channel = _peer->asChannel();
const auto channel = _peer->asChannel();
if (!_savingData.description
|| !channel
|| *_savingData.description == channel->about()) {
|| *_savingData.description == _peer->about()) {
return continueSave();
}
auto successCallback = [=] {
channel->setAbout(*_savingData.description);
const auto successCallback = [=] {
_peer->setAbout(*_savingData.description);
continueSave();
};
request(MTPchannels_EditAbout(
channel->inputChannel,
request(MTPmessages_EditChatAbout(
_peer->input,
MTP_string(*_savingData.description)
)).done([=](const MTPBool &result) {
successCallback();
}).fail([=](const RPCError &error) {
auto type = error.type();
const auto &type = error.type();
if (type == qstr("CHAT_ABOUT_NOT_MODIFIED")) {
successCallback();
return;
@@ -1322,11 +1254,23 @@ void Controller::saveDescription() {
}
void Controller::saveHistoryVisibility() {
auto channel = _peer->asChannel();
const auto channel = _peer->asChannel();
const auto hidden = channel ? channel->hiddenPreHistory() : true;
if (!_savingData.hiddenPreHistory
|| !channel
|| *_savingData.hiddenPreHistory == channel->hiddenPreHistory()) {
|| *_savingData.hiddenPreHistory == hidden) {
return continueSave();
} else if (!channel) {
const auto saveForChannel = [=](not_null<ChannelData*> channel) {
if (_peer->asChannel() == channel) {
saveHistoryVisibility();
} else {
cancelSave();
}
};
_peer->session().api().migrateChat(
_peer->asChat(),
crl::guard(this, saveForChannel));
return;
}
request(MTPchannels_TogglePreHistoryHidden(
channel->inputChannel,
@@ -1337,31 +1281,9 @@ void Controller::saveHistoryVisibility() {
// So after saving we need to update it manually.
channel->updateFullForced();
Auth().api().applyUpdates(result);
channel->session().api().applyUpdates(result);
continueSave();
}).fail([this](const RPCError &error) {
if (error.type() == qstr("CHAT_NOT_MODIFIED")) {
continueSave();
} else {
cancelSave();
}
}).send();
}
void Controller::saveInvites() {
auto channel = _peer->asChannel();
if (!_savingData.everyoneInvites
|| !channel
|| *_savingData.everyoneInvites == channel->anyoneCanAddMembers()) {
return continueSave();
}
request(MTPchannels_ToggleInvites(
channel->inputChannel,
MTP_bool(*_savingData.everyoneInvites)
)).done([this](const MTPUpdates &result) {
Auth().api().applyUpdates(result);
continueSave();
}).fail([this](const RPCError &error) {
}).fail([=](const RPCError &error) {
if (error.type() == qstr("CHAT_NOT_MODIFIED")) {
continueSave();
} else {
@@ -1371,7 +1293,7 @@ void Controller::saveInvites() {
}
void Controller::saveSignatures() {
auto channel = _peer->asChannel();
const auto channel = _peer->asChannel();
if (!_savingData.signatures
|| !channel
|| *_savingData.signatures == channel->addsSignature()) {
@@ -1380,10 +1302,10 @@ void Controller::saveSignatures() {
request(MTPchannels_ToggleSignatures(
channel->inputChannel,
MTP_bool(*_savingData.signatures)
)).done([this](const MTPUpdates &result) {
Auth().api().applyUpdates(result);
)).done([=](const MTPUpdates &result) {
channel->session().api().applyUpdates(result);
continueSave();
}).fail([this](const RPCError &error) {
}).fail([=](const RPCError &error) {
if (error.type() == qstr("CHAT_NOT_MODIFIED")) {
continueSave();
} else {
@@ -1397,7 +1319,7 @@ void Controller::savePhoto() {
? _controls.photo->takeResultImage()
: QImage();
if (!image.isNull()) {
Auth().api().uploadPeerPhoto(_peer, std::move(image));
_peer->session().api().uploadPeerPhoto(_peer, std::move(image));
}
_box->closeBox();
}
@@ -1406,21 +1328,25 @@ void Controller::deleteWithConfirmation() {
const auto channel = _peer->asChannel();
Assert(channel != nullptr);
auto text = lang(_isGroup
const auto text = lang(_isGroup
? lng_sure_delete_group
: lng_sure_delete_channel);
auto deleteCallback = crl::guard(this, [=] {
const auto deleteCallback = crl::guard(this, [=] {
deleteChannel();
});
Ui::show(Box<ConfirmBox>(
text,
lang(lng_box_delete),
st::attentionBoxButton,
std::move(deleteCallback)), LayerOption::KeepOther);
Ui::show(
Box<ConfirmBox>(
text,
lang(lng_box_delete),
st::attentionBoxButton,
deleteCallback),
LayerOption::KeepOther);
}
void Controller::deleteChannel() {
const auto channel = _peer->asChannel();
Assert(channel != nullptr);
const auto chat = channel->migrateFrom();
Ui::hideLayer();
@@ -1439,7 +1365,7 @@ void Controller::deleteChannel() {
EditPeerInfoBox::EditPeerInfoBox(
QWidget*,
not_null<PeerData*> peer)
: _peer(peer) {
: _peer(peer->migrateToOrMe()) {
}
void EditPeerInfoBox::prepare() {

View File

@@ -0,0 +1,487 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/peers/edit_peer_permissions_box.h"
#include "lang/lang_keys.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/checkbox.h"
#include "ui/toast/toast.h"
#include "info/profile/info_profile_button.h"
#include "info/profile/info_profile_icon.h"
#include "info/profile/info_profile_values.h"
#include "boxes/peers/edit_participants_box.h"
#include "boxes/peers/manage_peer_box.h"
#include "window/window_controller.h"
#include "mainwindow.h"
#include "styles/style_boxes.h"
#include "styles/style_info.h"
namespace {
template <typename CheckboxesMap, typename DependenciesMap>
void ApplyDependencies(
const CheckboxesMap &checkboxes,
const DependenciesMap &dependencies,
QPointer<Ui::Checkbox> changed) {
const auto checkAndApply = [&](
auto &&current,
auto dependency,
bool isChecked) {
for (auto &&checkbox : checkboxes) {
if ((checkbox.first & dependency)
&& (checkbox.second->checked() == isChecked)) {
current->setChecked(isChecked);
return true;
}
}
return false;
};
const auto applySomeDependency = [&] {
auto result = false;
for (auto &&entry : checkboxes) {
if (entry.second == changed) {
continue;
}
auto isChecked = entry.second->checked();
for (auto &&dependency : dependencies) {
const auto check = isChecked
? dependency.first
: dependency.second;
if (entry.first & check) {
if (checkAndApply(
entry.second,
(isChecked
? dependency.second
: dependency.first),
!isChecked)) {
result = true;
break;
}
}
}
}
return result;
};
while (true) {
if (!applySomeDependency()) {
break;
}
};
}
std::vector<std::pair<ChatRestrictions, LangKey>> RestrictionLabels() {
using Flag = ChatRestriction;
return {
{ Flag::f_send_messages, lng_rights_chat_send_text },
{ Flag::f_send_media, lng_rights_chat_send_media },
{ Flag::f_send_stickers
| Flag::f_send_gifs
| Flag::f_send_games
| Flag::f_send_inline, lng_rights_chat_send_stickers },
{ Flag::f_embed_links, lng_rights_chat_send_links },
{ Flag::f_send_polls, lng_rights_chat_send_polls },
{ Flag::f_invite_users, lng_rights_chat_add_members },
{ Flag::f_pin_messages, lng_rights_group_pin },
{ Flag::f_change_info, lng_rights_group_info },
};
}
std::vector<std::pair<ChatAdminRights, LangKey>> AdminRightLabels(
bool isGroup,
bool anyoneCanAddMembers) {
using Flag = ChatAdminRight;
if (isGroup) {
return {
{ Flag::f_change_info, lng_rights_group_info },
{ Flag::f_delete_messages, lng_rights_group_delete },
{ Flag::f_ban_users, lng_rights_group_ban },
{ Flag::f_invite_users, anyoneCanAddMembers
? lng_rights_group_invite_link
: lng_rights_group_invite },
{ Flag::f_pin_messages, lng_rights_group_pin },
{ Flag::f_add_admins, lng_rights_add_admins },
};
} else {
return {
{ Flag::f_change_info, lng_rights_channel_info },
{ Flag::f_post_messages, lng_rights_channel_post },
{ Flag::f_edit_messages, lng_rights_channel_edit },
{ Flag::f_delete_messages, lng_rights_channel_delete },
{ Flag::f_invite_users, lng_rights_group_invite },
{ Flag::f_add_admins, lng_rights_add_admins }
};
}
}
auto Dependencies(ChatRestrictions)
-> std::vector<std::pair<ChatRestriction, ChatRestriction>> {
using Flag = ChatRestriction;
return {
// stickers <-> gifs
{ Flag::f_send_gifs, Flag::f_send_stickers },
{ Flag::f_send_stickers, Flag::f_send_gifs },
// stickers <-> games
{ Flag::f_send_games, Flag::f_send_stickers },
{ Flag::f_send_stickers, Flag::f_send_games },
// stickers <-> inline
{ Flag::f_send_inline, Flag::f_send_stickers },
{ Flag::f_send_stickers, Flag::f_send_inline },
// stickers -> send_media
{ Flag::f_send_stickers, Flag::f_send_media },
// embed_links -> send_media
{ Flag::f_embed_links, Flag::f_send_media },
// send_media -> send_messages
{ Flag::f_send_media, Flag::f_send_messages },
// send_polls -> send_messages
{ Flag::f_send_polls, Flag::f_send_messages },
// send_messages -> view_messages
{ Flag::f_send_messages, Flag::f_view_messages },
};
}
ChatRestrictions NegateRestrictions(ChatRestrictions value) {
using Flag = ChatRestriction;
return (~value) & (Flag(0)
// view_messages is always allowed, so it is never in restrictions.
//| Flag::f_view_messages
| Flag::f_change_info
| Flag::f_embed_links
| Flag::f_invite_users
| Flag::f_pin_messages
| Flag::f_send_games
| Flag::f_send_gifs
| Flag::f_send_inline
| Flag::f_send_media
| Flag::f_send_messages
| Flag::f_send_polls
| Flag::f_send_stickers);
}
auto Dependencies(ChatAdminRights)
-> std::vector<std::pair<ChatAdminRight, ChatAdminRight>> {
return {};
}
auto ToPositiveNumberString() {
return rpl::map([](int count) {
return count ? QString::number(count) : QString();
});
}
ChatRestrictions DisabledByAdminRights(not_null<PeerData*> peer) {
using Flag = ChatRestriction;
using Admin = ChatAdminRight;
using Admins = ChatAdminRights;
const auto adminRights = [&] {
const auto full = ~Admins(0);
if (const auto chat = peer->asChat()) {
return chat->amCreator() ? full : chat->adminRights();
} else if (const auto channel = peer->asChannel()) {
return channel->amCreator() ? full : channel->adminRights();
}
Unexpected("User in DisabledByAdminRights.");
}();
return Flag(0)
| ((adminRights & Admin::f_pin_messages)
? Flag(0)
: Flag::f_pin_messages)
| ((adminRights & Admin::f_invite_users)
? Flag(0)
: Flag::f_invite_users)
| ((adminRights & Admin::f_change_info)
? Flag(0)
: Flag::f_change_info);
}
} // namespace
ChatAdminRights DisabledByDefaultRestrictions(not_null<PeerData*> peer) {
using Flag = ChatAdminRight;
using Restriction = ChatRestriction;
const auto restrictions = [&] {
if (const auto chat = peer->asChat()) {
return chat->defaultRestrictions();
} else if (const auto channel = peer->asChannel()) {
return channel->defaultRestrictions();
}
Unexpected("User in DisabledByDefaultRestrictions.");
}();
return Flag(0)
| ((restrictions & Restriction::f_pin_messages)
? Flag(0)
: Flag::f_pin_messages)
//
// We allow to edit 'invite_users' admin right no matter what
// is chosen in default permissions for 'invite_users', because
// if everyone can 'invite_users' it handles invite link for admins.
//
//| ((restrictions & Restriction::f_invite_users)
// ? Flag(0)
// : Flag::f_invite_users)
//
| ((restrictions & Restriction::f_change_info)
? Flag(0)
: Flag::f_change_info);
}
EditPeerPermissionsBox::EditPeerPermissionsBox(
QWidget*,
not_null<PeerData*> peer)
: _peer(peer->migrateToOrMe()) {
}
auto EditPeerPermissionsBox::saveEvents() const
-> rpl::producer<MTPDchatBannedRights::Flags> {
Expects(_save != nullptr);
return _save->clicks() | rpl::map(_value);
}
void EditPeerPermissionsBox::prepare() {
setTitle(langFactory(lng_manage_peer_permissions));
const auto inner = setInnerWidget(object_ptr<Ui::VerticalLayout>(this));
using Flag = ChatRestriction;
using Flags = ChatRestrictions;
const auto disabledByAdminRights = DisabledByAdminRights(_peer);
const auto restrictions = [&] {
if (const auto chat = _peer->asChat()) {
return chat->defaultRestrictions()
| disabledByAdminRights;
} else if (const auto channel = _peer->asChannel()) {
return channel->defaultRestrictions()
| (channel->isPublic()
? (Flag::f_change_info | Flag::f_pin_messages)
: Flags(0))
| disabledByAdminRights;
}
Unexpected("User in EditPeerPermissionsBox.");
}();
const auto disabledMessages = [&] {
auto result = std::map<Flags, QString>();
result.emplace(
disabledByAdminRights,
lang(lng_rights_permission_cant_edit));
if (const auto channel = _peer->asChannel()) {
if (channel->isPublic()) {
result.emplace(
Flag::f_change_info | Flag::f_pin_messages,
lang(lng_rights_permission_unavailable));
}
}
return result;
}();
auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
this,
lng_rights_default_restrictions_header,
restrictions,
disabledMessages);
inner->add(std::move(checkboxes));
addBannedButtons(inner);
_value = getRestrictions;
_save = addButton(langFactory(lng_settings_save));
addButton(langFactory(lng_cancel), [=] { closeBox(); });
setDimensionsToContent(st::boxWidth, inner);
}
void EditPeerPermissionsBox::addBannedButtons(
not_null<Ui::VerticalLayout*> container) {
if (const auto chat = _peer->asChat()) {
if (!chat->amCreator()) {
return;
}
}
const auto channel = _peer->asChannel();
container->add(
object_ptr<BoxContentDivider>(container),
{ 0, st::infoProfileSkip, 0, st::infoProfileSkip });
const auto navigation = App::wnd()->controller();
ManagePeerBox::CreateButton(
container,
Lang::Viewer(lng_manage_peer_exceptions),
(channel
? Info::Profile::RestrictedCountValue(channel)
: rpl::single(0)) | ToPositiveNumberString(),
[=] {
ParticipantsBoxController::Start(
navigation,
_peer,
ParticipantsBoxController::Role::Restricted);
},
st::peerPermissionsButton);
if (channel) {
ManagePeerBox::CreateButton(
container,
Lang::Viewer(lng_manage_peer_removed_users),
Info::Profile::KickedCountValue(channel)
| ToPositiveNumberString(),
[=] {
ParticipantsBoxController::Start(
navigation,
_peer,
ParticipantsBoxController::Role::Kicked);
},
st::peerPermissionsButton);
}
}
template <
typename Flags,
typename DisabledMessagePairs,
typename FlagLabelPairs>
EditFlagsControl<Flags> CreateEditFlags(
QWidget *parent,
LangKey header,
Flags checked,
const DisabledMessagePairs &disabledMessagePairs,
const FlagLabelPairs &flagLabelPairs) {
auto widget = object_ptr<Ui::VerticalLayout>(parent);
const auto container = widget.data();
const auto checkboxes = container->lifetime(
).make_state<std::map<Flags, QPointer<Ui::Checkbox>>>();
const auto value = [=] {
auto result = Flags(0);
for (const auto &[flags, checkbox] : *checkboxes) {
if (checkbox->checked()) {
result |= flags;
} else {
result &= ~flags;
}
}
return result;
};
const auto changes = container->lifetime(
).make_state<rpl::event_stream<>>();
const auto applyDependencies = [=](Ui::Checkbox *control) {
static const auto dependencies = Dependencies(Flags());
ApplyDependencies(*checkboxes, dependencies, control);
};
container->add(
object_ptr<Ui::FlatLabel>(
container,
Lang::Viewer(header),
st::rightsHeaderLabel),
st::rightsHeaderMargin);
auto addCheckbox = [&](Flags flags, const QString &text) {
const auto lockedIt = ranges::find_if(
disabledMessagePairs,
[&](const auto &pair) { return (pair.first & flags) != 0; });
const auto locked = (lockedIt != end(disabledMessagePairs))
? std::make_optional(lockedIt->second)
: std::nullopt;
const auto toggled = ((checked & flags) != 0);
auto toggle = std::make_unique<Ui::ToggleView>(
st::rightsToggle,
toggled);
toggle->setLocked(locked.has_value());
const auto control = container->add(
object_ptr<Ui::Checkbox>(
container,
text,
st::rightsCheckbox,
std::move(toggle)),
st::rightsToggleMargin);
control->checkedChanges(
) | rpl::start_with_next([=](bool checked) {
if (locked.has_value()) {
if (checked != toggled) {
Ui::Toast::Show(*locked);
control->setChecked(toggled);
}
} else {
InvokeQueued(control, [=] {
applyDependencies(control);
changes->fire({});
});
}
}, control->lifetime());
checkboxes->emplace(flags, control);
};
for (const auto &[flags, label] : flagLabelPairs) {
addCheckbox(flags, lang(label));
}
applyDependencies(nullptr);
for (const auto &[flags, checkbox] : *checkboxes) {
checkbox->finishAnimating();
}
return {
std::move(widget),
value,
changes->events() | rpl::map(value)
};
}
EditFlagsControl<MTPDchatBannedRights::Flags> CreateEditRestrictions(
QWidget *parent,
LangKey header,
MTPDchatBannedRights::Flags restrictions,
std::map<MTPDchatBannedRights::Flags, QString> disabledMessages) {
auto result = CreateEditFlags(
parent,
header,
NegateRestrictions(restrictions),
disabledMessages,
RestrictionLabels());
result.value = [original = std::move(result.value)]{
return NegateRestrictions(original());
};
result.changes = std::move(
result.changes
) | rpl::map(NegateRestrictions);
return result;
}
EditFlagsControl<MTPDchatAdminRights::Flags> CreateEditAdminRights(
QWidget *parent,
LangKey header,
MTPDchatAdminRights::Flags rights,
std::map<MTPDchatAdminRights::Flags, QString> disabledMessages,
bool isGroup,
bool anyoneCanAddMembers) {
return CreateEditFlags(
parent,
header,
rights,
disabledMessages,
AdminRightLabels(isGroup, anyoneCanAddMembers));
}

View File

@@ -0,0 +1,59 @@
/*
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 "boxes/abstract_box.h"
#include "data/data_peer.h"
namespace Ui {
class RoundButton;
class VerticalLayout;
} // namespace Ui
enum LangKey : int;
class EditPeerPermissionsBox : public BoxContent {
public:
EditPeerPermissionsBox(QWidget*, not_null<PeerData*> peer);
rpl::producer<MTPDchatBannedRights::Flags> saveEvents() const;
protected:
void prepare() override;
private:
void addBannedButtons(not_null<Ui::VerticalLayout*> container);
not_null<PeerData*> _peer;
Ui::RoundButton *_save = nullptr;
Fn<MTPDchatBannedRights::Flags()> _value;
};
template <typename Flags>
struct EditFlagsControl {
object_ptr<Ui::RpWidget> widget;
Fn<Flags()> value;
rpl::producer<Flags> changes;
};
EditFlagsControl<MTPDchatBannedRights::Flags> CreateEditRestrictions(
QWidget *parent,
LangKey header,
MTPDchatBannedRights::Flags restrictions,
std::map<MTPDchatBannedRights::Flags, QString> disabledMessages);
EditFlagsControl<MTPDchatAdminRights::Flags> CreateEditAdminRights(
QWidget *parent,
LangKey header,
MTPDchatAdminRights::Flags rights,
std::map<MTPDchatAdminRights::Flags, QString> disabledMessages,
bool isGroup,
bool anyoneCanAddMembers);
ChatAdminRights DisabledByDefaultRestrictions(not_null<PeerData*> peer);

View File

@@ -10,23 +10,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/combine.h>
#include "lang/lang_keys.h"
#include "boxes/peers/edit_peer_info_box.h"
#include "boxes/peers/edit_peer_permissions_box.h"
#include "boxes/peers/edit_participants_box.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/widgets/labels.h"
#include "history/admin_log/history_admin_log_section.h"
#include "window/window_controller.h"
#include "mainwindow.h"
#include "profile/profile_channel_controllers.h"
#include "info/profile/info_profile_button.h"
#include "info/profile/info_profile_icon.h"
#include "info/profile/info_profile_values.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "mainwindow.h"
#include "auth_session.h"
#include "apiwrap.h"
#include "styles/style_boxes.h"
#include "styles/style_info.h"
namespace {
Fn<QString()> ManagePeerTitle(
not_null<ChannelData*> channel) {
return langFactory(channel->isMegagroup()
Fn<QString()> ManagePeerTitle(not_null<PeerData*> peer) {
return langFactory((peer->isChat() || peer->isMegagroup())
? lng_manage_group_title
: lng_manage_channel_title);
}
@@ -37,51 +41,33 @@ auto ToPositiveNumberString() {
});
}
template <typename Callback>
Info::Profile::Button *AddButton(
not_null<Ui::VerticalLayout*> parent,
rpl::producer<QString> &&text,
Callback callback,
Fn<void()> callback,
const style::icon &icon) {
auto button = parent->add(
object_ptr<Info::Profile::Button>(
parent,
std::move(text),
st::managePeerButton));
button->addClickHandler(std::forward<Callback>(callback));
Ui::CreateChild<Info::Profile::FloatingIcon>(
button,
icon,
st::managePeerButtonIconPosition);
return button;
return ManagePeerBox::CreateButton(
parent,
std::move(text),
rpl::single(QString()),
std::move(callback),
st::managePeerButton,
&icon);
}
template <typename Callback>
void AddButtonWithCount(
not_null<Ui::VerticalLayout*> parent,
rpl::producer<QString> &&text,
rpl::producer<QString> &&count,
Callback callback,
Fn<void()> callback,
const style::icon &icon) {
auto button = AddButton(
ManagePeerBox::CreateButton(
parent,
std::move(text),
std::forward<Callback>(callback),
icon);
auto label = Ui::CreateChild<Ui::FlatLabel>(
button,
std::move(count),
st::managePeerButtonLabel);
label->setAttribute(Qt::WA_TransparentForMouseEvents);
rpl::combine(
button->widthValue(),
label->widthValue()
) | rpl::start_with_next([label](int outerWidth, int width) {
label->moveToRight(
st::managePeerButtonLabelPosition.x(),
st::managePeerButtonLabelPosition.y(),
outerWidth);
}, label->lifetime());
std::move(callback),
st::managePeerButton,
&icon);
}
bool HasRecentActions(not_null<ChannelData*> channel) {
@@ -94,22 +80,90 @@ void ShowRecentActions(
navigation->showSection(AdminLog::SectionMemento(channel));
}
bool HasEditInfoBox(not_null<ChannelData*> channel) {
if (channel->canEditInformation()) {
return true;
} else if (!channel->isPublic() && channel->canAddMembers()) {
// Edit invite link.
return true;
bool HasEditInfoBox(not_null<PeerData*> peer) {
if (const auto chat = peer->asChat()) {
if (chat->canEditInformation()) {
return true;
}
} else if (const auto channel = peer->asChannel()) {
if (channel->canEditInformation()) {
return true;
} else if (!channel->isPublic() && channel->canAddMembers()) {
// Edit invite link.
return true;
}
}
return false;
}
void FillManageBox(
void ShowEditPermissions(not_null<PeerData*> peer) {
const auto box = Ui::show(
Box<EditPeerPermissionsBox>(peer),
LayerOption::KeepOther);
box->saveEvents(
) | rpl::start_with_next([=](MTPDchatBannedRights::Flags restrictions) {
const auto callback = crl::guard(box, [=](bool success) {
if (success) {
box->closeBox();
}
});
peer->session().api().saveDefaultRestrictions(
peer->migrateToOrMe(),
MTP_chatBannedRights(MTP_flags(restrictions), MTP_int(0)),
callback);
}, box->lifetime());
}
void FillManageChatBox(
not_null<Window::Navigation*> navigation,
not_null<ChatData*> chat,
not_null<Ui::VerticalLayout*> content) {
if (HasEditInfoBox(chat)) {
AddButton(
content,
Lang::Viewer(lng_manage_group_info),
[=] { Ui::show(Box<EditPeerInfoBox>(chat)); },
st::infoIconInformation);
}
if (chat->canEditPermissions()) {
AddButton(
content,
Lang::Viewer(lng_manage_peer_permissions),
[=] { ShowEditPermissions(chat); },
st::infoIconPermissions);
}
if (chat->amIn()) {
AddButtonWithCount(
content,
Lang::Viewer(lng_manage_peer_administrators),
Info::Profile::AdminsCountValue(chat)
| ToPositiveNumberString(),
[=] {
ParticipantsBoxController::Start(
navigation,
chat,
ParticipantsBoxController::Role::Admins);
},
st::infoIconAdministrators);
AddButtonWithCount(
content,
Lang::Viewer(lng_manage_peer_members),
Info::Profile::MembersCountValue(chat)
| ToPositiveNumberString(),
[=] {
ParticipantsBoxController::Start(
navigation,
chat,
ParticipantsBoxController::Role::Members);
},
st::infoIconMembers);
}
}
void FillManageChannelBox(
not_null<Window::Navigation*> navigation,
not_null<ChannelData*> channel,
not_null<Ui::VerticalLayout*> content) {
using Profile::ParticipantsBoxController;
auto isGroup = channel->isMegagroup();
if (HasEditInfoBox(channel)) {
AddButton(
@@ -127,19 +181,12 @@ void FillManageBox(
[=] { ShowRecentActions(navigation, channel); },
st::infoIconRecentActions);
}
if (channel->canViewMembers()) {
AddButtonWithCount(
if (channel->canEditPermissions()) {
AddButton(
content,
Lang::Viewer(lng_manage_peer_members),
Info::Profile::MembersCountValue(channel)
| ToPositiveNumberString(),
[=] {
ParticipantsBoxController::Start(
navigation,
channel,
ParticipantsBoxController::Role::Members);
},
st::infoIconMembers);
Lang::Viewer(lng_manage_peer_permissions),
[=] { ShowEditPermissions(channel); },
st::infoIconPermissions);
}
if (channel->canViewAdmins()) {
AddButtonWithCount(
@@ -155,26 +202,26 @@ void FillManageBox(
},
st::infoIconAdministrators);
}
if (channel->canViewBanned()) {
if (channel->isMegagroup()) {
AddButtonWithCount(
content,
Lang::Viewer(lng_manage_peer_restricted_users),
Info::Profile::RestrictedCountValue(channel)
| ToPositiveNumberString(),
[=] {
ParticipantsBoxController::Start(
navigation,
channel,
ParticipantsBoxController::Role::Restricted);
},
st::infoIconRestrictedUsers);
}
if (channel->canViewMembers()) {
AddButtonWithCount(
content,
Lang::Viewer(lng_manage_peer_banned_users),
Info::Profile::KickedCountValue(channel)
Lang::Viewer(lng_manage_peer_members),
Info::Profile::MembersCountValue(channel)
| ToPositiveNumberString(),
[=] {
ParticipantsBoxController::Start(
navigation,
channel,
ParticipantsBoxController::Role::Members);
},
st::infoIconMembers);
}
if (!channel->isMegagroup()) {
AddButtonWithCount(
content,
Lang::Viewer(lng_manage_peer_removed_users),
Info::Profile::KickedCountValue(channel)
| ToPositiveNumberString(),
[=] {
ParticipantsBoxController::Start(
navigation,
@@ -189,36 +236,87 @@ void FillManageBox(
ManagePeerBox::ManagePeerBox(
QWidget*,
not_null<ChannelData*> channel)
: _channel(channel) {
not_null<PeerData*> peer)
: _peer(peer) {
}
bool ManagePeerBox::Available(not_null<ChannelData*> channel) {
// canViewMembers() is removed, because in supergroups you
// see them in profile and in channels only admins can see them.
bool ManagePeerBox::Available(not_null<PeerData*> peer) {
if (const auto chat = peer->asChat()) {
return false
|| chat->canEditInformation()
|| chat->canEditPermissions();
} else if (const auto channel = peer->asChannel()) {
// canViewMembers() is removed, because in supergroups you
// see them in profile and in channels only admins can see them.
// canViewAdmins() is removed, because in supergroups it is
// always true and in channels it is equal to canViewBanned().
// canViewAdmins() is removed, because in supergroups it is
// always true and in channels it is equal to canViewBanned().
return false
// || channel->canViewMembers()
// || channel->canViewAdmins()
|| channel->canViewBanned()
|| channel->canEditInformation()
|| HasRecentActions(channel);
return false
//|| channel->canViewMembers()
//|| channel->canViewAdmins()
|| channel->canViewBanned()
|| channel->canEditInformation()
|| channel->canEditPermissions()
|| HasRecentActions(channel);
} else {
return false;
}
}
Info::Profile::Button *ManagePeerBox::CreateButton(
not_null<Ui::VerticalLayout*> parent,
rpl::producer<QString> &&text,
rpl::producer<QString> &&count,
Fn<void()> callback,
const style::InfoProfileCountButton &st,
const style::icon *icon) {
const auto button = parent->add(
object_ptr<Info::Profile::Button>(
parent,
std::move(text),
st.button));
button->addClickHandler(callback);
if (icon) {
Ui::CreateChild<Info::Profile::FloatingIcon>(
button,
*icon,
st.iconPosition);
}
const auto label = Ui::CreateChild<Ui::FlatLabel>(
button,
std::move(count),
st.label);
label->setAttribute(Qt::WA_TransparentForMouseEvents);
rpl::combine(
button->widthValue(),
label->widthValue()
) | rpl::start_with_next([=, &st](int outerWidth, int width) {
label->moveToRight(
st.labelPosition.x(),
st.labelPosition.y(),
outerWidth);
}, label->lifetime());
return button;
}
void ManagePeerBox::prepare() {
_channel->updateFull();
_peer->updateFull();
setTitle(ManagePeerTitle(_channel));
addButton(langFactory(lng_cancel), [this] { closeBox(); });
setTitle(ManagePeerTitle(_peer));
addButton(langFactory(lng_cancel), [=] { closeBox(); });
setupContent();
}
void ManagePeerBox::setupContent() {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
FillManageBox(App::wnd()->controller(), _channel, content);
if (const auto chat = _peer->asChat()) {
FillManageChatBox(App::wnd()->controller(), chat, content);
} else if (const auto channel = _peer->asChannel()) {
FillManageChannelBox(App::wnd()->controller(), channel, content);
}
setDimensionsToContent(st::boxWidth, content);
}

View File

@@ -9,11 +9,33 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
namespace style {
struct InfoProfileCountButton;
} // namespace style
namespace Ui {
class VerticalLayout;
} // namespace Ui
namespace Info {
namespace Profile {
class Button;
} // namespace Profile
} // namespace Info
class ManagePeerBox : public BoxContent {
public:
ManagePeerBox(QWidget*, not_null<ChannelData*> channel);
ManagePeerBox(QWidget*, not_null<PeerData*> peer);
static bool Available(not_null<ChannelData*> channel);
static bool Available(not_null<PeerData*> peer);
static Info::Profile::Button *CreateButton(
not_null<Ui::VerticalLayout*> parent,
rpl::producer<QString> &&text,
rpl::producer<QString> &&count,
Fn<void()> callback,
const style::InfoProfileCountButton &st,
const style::icon *icon = nullptr);
protected:
void prepare() override;
@@ -21,6 +43,6 @@ protected:
private:
void setupContent();
not_null<ChannelData*> _channel;
not_null<PeerData*> _peer;
};

View File

@@ -11,26 +11,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "styles/style_boxes.h"
PhotoCropBox::PhotoCropBox(QWidget*, const QImage &img, const PeerId &peer)
: _img(img)
, _peerId(peer) {
init(img, nullptr);
}
PhotoCropBox::PhotoCropBox(QWidget*, const QImage &img, not_null<PeerData*> peer)
: _img(img)
, _peerId(peer->id) {
init(img, peer);
}
void PhotoCropBox::init(const QImage &img, PeerData *peer) {
if (peerIsChat(_peerId) || (peer && peer->isMegagroup())) {
_title = lang(lng_create_group_crop);
} else if (peerIsChannel(_peerId)) {
_title = lang(lng_create_channel_crop);
} else {
_title = lang(lng_settings_crop_profile);
}
PhotoCropBox::PhotoCropBox(
QWidget*,
const QImage &img,
const QString &title)
: _title(title)
, _img(img) {
}
void PhotoCropBox::prepare() {

View File

@@ -11,8 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class PhotoCropBox : public BoxContent {
public:
PhotoCropBox(QWidget*, const QImage &img, const PeerId &peer);
PhotoCropBox(QWidget*, const QImage &img, not_null<PeerData*> peer);
PhotoCropBox(QWidget*, const QImage &img, const QString &title);
int32 mouseState(QPoint p);
@@ -28,7 +27,6 @@ protected:
void mouseMoveEvent(QMouseEvent *e) override;
private:
void init(const QImage &img, PeerData *peer);
void sendPhoto();
QString _title;
@@ -39,7 +37,6 @@ private:
QImage _img;
QPixmap _thumb;
QImage _mask, _fade;
PeerId _peerId = 0;
rpl::event_stream<QImage> _readyImages;
};

View File

@@ -8,14 +8,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/report_box.h"
#include "lang/lang_keys.h"
#include "styles/style_boxes.h"
#include "styles/style_profile.h"
#include "data/data_peer.h"
#include "boxes/confirm_box.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/toast/toast.h"
#include "mainwindow.h"
#include "styles/style_boxes.h"
#include "styles/style_profile.h"
namespace {

View File

@@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localstorage.h"
#include "storage/storage_media_prepare.h"
#include "mainwidget.h"
#include "history/history_media_types.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/tabbed_panel.h"
@@ -27,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/grouped_layout.h"
#include "ui/text_options.h"
#include "ui/special_buttons.h"
#include "data/data_document.h"
#include "media/media_clip_reader.h"
#include "window/window_controller.h"
#include "layout.h"
@@ -677,7 +677,7 @@ void SingleMediaPreview::preparePreview(
void SingleMediaPreview::prepareAnimatedPreview(
const QString &animatedPreviewPath) {
if (!animatedPreviewPath.isEmpty()) {
auto callback = [this](Media::Clip::Notification notification) {
auto callback = [=](Media::Clip::Notification notification) {
clipCallback(notification);
};
_gifPreview = Media::Clip::MakeReader(
@@ -896,10 +896,14 @@ rpl::producer<int> SingleFilePreview::desiredHeightValue() const {
return rpl::single(st::boxPhotoPadding.top() + h + st::msgShadow);
}
Fn<QString()> FieldPlaceholder(const Storage::PreparedList &list) {
return langFactory(list.files.size() > 1
? lng_photos_comment
: lng_photo_caption);
Fn<QString()> FieldPlaceholder(
const Storage::PreparedList &list,
SendFilesWay way) {
const auto isAlbum = (way == SendFilesWay::Album);
const auto compressImages = (way != SendFilesWay::Files);
return langFactory(list.canAddCaption(isAlbum, compressImages)
? lng_photo_caption
: lng_photos_comment);
}
} // namespace
@@ -1339,7 +1343,7 @@ SendFilesBox::SendFilesBox(
this,
st::confirmCaptionArea,
Ui::InputField::Mode::MultiLine,
FieldPlaceholder(_list),
nullptr,
caption) {
}
@@ -1353,7 +1357,7 @@ void SendFilesBox::initPreview(rpl::producer<int> desiredPreviewHeight) {
std::move(desiredPreviewHeight),
_footerHeight.value(),
_titleHeight + _1 + _2
) | rpl::start_with_next([this](int height) {
) | rpl::start_with_next([=](int height) {
setDimensions(
st::boxWideWidth,
std::min(st::sendMediaPreviewHeightMax, height));
@@ -1431,10 +1435,10 @@ void SendFilesBox::setupShadows(
}
void SendFilesBox::prepare() {
_send = addButton(langFactory(lng_send_button), [this] { send(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
setupCaption();
_send = addButton(langFactory(lng_send_button), [=] { send(); });
addButton(langFactory(lng_cancel), [=] { closeBox(); });
initSendWay();
setupCaption();
preparePreview();
boxClosing() | rpl::start_with_next([=] {
if (!_confirmed && _cancelledCallback) {
@@ -1468,7 +1472,8 @@ void SendFilesBox::initSendWay() {
: SendFilesWay::Photos;
}();
_sendWay = std::make_shared<Ui::RadioenumGroup<SendFilesWay>>(value);
_sendWay->setChangedCallback([this](SendFilesWay value) {
_sendWay->setChangedCallback([=](SendFilesWay value) {
updateCaptionPlaceholder();
applyAlbumOrder();
if (_albumPreview) {
_albumPreview->setSendWay(value);
@@ -1477,6 +1482,13 @@ void SendFilesBox::initSendWay() {
});
}
void SendFilesBox::updateCaptionPlaceholder() {
if (_caption) {
const auto sendWay = _sendWay->value();
_caption->setPlaceholder(FieldPlaceholder(_list, sendWay));
}
}
void SendFilesBox::refreshAlbumMediaCount() {
_albumVideosCount = _list.albumIsPossible
? ranges::count(
@@ -1505,7 +1517,6 @@ void SendFilesBox::preparePreview() {
void SendFilesBox::setupControls() {
setupTitleText();
setupSendWayControls();
_caption->setPlaceholder(FieldPlaceholder(_list));
}
void SendFilesBox::setupSendWayControls() {
@@ -1593,6 +1604,7 @@ void SendFilesBox::setupCaption() {
getDelegate()->outerContainer(),
_caption);
updateCaptionPlaceholder();
setupEmojiPanel();
}
@@ -1805,18 +1817,20 @@ void SendFilesBox::updateControlsGeometry() {
bottom - _caption->height());
bottom -= st::boxPhotoCaptionSkip + _caption->height();
_emojiToggle->moveToLeft(
(st::boxPhotoPadding.left()
+ st::sendMediaPreviewSize
- _emojiToggle->width()),
_caption->y() + st::boxAttachEmojiTop);
if (_emojiToggle) {
_emojiToggle->moveToLeft(
(st::boxPhotoPadding.left()
+ st::sendMediaPreviewSize
- _emojiToggle->width()),
_caption->y() + st::boxAttachEmojiTop);
}
}
const auto pointers = {
_sendAlbum.data(),
_sendPhotos.data(),
_sendFiles.data()
};
for (auto pointer : base::reversed(pointers)) {
for (const auto pointer : ranges::view::reverse(pointers)) {
if (pointer) {
pointer->moveToLeft(
st::boxPhotoPadding.left(),

View File

@@ -101,6 +101,7 @@ private:
void setupTitleText();
void updateBoxSize();
void updateControlsGeometry();
void updateCaptionPlaceholder();
bool canAddFiles(not_null<const QMimeData*> data) const;
bool canAddUrls(const QList<QUrl> &urls) const;

View File

@@ -25,13 +25,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text_options.h"
#include "chat_helpers/message_field.h"
#include "history/history.h"
#include "history/history_media_types.h"
#include "history/history_message.h"
#include "window/themes/window_theme.h"
#include "boxes/peer_list_box.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "data/data_channel.h"
#include "data/data_user.h"
#include "data/data_session.h"
#include "auth_session.h"
#include "messenger.h"
#include "core/application.h"
#include "styles/style_boxes.h"
#include "styles/style_history.h"
@@ -216,10 +218,10 @@ void ShareBox::prepare() {
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
_select->setQueryChangedCallback([=](const QString &query) {
onFilterUpdate(query);
applyFilterUpdate(query);
});
_select->setItemRemovedCallback([=](uint64 itemId) {
if (const auto peer = App::peerLoaded(itemId)) {
if (const auto peer = Auth().data().peerLoaded(itemId)) {
_inner->peerUnselected(peer);
selectedChanged();
update();
@@ -333,8 +335,8 @@ void ShareBox::peopleReceived(
switch (result.type()) {
case mtpc_contacts_found: {
auto &found = result.c_contacts_found();
App::feedUsers(found.vusers);
App::feedChats(found.vchats);
Auth().data().processUsers(found.vusers);
Auth().data().processChats(found.vchats);
_inner->peopleReceived(
query,
found.vmy_results.v,
@@ -404,7 +406,7 @@ void ShareBox::createButtons() {
addButton(langFactory(lng_cancel), [=] { closeBox(); });
}
void ShareBox::onFilterUpdate(const QString &query) {
void ShareBox::applyFilterUpdate(const QString &query) {
onScrollToY(0);
_inner->updateFilter(query);
}
@@ -490,7 +492,7 @@ ShareBox::Inner::Inner(
const auto dialogs = App::main()->dialogsList();
const auto self = Auth().user();
if (_filterCallback(self)) {
_chatsIndexed->addToEnd(App::history(self));
_chatsIndexed->addToEnd(self->owner().history(self));
}
for (const auto row : dialogs->all()) {
if (const auto history = row->history()) {
@@ -879,7 +881,7 @@ void ShareBox::Inner::changeCheckState(Chat *chat) {
if (!chat) return;
if (!_filter.isEmpty()) {
const auto history = App::history(chat->peer);
const auto history = chat->peer->owner().history(chat->peer);
auto row = _chatsIndexed->getRow(history);
if (!row) {
const auto rowsByLetter = _chatsIndexed->addToEnd(history);
@@ -965,7 +967,7 @@ void ShareBox::Inner::updateFilter(QString filter) {
if (toFilter) {
_filtered.reserve(toFilter->size());
for (const auto row : *toFilter) {
auto &nameWords = row->entry()->chatsListNameWords();
const auto &nameWords = row->entry()->chatListNameWords();
auto nb = nameWords.cbegin(), ne = nameWords.cend(), ni = nb;
for (fi = fb; fi != fe; ++fi) {
auto filterName = *fi;
@@ -1016,8 +1018,8 @@ void ShareBox::Inner::peopleReceived(
d_byUsernameFiltered.reserve(already + my.size() + people.size());
const auto feedList = [&](const QVector<MTPPeer> &list) {
for (const auto &data : list) {
if (const auto peer = App::peerLoaded(peerFromMTP(data))) {
const auto history = App::historyLoaded(peer);
if (const auto peer = Auth().data().peerLoaded(peerFromMTP(data))) {
const auto history = Auth().data().historyLoaded(peer);
if (!_filterCallback(peer)) {
continue;
} else if (history && _chatsIndexed->getRow(history)) {
@@ -1065,7 +1067,9 @@ QVector<PeerData*> ShareBox::Inner::selected() const {
QString AppendShareGameScoreUrl(const QString &url, const FullMsgId &fullId) {
auto shareHashData = QByteArray(0x10, Qt::Uninitialized);
auto shareHashDataInts = reinterpret_cast<int32*>(shareHashData.data());
auto channel = fullId.channel ? App::channelLoaded(fullId.channel) : static_cast<ChannelData*>(nullptr);
auto channel = fullId.channel
? Auth().data().channelLoaded(fullId.channel)
: static_cast<ChannelData*>(nullptr);
auto channelAccessHash = channel ? channel->access : 0ULL;
auto channelAccessHashInts = reinterpret_cast<int32*>(&channelAccessHash);
shareHashDataInts[0] = Auth().userId();
@@ -1168,17 +1172,19 @@ void ShareGameScoreByHash(const QString &hash) {
});
};
auto channel = channelId ? App::channelLoaded(channelId) : nullptr;
const auto channel = channelId
? Auth().data().channelLoaded(channelId)
: nullptr;
if (channel || !channelId) {
resolveMessageAndShareScore(channel);
} else {
auto requestChannelIds = MTP_vector<MTPInputChannel>(1, MTP_inputChannel(MTP_int(channelId), MTP_long(channelAccessHash)));
auto requestChannel = MTPchannels_GetChannels(requestChannelIds);
MTP::send(requestChannel, rpcDone([channelId, resolveMessageAndShareScore](const MTPmessages_Chats &result) {
if (auto chats = Api::getChatsFromMessagesChats(result)) {
App::feedChats(*chats);
}
if (auto channel = App::channelLoaded(channelId)) {
MTP::send(requestChannel, rpcDone([=](const MTPmessages_Chats &result) {
result.match([](const auto &data) {
Auth().data().processChats(data.vchats);
});
if (const auto channel = Auth().data().channelLoaded(channelId)) {
resolveMessageAndShareScore(channel);
}
}));

View File

@@ -60,7 +60,7 @@ private:
void scrollTo(Ui::ScrollToRequest request);
void needSearchByUsername();
void onFilterUpdate(const QString &query);
void applyFilterUpdate(const QString &query);
void selectedChanged();
void createButtons();
int getTopScrollSkip() const;

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