Compare commits

...

329 Commits

Author SHA1 Message Date
John Preston
275327c789 Version 5.0.3.
- Ctrl+Click on Reply in menu to Reply in another chat.
- Allow Zero-Width-Space character in text rendering.
- Fix creating custom links in the message field.
- Fix jump to the topic with last unread message.
- Fix newline entering via Shift+Enter on Linux.
- Fix forum search open by Ctrl+F.
2024-05-28 11:53:51 +04:00
John Preston
72471c74d0 Limit symbols that allow diacritics after them. 2024-05-28 11:45:21 +04:00
John Preston
b9f63f80f1 Reply-in-another-chat by Ctrl+Click on Reply. 2024-05-28 10:59:46 +04:00
John Preston
32cd2120ac Allow zero width space in the middle of text.
Fixes #6959.
2024-05-28 10:54:20 +04:00
John Preston
7db9abf725 Fix forum search open by Ctrl+F. 2024-05-28 10:04:57 +04:00
John Preston
f09a939a7c Improve fix for markup. 2024-05-27 22:24:19 +04:00
John Preston
f1c1c900bf Fix newline regression on Linux. 2024-05-27 22:08:20 +04:00
John Preston
a43143d01c Separate ForceDisable from LastCheckCrashed. 2024-05-27 17:54:23 +04:00
John Preston
7357b40ba1 Fix build with updated lib_ui. 2024-05-27 17:54:23 +04:00
Ilya Fedin
ad6e34f3a4 Update lib_webview 2024-05-27 14:00:26 +04:00
John Preston
daf30dcab8 Fix jump to last topic in case of unread topic click. 2024-05-27 13:33:25 +04:00
Ilya Fedin
0033ad749f Update lib_webview 2024-05-27 08:06:25 +04:00
Ilya Fedin
62c249015d Update lib_webview 2024-05-26 14:20:43 +04:00
John Preston
31fcca245f Version 5.0.2: Fix IME-to-search on macOS. 2024-05-24 20:54:10 +04:00
John Preston
f4864cddc9 Fix possible crash in Settings of crashed web-app.
Fixes #27943.
2024-05-24 20:54:10 +04:00
John Preston
36db1105a2 Fix build with Xcode. 2024-05-24 16:09:01 +04:00
John Preston
364f62660c Version 5.0.2: Update the version number. 2024-05-24 15:26:18 +04:00
John Preston
b67a1bca49 Version 5.0.2.
- Toggle chats search focus by Tab key.
- Unfocus empty search by Backspace key.
- Add a spoiler to already sent media when editing.
- Forward by drag-n-drop to frequent contacts / recent searches.
- Allow wide chats list in empty window
with narrow list when a chat is opened.
- Improve custom font support in the input fields.
- Fix input method typing from unfocused search.
- Fix possible media reordering on batch send.
- Fix crash in group emoji set saving.
- Fix possible hang on quit on macOS.
2024-05-24 15:13:04 +04:00
John Preston
000911546b Fix usernames drop on min user being received. 2024-05-24 14:50:00 +04:00
John Preston
e26b8e7ebc Fix passcode lock glitch with new search. 2024-05-24 14:14:29 +04:00
John Preston
f03935ad9a Kill hang processes in case of app quitting. 2024-05-24 14:08:14 +04:00
John Preston
d91b75b8f8 Allow wide chats in empty window. 2024-05-24 12:55:48 +04:00
John Preston
4d4cf472c8 Fix crash in group emoji set saving. 2024-05-24 12:55:48 +04:00
John Preston
685c7a865e Use 64 iterations (like OpenSSL 3 for 2048 primes). 2024-05-24 12:55:48 +04:00
John Preston
0551550e61 Don't always open last topic in forums. 2024-05-24 12:55:48 +04:00
John Preston
c3fab28e1c Improve scroll style for limited reaction selector. 2024-05-24 12:55:48 +04:00
John Preston
74db2167f9 Allow copy custom emoji from panel. 2024-05-24 12:55:48 +04:00
John Preston
cedd8fc546 Fix incorrect replace media button show in reply. 2024-05-24 12:55:48 +04:00
John Preston
db486e957c Fix group/channel Appearance button style. 2024-05-24 12:55:48 +04:00
John Preston
1c6f45d8d3 Fix drag-n-drop to frequent/recent contacts. 2024-05-24 12:55:48 +04:00
John Preston
ba8bb2d50f Add .vscode to .gitignore. 2024-05-24 12:55:48 +04:00
John Preston
ec6d6a78cf Tab toggles search focus, Backspace doesn't focus. 2024-05-24 12:55:48 +04:00
John Preston
f10f3c08de Attempt to fix sending messages to broken topics. 2024-05-24 12:55:48 +04:00
John Preston
39c1da6642 Attempt to fix jump to unread in some cases. 2024-05-24 12:55:48 +04:00
John Preston
84ec2a5f74 Possibly fix wrong requests order. 2024-05-22 21:21:03 +04:00
Ilya Fedin
5e3e9ba824 Ensure Qt doesn't prevent quit due to ignored widget close events 2024-05-22 16:47:08 +04:00
Ilya Fedin
198528f79f Remove code needed for a removed patch 2024-05-21 14:20:43 +04:00
Ilya Fedin
8a5ad581b8 Update Qt to 6.7.1 on Linux 2024-05-21 14:20:43 +04:00
Ilya Fedin
7dbf8ca1f4 Remove unneeded Qt::WA_InputMethodEnabled
The widget already had it
2024-05-21 14:10:47 +04:00
dependabot[bot]
df808df8d3 Bump actions/upload-artifact from 2 to 4
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-21 09:16:03 +04:00
Ilya Fedin
4fddaef112 Update lib_webview 2024-05-20 14:25:23 +04:00
John Preston
2bdc204c47 Fix geometry applying in webview windows. 2024-05-20 14:25:23 +04:00
Ilya Fedin
6734f99ba8 Update lib_ui & patches on Linux 2024-05-19 20:30:13 +04:00
Ilya Fedin
af4e0178d0 Ensure window flags are set before calling SeparatePanel::setInnerSize
Setting window flags may lead to native window being re-created
2024-05-19 20:30:13 +04:00
Ilya Fedin
65b62485be Forward input method event from Dialogs::Widget to search field 2024-05-19 11:47:16 +04:00
Ilya Fedin
98b58c1168 Add missing binds for OpenGL shaders and buffers 2024-05-19 09:55:34 +04:00
Ilya Fedin
ad13048cdc Update submodules 2024-05-16 22:20:08 +04:00
Ilya Fedin
a753c1515b Remove unneeded wayland-client dependency from telegram part in snap 2024-05-16 22:20:08 +04:00
Ilya Fedin
bd48e2fff2 Fix more package names in snap 2024-05-16 22:20:08 +04:00
John Preston
88ad1be4c3 Try to fix custom fonts in the input field. 2024-05-14 19:36:52 +04:00
John Preston
1f31d8032f Bring chosen font to the top of the box. 2024-05-14 19:36:52 +04:00
John Preston
b5f5aed119 Fix search focus in a hidden window on launch.
Fixes #27853.
2024-05-14 19:36:52 +04:00
John Preston
213326a212 Update submodules. 2024-05-14 19:36:52 +04:00
23rd
264e35a6a8 Fixed rare crash in photo editor when editing media in message. 2024-05-14 17:19:39 +03:00
23rd
76137ca1cf Fixed link to premium section in box about sponsored messages. 2024-05-14 17:19:39 +03:00
23rd
e4323ecd26 Slightly improved code style in processing of api text entities. 2024-05-14 17:19:39 +03:00
23rd
b406e2b99b Fixed sender name for voices in overview layout when sender is hidden. 2024-05-14 17:19:39 +03:00
23rd
c1c49e8ddc Added captions to voice messages in overview layout. 2024-05-14 17:19:39 +03:00
23rd
41e414af37 Removed transcribe buttons from scheduled messages. 2024-05-14 17:19:39 +03:00
23rd
2afb69e787 Fixed text state of reply bubble for messages with unwrapped media. 2024-05-14 17:19:39 +03:00
23rd
3e7d4af4a0 Fixed reply bubble width for short quotes. 2024-05-14 17:19:39 +03:00
23rd
bc11c66777 Added ability to hide single sponsored message to premium users. 2024-05-14 17:19:39 +03:00
23rd
7a95b38d3c Fixed refreshing of file reference in api media spoiler editing module. 2024-05-14 17:19:39 +03:00
23rd
334fd4e951 Added ability to edit media with spoiled one without resending. 2024-05-14 17:19:39 +03:00
GoodDok
923e3ee808 Fix 25202: surrounding $PWD with double quotes
Added double quotes to work correctly with spaces in current directory's name
2024-05-14 14:43:38 +04:00
Robert-André Mauchin
3358673ba4 Add compatibility with FFMPEG 7.0
avio: Constify data pointees of write callbacks

avutil: remove deprecated FF_API_PKT_DURATION

Ref: 2a68d945cd
     02aea61d69
     b8fef7e9c5
2024-05-14 14:39:29 +04:00
dependabot[bot]
d28b9f10b4 Bump jlumbroso/free-disk-space from 1.3.0 to 1.3.1
Bumps [jlumbroso/free-disk-space](https://github.com/jlumbroso/free-disk-space) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/jlumbroso/free-disk-space/releases)
- [Commits](f68fdb76e2...54081f1387)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-14 14:38:48 +04:00
Ilya Fedin
6ebbca58e8 Track major version for official GitHub helpers in actions 2024-05-14 09:54:25 +04:00
Ilya Fedin
15697f4f2b Fix libpng package name in snap 2024-05-08 12:10:37 +04:00
John Preston
2cb0651b04 Redirect IME to search on Windows. 2024-05-07 22:17:34 +04:00
John Preston
1f3a3ec04b Don't focus search when forum is opened. 2024-05-07 22:17:34 +04:00
John Preston
c1a0172822 De-focus empty search by backspace. 2024-05-07 22:17:33 +04:00
GkvJwa
593e76479c Use jom build openssl on windows 2024-05-07 09:52:32 +04:00
dependabot[bot]
5d622b906e Bump ilammy/msvc-dev-cmd from 1.12.0 to 1.13.0
Bumps [ilammy/msvc-dev-cmd](https://github.com/ilammy/msvc-dev-cmd) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/ilammy/msvc-dev-cmd/releases)
- [Commits](https://github.com/ilammy/msvc-dev-cmd/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: ilammy/msvc-dev-cmd
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-07 09:02:31 +04:00
dependabot[bot]
7e253314ec Bump jinja2 from 3.1.3 to 3.1.4 in /Telegram/build/docker/centos_env
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.3 to 3.1.4.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.3...3.1.4)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 23:29:00 +04:00
dependabot[bot]
78a05768d8 Bump dessant/lock-threads from 3 to 5
Bumps [dessant/lock-threads](https://github.com/dessant/lock-threads) from 3 to 5.
- [Release notes](https://github.com/dessant/lock-threads/releases)
- [Changelog](https://github.com/dessant/lock-threads/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dessant/lock-threads/compare/v3...v5)

---
updated-dependencies:
- dependency-name: dessant/lock-threads
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 23:28:48 +04:00
dependabot[bot]
ddf6514a90 Bump actions/stale from 5 to 9
Bumps [actions/stale](https://github.com/actions/stale) from 5 to 9.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v5...v9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 20:40:31 +04:00
dependabot[bot]
3847b97f80 Bump actions/cache from 3.0.11 to 4.0.2
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.11 to 4.0.2.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.11...v4.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 20:40:18 +04:00
dependabot[bot]
046bc03037 Bump actions/checkout from 3.1.0 to 4.1.4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.1.0 to 4.1.4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3.1.0...v4.1.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 20:40:02 +04:00
Ilya Fedin
7ff5f81b88 Try automatic actions update 2024-05-06 09:46:18 +04:00
John Preston
fb444e7dd4 Version 5.0.1.
- Fix several crashes in new interfaces.
- Fix monospace font size.
- Fix possible problem with underline font size.
2024-05-04 19:08:30 +04:00
23rd
c4f0db5bc5 Fixed possible crash on slow resolving message from personal channel. 2024-05-04 18:13:30 +04:00
23rd
a866ba8cdd Fixed crash on restricting users from moderation box. 2024-05-04 18:13:28 +04:00
John Preston
129d356909 Fix scrolling expanded frequent contacts. 2024-05-04 18:13:04 +04:00
Ilya Fedin
7289292f02 Update lib_ui & patches on Linux 2024-05-04 14:05:09 +04:00
Ilya Fedin
638e88ec79 Revert "Use latest mesa in snap"
This reverts commit b246328dcf.
2024-05-04 14:05:09 +04:00
John Preston
19ead551d9 Fix a typo bug in replies section scrolling. 2024-05-03 21:04:06 +04:00
John Preston
fccaa587e1 Fix adjustment of size for modified fonts. 2024-05-03 21:04:06 +04:00
23rd
1709fcd0df Guarded personal channel info from possible crash on deleting message. 2024-05-03 21:04:06 +04:00
23rd
de1f222bac Fixed rescheduling of selected and grouped scheduled messages. 2024-05-03 21:04:06 +04:00
Ilya Fedin
5e1f0e9f7f Move waiting for answer action 30 minutes later than lock threads 2024-05-03 19:59:24 +04:00
Alan Pope
e3a14d432b Be consistent with libgtk-3-0t64
The reason it worked without explicitly updating this one is because the deb of libgtk-3-0t64 has a 'replaces' line for libgtk-3-0 - which nullifies this entry, as both were specified. But, you're right, looks better to have the right package in there.
2024-05-03 19:59:07 +04:00
Alan Pope
2b013c108f Fixes for unable to find gtk-query-immodules-3.0
The package has been updated for 2038 t64 update. This fixes that. 

Successfully build telegram 5.0.0 in arm64 after the fixes in this PR.
2024-05-03 19:59:07 +04:00
Alan Pope
0194687181 fix: Incorrect package name
Noble has libvpx9, not vpx8.

https://packages.ubuntu.com/search?keywords=libvpx9
2024-05-03 19:59:07 +04:00
John Preston
bb47f4659d Don't adjust monospace font size. 2024-05-02 19:36:59 +04:00
John Preston
2b580d29ae Add .qoi to list of image extensions. 2024-05-02 17:17:40 +04:00
John Preston
b7e52119bc Add .jxl to the list of image extensions. 2024-05-02 16:35:29 +04:00
John Preston
fd633ecb49 Version 5.0.
- Choose custom font family in Settings > Chat settings > Font family.
- Show "Frequent contacts" when you focus the search field.
- Show "Recent chats" when you focus the search field.
- Show "Channels" list and similar channels.
- Premium users can use animated emoji in polls.
- Group admins can mass-moderate many messages.
- Fix frequent crashes on some Linux systems.
2024-05-02 14:11:20 +04:00
John Preston
9c77346f36 Fix build with Xcode and GCC. 2024-05-02 14:11:20 +04:00
John Preston
641e74763f Revert non-production-ready prepare.py changes.
Accidentally commited in b9b7d9e337.
2024-05-02 12:17:57 +04:00
John Preston
2e71427427 Improve font size edge cases. 2024-05-02 12:05:46 +04:00
John Preston
95ec0633ed Use custom-adjusted font metrics for custom fonts.
Fixes #27814.
2024-05-02 11:33:47 +04:00
23rd
14e8b8fb91 Fixed spam reporting in moderation box. 2024-05-02 04:09:25 +03:00
23rd
d40951f068 Added ability to moderate non-users to moderation box. 2024-05-02 04:09:25 +03:00
23rd
ab85d18cc8 Added support of personal channel with message. 2024-05-02 04:09:25 +03:00
23rd
6becaaa953 Slightly improved view style of giveaway results in dialogs list. 2024-05-02 04:09:25 +03:00
23rd
26e8c29f40 Moved formatting of dialog last date to td_ui. 2024-05-02 04:09:25 +03:00
John Preston
47800ee02d Improve font size selection a bit. 2024-05-01 19:09:54 +04:00
John Preston
12a24dd473 Fix possible crash in common groups list.
Currently Data::Session::processChat() may change adminRights(),
that may change Data::CanSendAnyOf(peer, ...), that may lead to
Window::SessionController::updateThirdColumnToCurrentChat() call,
that destroys current third column to replace it with another one.

If common groups list was opened in the third column this will crash.

Fixes #27640.
2024-05-01 17:52:40 +04:00
23rd
ad6321d3ae Fixed possible crash in moderate box. 2024-05-01 14:10:37 +03:00
John Preston
7e071c770f Fix possible crash with custom wallpapered peers. 2024-05-01 14:38:06 +04:00
John Preston
a62d1dfa63 Focus chats search by Ctrl[Cmd]+F. 2024-05-01 14:38:05 +04:00
John Preston
b3eb1dbc14 Revert "Toggle search focus by escape."
This reverts commit 72d5a9b3e0.
2024-05-01 14:38:05 +04:00
Ilya Fedin
3170a45158 Increase snapcraft verbosity 2024-05-01 14:06:23 +04:00
John Preston
32483fa13b Just focus the search field on Space. 2024-05-01 12:57:47 +04:00
John Preston
9166a1c3a6 Update Linux Qt patches. Hope fixes crashes. 2024-05-01 12:57:07 +04:00
GitHub Action
6fa0afff37 Update User-Agent for DNS to Chrome 124.0.0.0. 2024-05-01 12:47:51 +04:00
Ilya Fedin
bc649af941 Schedule no-response action runs at different times
To avoid rate limits
2024-05-01 12:47:42 +04:00
John Preston
16ce5ef046 Greeting category first in ChatIntro setup. 2024-05-01 12:47:20 +04:00
John Preston
bb6fd4bc4d Update scheme, new sticker categories. 2024-05-01 12:47:20 +04:00
John Preston
63e1731d7c Fix premium preview bullet-dots.
Regression was introduced in 88751896af.
2024-05-01 12:47:20 +04:00
John Preston
b9b7d9e337 Implement live location view. 2024-05-01 12:47:20 +04:00
23rd
ef8c07e6eb Added ability to translate selected text in profile section. 2024-04-30 17:35:34 +03:00
23rd
20a13663a6 Slightly improved style of rpl text mapping. 2024-04-30 17:35:04 +03:00
23rd
2f0aa6ef05 Removed state sending of low level of battery to call participant. 2024-04-30 16:49:17 +03:00
23rd
62bd1354dc Replaced box for leaving or deleting chat with generic box. 2024-04-28 20:14:26 +03:00
23rd
be255f1d09 Added to moderate box number of all messages that will be deleted. 2024-04-28 20:14:26 +03:00
23rd
b8b02b2285 Replaced list of history items for sending now with sorted by date. 2024-04-28 13:33:04 +03:00
23rd
e0db0642bd Fixed crash when deleting message from non-user. 2024-04-27 16:39:13 +03:00
23rd
e81465a54e Added Enter shortcut to moderation box for confirmation. 2024-04-27 13:31:00 +03:00
23rd
6e67cfc7be Fixed possible crash in moderation box. 2024-04-27 12:59:36 +03:00
John Preston
c0db5ee98a Beta version 4.16.10: Fix GCC build. 2024-04-26 23:41:28 +04:00
John Preston
372b3da09c Beta version 4.16.10.
- Group admins can mass-moderate many messages.
- Premium users can use animated emoji in polls.
- Revert the default "Open Sans" font to 1.10.
- Several crash fixes and small improvements.
2024-04-26 20:55:06 +04:00
John Preston
79532954dc Allow a bit more font size adjusting. 2024-04-26 20:18:30 +04:00
23rd
aff2be605e Removed item for poll creation from menu when it is impossible. 2024-04-26 19:15:03 +03:00
John Preston
363c191a6e Skip media bottom skip in IV. 2024-04-26 20:12:30 +04:00
John Preston
2949cdab61 Don't request IV two times in a row. 2024-04-26 20:12:29 +04:00
John Preston
7addcf2d25 Add IV footer. 2024-04-26 20:12:29 +04:00
John Preston
a272807a99 Remove "Create poll" button in Replies chat.
Fixes #27817.
2024-04-26 20:12:29 +04:00
23rd
c803603de4 Added ability to insert custom emoji to polls. 2024-04-26 20:12:29 +04:00
23rd
e6c22ec1ca Added api support for custom emoji in polls. 2024-04-26 20:12:29 +04:00
John Preston
b3ae843f0e Update API scheme to layer 179. 2024-04-26 20:12:29 +04:00
John Preston
12a78c1f45 Allow pasting text to the unfocused search. 2024-04-26 20:12:29 +04:00
23rd
612b81ee87 Fixed allowed reactions for channel posts in linked chats. 2024-04-26 20:12:29 +04:00
23rd
e5b91d2f3d Added entry points for moderation box. 2024-04-26 20:12:29 +04:00
23rd
82293c98eb Added arrow icon to divider link in moderation box. 2024-04-26 20:12:29 +04:00
23rd
629da68cfc Added api support to moderation box. 2024-04-26 20:12:29 +04:00
23rd
643ecd2c2c Implemented initial ui of moderation box. 2024-04-26 20:12:29 +04:00
Ilya Fedin
dd1cb00c62 Update snap to core24 2024-04-26 18:39:56 +04:00
23rd
2c4d8418c1 Removed unused code for sponsored messages. 2024-04-26 11:21:41 +04:00
23rd
9fcb5d6f31 Added emoji button to input field for peer title edit. 2024-04-26 11:21:41 +04:00
23rd
38fc6bfbb9 Added special mode to emoji list widget to exclude non-unicode emoji. 2024-04-26 11:21:41 +04:00
23rd
804991a69c Added universal duration to styles. 2024-04-26 11:21:41 +04:00
23rd
7388f46adf Fixed display of subscribers count in dialogs suggestions. 2024-04-26 11:21:41 +04:00
John Preston
10c427127e Fix accidental search focusing. 2024-04-26 11:10:12 +04:00
John Preston
c6d034174b Support separate webview storages. 2024-04-26 08:58:42 +04:00
Ilya Fedin
471831bcd6 Revert "Remove not really needed notification capability checks"
This reverts commit abdfa4f785.
2024-04-25 15:07:51 +04:00
Ilya Fedin
b1e64419a5 Fix setting application icon in Linux notifications 2024-04-25 11:31:43 +04:00
Ilya Fedin
e2f17f1131 Remove unneeded includes from notifications_manager_linux 2024-04-25 11:31:43 +04:00
John Preston
ae4a73a15b Rollback OpenSans to 1.10. 2024-04-24 22:20:22 +04:00
Kolya
a84ca00270 fix typo 2024-04-24 19:18:33 +04:00
John Preston
c16d820b88 Move OpenSSL probing before crash reporter. 2024-04-24 19:12:46 +04:00
John Preston
ef614150d5 Remove dead code in lib_ui. 2024-04-24 17:44:51 +04:00
John Preston
b46ca1ec17 Use different font fallback algo on Windows. 2024-04-24 17:44:40 +04:00
John Preston
d5a347ede7 Try cancelling the new search by mouse back button. 2024-04-24 17:41:12 +04:00
John Preston
72d5a9b3e0 Toggle search focus by escape. 2024-04-24 16:56:20 +04:00
John Preston
e6d72b4861 Un-focus chats search on empty history click. 2024-04-24 16:54:10 +04:00
John Preston
3da51b1bc9 Fix crash on quit in sponsored messages. 2024-04-24 16:37:27 +04:00
John Preston
17b7db6219 Request 64 frequent contacts instead of 32. 2024-04-24 16:37:27 +04:00
John Preston
8353180161 Fix phrase cut-off in QR login. 2024-04-24 16:37:27 +04:00
John Preston
f675a8dcf7 Attempt to fix a couple of crashes. 2024-04-24 16:37:27 +04:00
John Preston
493f1d69e2 Fix cancel search button overlap. 2024-04-24 16:37:27 +04:00
John Preston
331e8c3ec6 Beta version 4.16.9.
- Show "Frequent contacts" when you focus the search field.
- Show "Recent chats" when you focus the search field.
- Show "Channels" and channel recommendations.
- Allow changing font in Settings > Chat settings > Font family.
2024-04-23 23:14:19 +04:00
John Preston
56bce70558 Fix "long time ago" for users who blocked me. 2024-04-23 21:47:57 +04:00
John Preston
d82e48f8e4 Improve ChooseFontBox navigation. 2024-04-23 19:17:42 +04:00
John Preston
97ecc57be8 Add choose font settings. 2024-04-23 19:17:42 +04:00
John Preston
25bd2b145b Update submodules. 2024-04-23 19:17:42 +04:00
John Preston
7111c92ae7 Fix keyboard navigation in top peers. 2024-04-23 19:17:42 +04:00
John Preston
e066cf1589 Add "Show all" for top peers. 2024-04-23 19:17:41 +04:00
John Preston
6f328b2ef8 Fix inline query draft applying. 2024-04-23 19:17:41 +04:00
23rd
7a6c55bd8a Fixed color of icon for replies with media story. 2024-04-23 19:17:41 +04:00
23rd
b35d3f57fe Slightly improved style of sub-button in sponsored messages. 2024-04-23 19:17:41 +04:00
23rd
eef65af173 Slightly simplified meson usage on macOS in prepare.py. 2024-04-23 19:17:41 +04:00
23rd
bea715b41c Added ICCv4 with Little CMS on macOS. 2024-04-23 19:17:41 +04:00
23rd
7be68ca82c Updated Qt to 6.2.8 on macOS. 2024-04-23 19:17:41 +04:00
John Preston
287d5a7413 Fix shadow below Chats/Channels. 2024-04-23 19:17:41 +04:00
John Preston
1448cea01c Fix ripple in active recommendation row. 2024-04-23 19:17:41 +04:00
John Preston
768e8b457b Improve second-time choose of toppeers. 2024-04-23 19:17:41 +04:00
John Preston
05474f4a3f Improve filter switching with toppeers. 2024-04-23 19:17:41 +04:00
John Preston
8acada9b0f Fix toppeers animation with expanded stories. 2024-04-23 19:17:41 +04:00
23rd
a86e7f035f Slightly improved style of inner box for channel earn history entries. 2024-04-23 19:17:41 +04:00
John Preston
9caff93c35 Fix build for Xcode. 2024-04-23 19:13:48 +04:00
John Preston
e6ba85e112 Keyboard navigation in channels/recommendations. 2024-04-23 19:13:48 +04:00
John Preston
046803dbed Fix rotation reading in FFmpeg. 2024-04-23 19:13:48 +04:00
John Preston
615f4b1d1c Ignore pageBlockUnsupported blocks. 2024-04-23 19:13:48 +04:00
John Preston
a11535806d Always show cancel search when focused. 2024-04-23 19:13:48 +04:00
John Preston
34a7169b4f Make unread badges on top peers nicer. 2024-04-23 19:13:47 +04:00
John Preston
4f365c73ad Support active selection in recommendations. 2024-04-23 19:13:47 +04:00
John Preston
705bd9693d Load recommendations on demand. 2024-04-23 19:13:47 +04:00
John Preston
a88770a8ec Don't close recommendations. 2024-04-23 19:13:47 +04:00
John Preston
da423b5bd2 Add a general FastShareLink method. 2024-04-23 19:13:47 +04:00
John Preston
56080bd0e4 Fix boost group link handling for forums. 2024-04-23 19:13:47 +04:00
John Preston
63f66a1369 Show "Boost" button in boost group links. 2024-04-23 19:13:47 +04:00
John Preston
ca1a30196e Fix LinkButton resizing. 2024-04-23 19:13:47 +04:00
John Preston
b2d8e2a1e6 Initial version of channels/recommendations. 2024-04-23 19:13:47 +04:00
John Preston
2ab8bb13c5 Fix links preview. 2024-04-23 19:13:47 +04:00
23rd
3a2c5c6d0a Increased size of stickerset preview in webpages. 2024-04-23 19:13:47 +04:00
23rd
ed13a325e9 Added stickerset preview to webpages. 2024-04-23 19:13:47 +04:00
23rd
f43f99cff2 Added initial api support of stickerset attribute in web pages. 2024-04-23 19:13:47 +04:00
John Preston
a3b8397361 Close chat/media if thrown out by admin. 2024-04-23 19:13:47 +04:00
John Preston
4fb03e532c Optimize stories list generation. 2024-04-23 19:13:47 +04:00
John Preston
645ad5e1bd Update pinned icon in stories list. 2024-04-23 19:13:47 +04:00
John Preston
9036e9e8e3 Show pinned icon in stories. 2024-04-23 19:13:47 +04:00
John Preston
468d8b04d6 Implement stories pin-to-top. 2024-04-23 19:13:47 +04:00
John Preston
4b98ab1246 Fix build with simplified ads. 2024-04-23 19:13:46 +04:00
John Preston
d33e3dc13a Rename Story::pinned to Story::inProfile. 2024-04-23 19:13:46 +04:00
23rd
df16e7c00b Added ability to change reactions limit in channels. 2024-04-23 19:13:46 +04:00
23rd
225c0e0af3 Added initial api support of reactions limit in channels and groups. 2024-04-23 19:13:46 +04:00
23rd
5543927042 Added ability to enable sponsored messages for premium self. 2024-04-23 19:13:46 +04:00
23rd
72b274a2bf Added support of simplified constructor for MTP sponsored message. 2024-04-23 19:13:46 +04:00
John Preston
ac15990b48 Update API scheme to layer178, ads broke build. 2024-04-23 19:13:46 +04:00
John Preston
7387dfdd9c Don't create top/recent peers unnecessary. 2024-04-23 19:13:46 +04:00
John Preston
74a7e7d1b4 Fix non-intentional chats list search focus. 2024-04-23 19:13:46 +04:00
John Preston
a0a9de1d18 Cancel search on switching folders. 2024-04-23 19:13:46 +04:00
John Preston
360366ba9e Fix opening forums from recent peers. 2024-04-23 19:13:46 +04:00
John Preston
0180fe9468 Animate search suggestions. 2024-04-23 19:13:46 +04:00
John Preston
19f5d95a3c Implement keyboard navigation for recent peers. 2024-04-23 19:13:46 +04:00
John Preston
051ca51d3b Allow clearing search results. 2024-04-23 19:13:46 +04:00
John Preston
2a6ff9203b Implement basic recent search results. 2024-04-23 19:13:46 +04:00
John Preston
2e0529bd9a Add data component for RecentPeers. 2024-04-23 19:13:46 +04:00
John Preston
e24ab4f1ab Allow saving emoji to files in debug builds. 2024-04-23 19:13:46 +04:00
John Preston
3dbadeb232 Allow opening top peers in a new window. 2024-04-23 19:13:46 +04:00
John Preston
4cdd939028 Cache topPeers locally. 2024-04-23 19:13:46 +04:00
John Preston
c11f4efc5c Support disabling top peers completely. 2024-04-23 19:13:46 +04:00
John Preston
e1c21b908c Add keyboard navigation to top peers. 2024-04-23 19:13:46 +04:00
John Preston
a6c1def6fe Add ripples to top peers. 2024-04-23 19:13:46 +04:00
John Preston
19ae76d8de Top peers context menu. 2024-04-23 19:13:46 +04:00
John Preston
56e28feb00 Top peer realtime badges. 2024-04-23 19:13:45 +04:00
John Preston
b259c566b7 Top peer unread badges and online indicators. 2024-04-23 19:13:45 +04:00
John Preston
18598f8dca Initial version of top peers. 2024-04-23 19:13:45 +04:00
John Preston
11e4c45969 Don't collapse chats list on window unfocus. 2024-04-23 19:13:45 +04:00
John Preston
39658ffe52 Allow narrow chats width without main section. 2024-04-23 19:13:45 +04:00
John Preston
2f03b9aa29 Make dialogs filter onfocused by default. 2024-04-23 19:13:45 +04:00
John Preston
dc438cff23 Always export file_name in json. 2024-04-23 19:13:45 +04:00
John Preston
18f5521be5 Don't look for SDL when building on macOS. 2024-04-23 19:13:45 +04:00
Ilya Fedin
bae9af1076 Switch crash reporter to system fonts
This should prevent nested crashes when loading custom fonts
2024-04-23 11:01:19 +04:00
Ilya Fedin
8d2805f226 Set font weight in crash reporter 2024-04-23 10:10:43 +04:00
Ilya Fedin
0ad18c8182 Get rid of direct Wayland usage
This should get rid of a very nasty vector of misbehaving and quite a lot of code which is boilerplate over Wayland protocols at the price of losing support of niche features

Most of things served by xcb code on X11 is served by portal code on Wayland, hopefully this tendention will continue and we will be fine just with glib which provides D-Bus and other basic APIs
2024-04-22 11:04:02 +04:00
Ilya Fedin
9046b2cafb Stop using the plasma-shell protocol
Looks like this isn't really correct in combination with xdg-shell
2024-04-22 10:59:16 +04:00
Ilya Fedin
3d994b58a0 ShowXDPOpenWithDialog -> UnsafeShowOpenWith 2024-04-21 03:14:34 +04:00
Ilya Fedin
4310c4978e Fix macOS packaged action 2024-04-19 17:41:07 +04:00
23rd
01bfa46729 Fixed display of default box for message schedule in correspond window. 2024-04-18 11:57:25 +04:00
Kolya
3510ca7184 Don't use/link libsharpyuv in libavif 2024-04-18 09:23:15 +04:00
Ilya Fedin
8d7845daa1 Use --shallow-submodules in Docker 2024-04-18 09:13:37 +04:00
Ilya Fedin
cb5fdac0da Replace mozjpeg with jpegli on Linux 2024-04-17 23:45:15 +04:00
John Preston
78c8d0562b Version 4.16.8: Fix build on Linux. 2024-04-16 23:23:11 +04:00
John Preston
bb0280f096 Revert "Replace mozjpeg with jpegli in snap"
This reverts commit f57e2edf2a.
2024-04-16 23:22:55 +04:00
John Preston
566b53ce9f Revert "Clean remainings of mozjpeg in snapcraft.yml"
This reverts commit da1909cc1d.
2024-04-16 23:22:43 +04:00
John Preston
ec95db0945 Revert "Replace mozjpeg with jpegli in Docker"
This reverts commit a1e2e3d011.
2024-04-16 23:00:08 +04:00
John Preston
a0d7d07543 Version 4.16.8.
- Fix in-app playing of some video and audio files.
- Fix crash on Linux opening chats with custom backgrounds.
- Fix crash on quit after using scheduled messages.
2024-04-16 20:15:32 +04:00
John Preston
fe73bf9053 Fix ripples in channel comments buttons. 2024-04-16 20:14:04 +04:00
John Preston
49c9e08b6c Enforce video/audio extension by mime type. 2024-04-16 20:14:01 +04:00
John Preston
abcf55c498 Check chat background read result. 2024-04-16 20:13:55 +04:00
John Preston
849ce310c4 Jump by keyboard between time input fields. 2024-04-16 20:13:02 +04:00
John Preston
a3e593b747 Fix crash in scheduled messages tear-down. 2024-04-16 20:12:03 +04:00
23rd
5433f95eda Simplified creation of label with custom emoji. 2024-04-16 03:44:00 +03:00
23rd
0459196982 Moved out level meter widget to td_ui. 2024-04-16 03:43:39 +03:00
23rd
6f1457d30d Fixed phrase of placeholder for message edition mode. 2024-04-15 23:56:37 +03:00
23rd
ce596e29c4 Fixed opening message from media player widget in corresponding window. 2024-04-15 23:41:00 +03:00
23rd
86b9d16747 Added additional note for macOS keys to json of custom shortcuts. 2024-04-15 23:30:57 +03:00
John Preston
06911ae42a Version 4.16.7.
- Reimplement file open confirmations.
2024-04-15 17:56:45 +04:00
John Preston
af2cb9b757 Fix possible unhandled exception in highlighting. 2024-04-15 17:56:45 +04:00
John Preston
6a28cd1a35 Reimplement file open confirmations. 2024-04-15 12:07:27 +04:00
23rd
f4a09a9ca0 Fixed display of growth rate in statistics info for huge values. 2024-04-15 11:20:30 +04:00
23rd
a35f020f56 Moved out ScheduledMessages module to components submodule. 2024-04-15 11:20:30 +04:00
23rd
3d48111368 Moved out SponsoredMessages module to components submodule. 2024-04-15 11:20:30 +04:00
23rd
39ed7d7f4c Increased clickable area for each item in EditPeerHistoryVisibilityBox. 2024-04-15 11:20:30 +04:00
23rd
abe83ccb8f Moved inaccessible groups in box for user's own groups to sub-list.
Fixed #27729.
2024-04-15 11:20:30 +04:00
23rd
d1be7c1ff7 Simplified management of PasscodeBox within customCheckCallback. 2024-04-15 11:20:30 +04:00
23rd
1c223e570a Slightly improved code style for long equality expressions. 2024-04-15 11:20:30 +04:00
23rd
a37cbd7d05 Initially refactored statistics module to simplify value types changing. 2024-04-15 11:20:30 +04:00
23rd
7ffa9844e2 Fixed currency formatting for channel earn info section. 2024-04-15 11:20:30 +04:00
23rd
bdf5872f04 Fixed opening channel earn info section for admins. 2024-04-15 11:20:30 +04:00
Ilya Fedin
da1909cc1d Clean remainings of mozjpeg in snapcraft.yml 2024-04-15 10:11:54 +04:00
Ilya Fedin
a503197352 Remove XCBSetDesktopFileName
This is done by Qt nowadays
2024-04-15 10:11:30 +04:00
Nyan
d9d9a8f49d Update data_document_resolver.cpp
Added the LEXE and WLUA extensions to the IsExecutableName function for Windows. Those are registered by default on Windows devices with LUA installed
2024-04-13 23:45:03 +04:00
Ilya Fedin
f57e2edf2a Replace mozjpeg with jpegli in snap 2024-04-11 12:35:47 +04:00
Ilya Fedin
a1e2e3d011 Replace mozjpeg with jpegli in Docker 2024-04-11 12:35:47 +04:00
Ilya Fedin
5e546d1198 Update libjxl in Docker & snap 2024-04-11 12:35:47 +04:00
Ilya Fedin
90405f3ebc Install protobuf in Docker 2024-04-11 12:15:55 +04:00
Nyan
11b57ff7d3 Update data_document_resolver.cpp
The correct python zipapp extension on windows is pyzw, this typo could lead to executing code in the client device without proper warning
2024-04-11 11:24:15 +04:00
Ilya Fedin
fe06cd63ac Lock event loop 2024-04-09 20:48:47 +04:00
John Preston
6c9d5e1499 Improve clipboard QR code image logo quality. 2024-04-09 19:05:02 +04:00
John Preston
6ed910de9f Version 4.16.6.
- Show custom emoji preview on long press.
- Fix resume chat bot button disappearance.
- Fix GIF files playback. (regression in 4.16.3.beta)
2024-04-09 15:20:16 +04:00
John Preston
a506b8b25c Support t.me/username?text=.. links. 2024-04-09 15:20:10 +04:00
John Preston
170ebb57c6 Preview custom emoji on long press from the panel. 2024-04-09 12:11:07 +04:00
John Preston
29dd574e22 Fix toggling chatbot replying, fix saving. 2024-04-09 11:55:52 +04:00
John Preston
72770aa76d Add GIF parser to FFmpeg build.
Fixes #27722.
2024-04-09 11:29:19 +04:00
John Preston
7f1c319aee Add interactive mode to prepare script. 2024-04-09 11:29:16 +04:00
John Preston
60805bd916 Version 4.16.5.
- Fix editing privacy for groups and channels invitations.
- Possible fix for the network unresponsiveness after sleep.
- Possible fix for wide range of memory allocator crashes on Linux.
2024-04-08 22:31:07 +04:00
Ilya Fedin
10272ee0cf Revert "Replace jemalloc with scudo"
This reverts commit 960761ef37.
2024-04-08 21:57:25 +04:00
John Preston
7c44cda76e Fix premium users deselection in multi-select field. 2024-04-08 21:56:56 +04:00
John Preston
0152d2c48e Fix possible temp auth key bind freeze. 2024-04-08 21:56:56 +04:00
23rd
c5febce548 Moved out some functions for main menu to separated files. 2024-04-08 17:34:58 +03:00
23rd
129b07c2c0 Improved style of box for all of user's own groups and channels. 2024-04-08 17:02:52 +03:00
23rd
47bf099b88 Fixed updating of field placeholder in compose controls while editing. 2024-04-08 06:55:44 +03:00
23rd
94f1d23788 Fixed behavior to open section with scheduled messages at top. 2024-04-08 04:12:14 +03:00
Ilya Fedin
84ce72ec7a Fix a crash due to half-hidden media viewer 2024-04-07 14:28:51 +04:00
Ilya Fedin
3da8351522 Let Wayland to use QGuiApplication::screenAt (forgotten place) 2024-04-07 14:08:40 +04:00
Ilya Fedin
542153d950 Use nasm from repository 2024-04-07 11:20:46 +04:00
Ilya Fedin
090fdfb458 Add asan libraries to Docker image 2024-04-07 11:20:46 +04:00
John Preston
77835a43a5 Version 4.16.4: Update patches on Linux. 2024-04-07 11:19:23 +04:00
John Preston
1a2a1f1c17 Version 4.16.4.
- Bug fixes and other minor improvements.
2024-04-06 22:59:03 +04:00
John Preston
eaaa704fa4 Check the URL in IV like in attach bots. 2024-04-06 22:59:03 +04:00
John Preston
7803f8e670 Simplify escaping when parsing IV. 2024-04-06 22:59:03 +04:00
Sergey A. Osokin
f36e3c213e Fix build on FreeBSD 13.3 / llvm 17.0.6 / qt 5.15.13 2024-04-06 20:30:08 +04:00
John Preston
6fb1e23ed5 Fix file dialog filter for videos from attach menu. 2024-04-06 13:05:12 +04:00
John Preston
86d0c49e44 Add "Boost Group/Channel" button to chat menu. 2024-04-06 13:05:12 +04:00
John Preston
9251e6faba Use better title in boost box. 2024-04-06 13:05:12 +04:00
John Preston
8f8725e1af Beta version 4.16.3: Fix dav1d in FFmpeg on macOS. 2024-04-05 21:25:20 +04:00
John Preston
0ce6a4cbdb Beta version 4.16.3: Re-enable system proxy (Linux).
This reverts commit 1e6fb202f0.
2024-04-05 20:48:06 +04:00
John Preston
ad3f705f50 Beta version 4.16.3.
- Improve media upload speed.
- Update FFmpeg to 6.1.1.
2024-04-05 15:35:01 +04:00
John Preston
c5847caa91 Update FFmpeg to 6.1.1: Fix AV1 videos. 2024-04-05 14:42:29 +04:00
John Preston
8df6d9db7e Don't update server-time too frequently. 2024-04-05 09:51:20 +04:00
John Preston
a9c1970f41 Send up to 1MB of parts to a single session. 2024-04-05 09:51:20 +04:00
John Preston
c3f0d2ef31 Adaptive upload up to 8 sessions. 2024-04-05 09:51:20 +04:00
John Preston
0dcc439dda Allow upload requests for several files at once. 2024-04-05 09:51:20 +04:00
John Preston
5b0cac47ad Reapply "Use plain vector for prepared upload parts."
This reverts commit f6f8eefaa0.
2024-04-05 09:51:20 +04:00
John Preston
b39e78a4a9 Reapply "Remove SendMediaReady legacy helper."
This reverts commit 09f07a7a9d.
2024-04-05 09:51:20 +04:00
524 changed files with 14219 additions and 24286 deletions

6
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

View File

@@ -1,32 +1,11 @@
name: No Response
name: Can't reproduce.
# Both `issue_comment` and `scheduled` event types are required for this Action
# to work properly.
on:
issue_comment:
types: [created]
schedule:
- cron: '0 0 * * *'
- cron: '0 3 * * *'
jobs:
waiting-for-answer:
runs-on: ubuntu-latest
steps:
- uses: lee-dohm/no-response@v0.5.0
with:
token: ${{ github.token }}
responseRequiredLabel: waiting for answer
needs-user-action:
runs-on: ubuntu-latest
steps:
- uses: lee-dohm/no-response@v0.5.0
with:
token: ${{ github.token }}
responseRequiredLabel: needs user action
cant-reproduce:
if: github.event_name != 'issue_comment'
runs-on: ubuntu-latest
steps:
- uses: lee-dohm/no-response@v0.5.0

View File

@@ -20,7 +20,7 @@ jobs:
steps:
- name: Clone.
uses: actions/checkout@v3.1.0
uses: actions/checkout@v4
with:
submodules: recursive
@@ -31,7 +31,7 @@ jobs:
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin
- name: Free up some disk space.
uses: jlumbroso/free-disk-space@f68fdb76e2ea636224182cfb7377ff9a1708f9b8
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
- name: Docker image build.
run: |

View File

@@ -49,7 +49,6 @@ jobs:
defines:
- ""
- "DESKTOP_APP_DISABLE_X11_INTEGRATION"
- "DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION"
env:
UPLOAD_ARTIFACT: "true"
@@ -59,7 +58,7 @@ jobs:
run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
- name: Clone.
uses: actions/checkout@v3.1.0
uses: actions/checkout@v4
with:
submodules: recursive
path: ${{ env.REPO_NAME }}
@@ -117,7 +116,7 @@ jobs:
cd $REPO_NAME/out/Debug
sudo mkdir artifact
sudo mv {Telegram,Updater} artifact/
- uses: actions/upload-artifact@master
- uses: actions/upload-artifact@v4
if: env.UPLOAD_ARTIFACT == 'true'
name: Upload artifact.
with:

View File

@@ -8,7 +8,7 @@ jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v3
- uses: dessant/lock-threads@v5
with:
github-token: ${{ github.token }}
issue-inactive-days: 45

View File

@@ -56,7 +56,7 @@ jobs:
run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
- name: Clone.
uses: actions/checkout@v3.1.0
uses: actions/checkout@v4
with:
submodules: recursive
path: ${{ env.REPO_NAME }}
@@ -76,7 +76,7 @@ jobs:
- name: ThirdParty cache.
id: cache-third-party
uses: actions/cache@v3.0.11
uses: actions/cache@v4
with:
path: ThirdParty
key: ${{ runner.OS }}-third-party-${{ hashFiles(format('{0}/{1}', env.REPO_NAME, env.PREPARE_PATH)) }}
@@ -84,7 +84,7 @@ jobs:
- name: Libraries cache.
id: cache-libs
uses: actions/cache@v3.0.11
uses: actions/cache@v4
with:
path: Libraries
key: ${{ runner.OS }}-libs-${{ hashFiles(format('{0}/{1}', env.REPO_NAME, env.PREPARE_PATH)) }}
@@ -134,7 +134,7 @@ jobs:
mkdir artifact
mv Telegram.app artifact/
mv Updater artifact/
- uses: actions/upload-artifact@master
- uses: actions/upload-artifact@v4
if: env.UPLOAD_ARTIFACT == 'true'
name: Upload artifact.
with:

View File

@@ -49,7 +49,7 @@ jobs:
env:
GIT: "https://github.com"
OPENALDIR: "/usr/local/opt/openal-soft"
CMAKE_PREFIX_PATH: "/usr/local/opt/ffmpeg@6:/usr/local/opt/openal-soft"
UPLOAD_ARTIFACT: "true"
ONLY_CACHE: "false"
MANUAL_CACHING: "1"
@@ -60,7 +60,7 @@ jobs:
run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
- name: Clone.
uses: actions/checkout@v3.1.0
uses: actions/checkout@v4
with:
submodules: recursive
path: ${{ env.REPO_NAME }}
@@ -69,7 +69,7 @@ jobs:
run: |
brew update
brew upgrade || true
brew install autoconf automake boost cmake ffmpeg openal-soft openssl opus ninja pkg-config python qt yasm xz
brew install autoconf automake boost cmake ffmpeg@6 openal-soft openssl opus ninja pkg-config python qt yasm xz
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
xcodebuild -version > CACHE_KEY.txt
@@ -99,7 +99,7 @@ jobs:
- name: WebRTC cache.
id: cache-webrtc
uses: actions/cache@v3.0.11
uses: actions/cache@v4
with:
path: ${{ env.LibrariesPath }}/tg_owt
key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}-${{ hashFiles('**/tg_owt-version.json') }}
@@ -153,7 +153,7 @@ jobs:
cd $REPO_NAME/build
mkdir artifact
mv Telegram.dmg artifact/
- uses: actions/upload-artifact@master
- uses: actions/upload-artifact@v4
if: env.UPLOAD_ARTIFACT == 'true'
name: Upload artifact.
with:

View File

@@ -11,7 +11,7 @@ jobs:
SKIP: "0"
to_branch: "master"
steps:
- uses: actions/checkout@v4.1.0
- uses: actions/checkout@v4
with:
fetch-depth: 0
if: env.SKIP == '0'

16
.github/workflows/needs-user-action.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: Needs user action.
on:
issue_comment:
types: [created]
schedule:
- cron: '0 2 * * *'
jobs:
needs-user-action:
runs-on: ubuntu-latest
steps:
- uses: lee-dohm/no-response@v0.5.0
with:
token: ${{ github.token }}
responseRequiredLabel: needs user action

View File

@@ -47,7 +47,7 @@ jobs:
steps:
- name: Clone.
uses: actions/checkout@v3.1.0
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: recursive
@@ -61,10 +61,10 @@ jobs:
sudo snap run lxd waitready
- name: Free up some disk space.
uses: jlumbroso/free-disk-space@f68fdb76e2ea636224182cfb7377ff9a1708f9b8
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
- name: Telegram Desktop snap build.
run: sg lxd -c 'snap run snapcraft -v'
run: sg lxd -c 'snap run snapcraft --verbosity=debug'
- name: Move artifact.
if: env.UPLOAD_ARTIFACT == 'true'
@@ -75,7 +75,7 @@ jobs:
mkdir artifact
mv $artifact_name artifact
- uses: actions/upload-artifact@master
- uses: actions/upload-artifact@v4
if: env.UPLOAD_ARTIFACT == 'true'
name: Upload artifact.
with:

View File

@@ -7,7 +7,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
- uses: actions/stale@v9
with:
stale-issue-message: |
Hey there!

View File

@@ -0,0 +1,16 @@
name: Waiting for answer.
on:
issue_comment:
types: [created]
schedule:
- cron: '30 0 * * *'
jobs:
waiting-for-answer:
runs-on: ubuntu-latest
steps:
- uses: lee-dohm/no-response@v0.5.0
with:
token: ${{ github.token }}
responseRequiredLabel: waiting for answer

View File

@@ -69,13 +69,13 @@ jobs:
shell: bash
run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
- uses: ilammy/msvc-dev-cmd@v1.12.0
- uses: ilammy/msvc-dev-cmd@v1.13.0
name: Native Tools Command Prompt.
with:
arch: ${{ matrix.arch }}
- name: Clone.
uses: actions/checkout@v3.1.0
uses: actions/checkout@v4
with:
submodules: recursive
path: ${{ env.TBUILD }}\${{ env.REPO_NAME }}
@@ -96,7 +96,7 @@ jobs:
- name: Libraries cache.
id: cache-libs
uses: actions/cache@v3.0.11
uses: actions/cache@v4
with:
path: ${{ env.TBUILD }}\Libraries
key: ${{ runner.OS }}-${{ matrix.arch }}-libs-${{ env.CACHE_KEY }}
@@ -183,7 +183,7 @@ jobs:
mkdir artifact
move %OUT%\Telegram.exe artifact/
move %OUT%\Updater.exe artifact/
- uses: actions/upload-artifact@master
- uses: actions/upload-artifact@v4
name: Upload artifact.
if: (env.UPLOAD_ARTIFACT == 'true') || (github.ref == 'refs/heads/nightly')
with:

1
.gitignore vendored
View File

@@ -18,6 +18,7 @@ Release/
*.xcodeproj
ipch/
.vs/
.vscode/
/Telegram/log.txt
/Telegram/data

12
.gitmodules vendored
View File

@@ -76,15 +76,12 @@
[submodule "Telegram/lib_webview"]
path = Telegram/lib_webview
url = https://github.com/desktop-app/lib_webview.git
[submodule "Telegram/ThirdParty/jemalloc"]
path = Telegram/ThirdParty/jemalloc
url = https://github.com/jemalloc/jemalloc
[submodule "Telegram/ThirdParty/dispatch"]
path = Telegram/ThirdParty/dispatch
url = https://github.com/apple/swift-corelibs-libdispatch
[submodule "Telegram/ThirdParty/plasma-wayland-protocols"]
path = Telegram/ThirdParty/plasma-wayland-protocols
url = https://github.com/KDE/plasma-wayland-protocols.git
[submodule "Telegram/ThirdParty/wayland-protocols"]
path = Telegram/ThirdParty/wayland-protocols
url = https://github.com/gitlab-freedesktop-mirrors/wayland-protocols.git
[submodule "Telegram/ThirdParty/kimageformats"]
path = Telegram/ThirdParty/kimageformats
url = https://github.com/KDE/kimageformats.git
@@ -94,9 +91,6 @@
[submodule "Telegram/ThirdParty/cld3"]
path = Telegram/ThirdParty/cld3
url = https://github.com/google/cld3.git
[submodule "Telegram/ThirdParty/wayland"]
path = Telegram/ThirdParty/wayland
url = https://github.com/gitlab-freedesktop-mirrors/wayland.git
[submodule "Telegram/ThirdParty/libprisma"]
path = Telegram/ThirdParty/libprisma
url = https://github.com/desktop-app/libprisma.git

View File

@@ -62,7 +62,7 @@ if (NOT DESKTOP_APP_USE_PACKAGED)
if (WIN32)
set(qt_version 5.15.13)
elseif (APPLE)
set(qt_version 6.2.7)
set(qt_version 6.2.8)
endif()
endif()
include(cmake/external/qt/package.cmake)

View File

@@ -273,6 +273,8 @@ PRIVATE
boxes/local_storage_box.h
boxes/max_invite_box.cpp
boxes/max_invite_box.h
boxes/moderate_messages_box.cpp
boxes/moderate_messages_box.h
boxes/peer_list_box.cpp
boxes/peer_list_box.h
boxes/peer_list_controllers.cpp
@@ -460,6 +462,14 @@ PRIVATE
data/business/data_business_info.h
data/business/data_shortcut_messages.cpp
data/business/data_shortcut_messages.h
data/components/recent_peers.cpp
data/components/recent_peers.h
data/components/scheduled_messages.cpp
data/components/scheduled_messages.h
data/components/sponsored_messages.cpp
data/components/sponsored_messages.h
data/components/top_peers.cpp
data/components/top_peers.h
data/notify/data_notify_settings.cpp
data/notify/data_notify_settings.h
data/notify/data_peer_notify_settings.cpp
@@ -577,14 +587,10 @@ PRIVATE
data/data_send_action.h
data/data_session.cpp
data/data_session.h
data/data_scheduled_messages.cpp
data/data_scheduled_messages.h
data/data_shared_media.cpp
data/data_shared_media.h
data/data_sparse_ids.cpp
data/data_sparse_ids.h
data/data_sponsored_messages.cpp
data/data_sponsored_messages.h
data/data_statistics.h
data/data_stories.cpp
data/data_stories.h
@@ -608,6 +614,18 @@ PRIVATE
data/data_wall_paper.h
data/data_web_page.cpp
data/data_web_page.h
dialogs/ui/dialogs_layout.cpp
dialogs/ui/dialogs_layout.h
dialogs/ui/dialogs_message_view.cpp
dialogs/ui/dialogs_message_view.h
dialogs/ui/dialogs_stories_content.cpp
dialogs/ui/dialogs_stories_content.h
dialogs/ui/dialogs_suggestions.cpp
dialogs/ui/dialogs_suggestions.h
dialogs/ui/dialogs_topics_view.cpp
dialogs/ui/dialogs_topics_view.h
dialogs/ui/dialogs_video_userpic.cpp
dialogs/ui/dialogs_video_userpic.h
dialogs/dialogs_entry.cpp
dialogs/dialogs_entry.h
dialogs/dialogs_indexed_list.cpp
@@ -630,16 +648,6 @@ PRIVATE
dialogs/dialogs_search_tags.h
dialogs/dialogs_widget.cpp
dialogs/dialogs_widget.h
dialogs/ui/dialogs_layout.cpp
dialogs/ui/dialogs_layout.h
dialogs/ui/dialogs_message_view.cpp
dialogs/ui/dialogs_message_view.h
dialogs/ui/dialogs_stories_content.cpp
dialogs/ui/dialogs_stories_content.h
dialogs/ui/dialogs_topics_view.cpp
dialogs/ui/dialogs_topics_view.h
dialogs/ui/dialogs_video_userpic.cpp
dialogs/ui/dialogs_video_userpic.h
editor/color_picker.cpp
editor/color_picker.h
editor/controllers/controllers.h
@@ -680,6 +688,8 @@ PRIVATE
history/view/controls/compose_controls_common.h
history/view/controls/history_view_compose_controls.cpp
history/view/controls/history_view_compose_controls.h
history/view/controls/history_view_compose_media_edit_manager.cpp
history/view/controls/history_view_compose_media_edit_manager.h
history/view/controls/history_view_compose_search.cpp
history/view/controls/history_view_compose_search.h
history/view/controls/history_view_draft_options.cpp
@@ -1199,11 +1209,6 @@ PRIVATE
payments/payments_checkout_process.h
payments/payments_form.cpp
payments/payments_form.h
platform/linux/linux_wayland_integration_dummy.cpp
platform/linux/linux_wayland_integration.cpp
platform/linux/linux_wayland_integration.h
platform/linux/linux_xdp_open_with_dialog.cpp
platform/linux/linux_xdp_open_with_dialog.h
platform/linux/file_utilities_linux.cpp
platform/linux/file_utilities_linux.h
platform/linux/launcher_linux.cpp
@@ -1459,8 +1464,6 @@ PRIVATE
ui/image/image_location.h
ui/image/image_location_factory.cpp
ui/image/image_location_factory.h
ui/widgets/level_meter.cpp
ui/widgets/level_meter.h
ui/countryinput.cpp
ui/countryinput.h
ui/dynamic_thumbnails.cpp
@@ -1476,6 +1479,8 @@ PRIVATE
ui/search_field_controller.h
ui/text/format_song_document_name.cpp
ui/text/format_song_document_name.h
ui/widgets/label_with_custom_emoji.cpp
ui/widgets/label_with_custom_emoji.h
ui/unread_badge.cpp
ui/unread_badge.h
window/main_window.cpp
@@ -1503,6 +1508,8 @@ PRIVATE
window/window_lock_widgets.h
window/window_main_menu.cpp
window/window_main_menu.h
window/window_main_menu_helpers.cpp
window/window_main_menu_helpers.h
window/window_media_preview.cpp
window/window_media_preview.h
window/window_peer_menu.cpp
@@ -1554,16 +1561,6 @@ if (NOT build_winstore)
)
endif()
if (DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION)
remove_target_sources(Telegram ${src_loc}
platform/linux/linux_wayland_integration.cpp
)
else()
remove_target_sources(Telegram ${src_loc}
platform/linux/linux_wayland_integration_dummy.cpp
)
endif()
if (DESKTOP_APP_USE_PACKAGED)
remove_target_sources(Telegram ${src_loc}
platform/mac/mac_iconv_helper.c
@@ -1701,19 +1698,6 @@ else()
desktop-app::external_xcb
)
endif()
if (NOT DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION)
qt_generate_wayland_protocol_client_sources(Telegram
FILES
${third_party_loc}/wayland/protocol/wayland.xml
${third_party_loc}/plasma-wayland-protocols/src/protocols/plasma-shell.xml
)
target_link_libraries(Telegram
PRIVATE
desktop-app::external_wayland_client
)
endif()
endif()
if (build_macstore)

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 754 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -134,9 +134,32 @@ html.custom_scroll ::-webkit-scrollbar-thumb:hover {
.page-slide {
position: relative;
width: 100%;
min-height: 100%;
margin-left: 0%;
transition: margin 240ms ease-in-out;
}
.page-footer {
height: 32px;
margin-top: -32px;
background: var(--td-window-bg-over);
}
.page-footer .content {
padding: 3px 18px;
font-size: 15px;
color: var(--td-window-sub-text-fg);
text-align: center;
}
.page-footer .wrong {
position: relative;
padding: 5px;
margin: -5px;
color: var(--td-window-sub-text-fg);
text-decoration: none;
cursor: pointer;
}
.page-footer .wrong:hover {
text-decoration: underline;
}
.hidden-left,
.hidden-right {
pointer-events: none;
@@ -148,7 +171,7 @@ html.custom_scroll ::-webkit-scrollbar-thumb:hover {
margin-left: 100%;
}
article {
padding-bottom: 12px;
padding-bottom: 40px;
overflow-y: hidden;
overflow-x: auto;
white-space: pre-wrap;
@@ -893,6 +916,9 @@ section.related a.related-link:after {
right: 0;
bottom: 0;
}
section.related a.related-link:last-child:after {
border-bottom: 0px;
}
section.related .related-link-url {
display: block;
font-size: 15px;
@@ -1027,6 +1053,9 @@ section.channel > a > h4 {
display: block;
margin: 0 auto;
}
.media-outer {
margin-bottom: 16px;
}
.photo-wrap,
.video-wrap {
width: 100%;

View File

@@ -26,7 +26,7 @@ var IV = {
}
target = target.parentNode;
}
if (!target || !target.hasAttribute('href')) {
if (!target || (context === '' && !target.hasAttribute('href'))) {
return;
}
var base = document.createElement('A');
@@ -413,9 +413,12 @@ var IV = {
var article = function (el) {
return el.getElementsByTagName('article')[0];
};
var from = article(IV.findPageScroll());
var to = article(IV.makeScrolledContent(data.html));
morphdom(from, to, {
var footer = function (el) {
return el.getElementsByClassName('page-footer')[0];
};
var from = IV.findPageScroll();
var to = IV.makeScrolledContent(data.html);
morphdom(article(from), article(to), {
onBeforeElUpdated: function (fromEl, toEl) {
if (fromEl.classList.contains('video')
&& toEl.classList.contains('video')
@@ -439,6 +442,7 @@ var IV = {
return !fromEl.isEqualNode(toEl);
}
});
morphdom(footer(from), footer(to));
IV.initMedia();
eval(data.js);
},
@@ -477,9 +481,7 @@ var IV = {
var result = document.createElement('div');
result.className = 'page-scroll';
result.tabIndex = '-1';
result.innerHTML = '<div class="page-slide"><article>'
+ html
+ '</article></div>';
result.innerHTML = html.trim();
result.onscroll = IV.frameScrolled;
return result;
},

View File

@@ -309,7 +309,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_limit_reached#one" = "You've reached the message text limit. Please make the text shorter by {count} character.";
"lng_edit_limit_reached#other" = "You've reached the message text limit. Please make the text shorter by {count} characters.";
"lng_edit_message" = "Edit message";
"lng_edit_message_text" = "New message text...";
"lng_edit_message_text" = "Caption";
"lng_deleted" = "Deleted Account";
"lng_deleted_message" = "Deleted message";
"lng_deleted_story" = "Deleted story";
@@ -730,10 +730,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_angle_backend_d3d11on12" = "D3D11on12";
"lng_settings_angle_backend_opengl" = "OpenGL";
"lng_settings_angle_backend_disabled" = "Disabled";
"lng_settings_top_peers_title" = "Frequent contacts";
"lng_settings_top_peers_suggest" = "Suggest frequent contacts";
"lng_settings_top_peers_about" = "Display people you message frequently at the top of the search section for quick access.";
"lng_settings_sensitive_title" = "Sensitive content";
"lng_settings_sensitive_disable_filtering" = "Disable filtering";
"lng_settings_sensitive_about" = "Display sensitive media in public channels on all your Telegram devices.";
"lng_settings_security_bots" = "Bots and websites";
"lng_settings_file_confirmations" = "File open confirmations";
"lng_settings_edit_extensions" = "Extensions whitelist";
"lng_settings_edit_extensions_about" = "Open files with the following extensions without additional confirmation.";
"lng_settings_edit_ip_confirm" = "IP reveal warning";
"lng_settings_edit_ip_confirm_about" = "Show confirmation when opening files that may reveal your IP address.";
"lng_settings_clear_payment_info" = "Clear Payment and Shipping Info";
"lng_settings_logged_in" = "Connected websites";
"lng_settings_logged_in_title" = "Logged in with Telegram";
@@ -835,6 +843,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_auto_night_mode_on" = "System";
"lng_settings_auto_night_warning" = "You have enabled auto-night mode. If you want to change the dark mode settings, you'll need to disable it first.";
"lng_settings_auto_night_disable" = "Disable";
"lng_settings_font_family" = "Font family";
"lng_settings_color_title" = "Color preview";
"lng_settings_color_reply" = "Reply to your message";
@@ -1487,6 +1496,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_manage_peer_reactions_none_about" = "Members of the group can't add any reactions to messages.";
"lng_manage_peer_reactions_some_title" = "Only allow these reactions";
"lng_manage_peer_reactions_available" = "Available reactions";
"lng_manage_peer_reactions_available_ph" = "Add reactions...";
"lng_manage_peer_reactions_own" = "You can also {link} emoji packs and use them as reactions.";
"lng_manage_peer_reactions_own_link" = "create your own";
"lng_manage_peer_reactions_level#one" = "Your channel needs to reach level **{count}** to use **{same_count}** custom reaction.";
@@ -1495,6 +1505,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_manage_peer_reactions_boost_link" = "here";
"lng_manage_peer_reactions_limit" = "Channels can't have more custom reactions.";
"lng_manage_peer_reactions_max_title" = "Maximum number of reactions";
"lng_manage_peer_reactions_max_slider#one" = "{count} reaction per post";
"lng_manage_peer_reactions_max_slider#other" = "{count} reactions per post";
"lng_manage_peer_reactions_max_about" = "Limit the number of different reactions that can be added to a post, including already published ones.";
"lng_manage_peer_antispam" = "Aggressive Anti-Spam";
"lng_manage_peer_antispam_about" = "Telegram will filter more spam but may occasionally affect ordinary messages. You can report False Positives in Recent Actions.";
"lng_manage_peer_antispam_not_enough#one" = "Aggressive filtering can be enabled only in groups with more than **{count} member**.";
@@ -2264,6 +2279,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_business_about_chat_intro" = "Customize the message people see before they start a chat with you.";
"lng_business_subtitle_chat_links" = "Links to Chat";
"lng_business_about_chat_links" = "Create links that start a chat with you, suggesting the first message.";
"lng_business_subtitle_sponsored" = "Ads in Channels";
"lng_business_button_sponsored" = "Do Not Hide Ads";
"lng_business_about_sponsored" = "As a Premium subscriber, you dont see any ads on Telegram, but you can turn them on, for example, to view your own ads that you launched on the {link}";
"lng_business_about_sponsored_link" = "Telegram Ad Platform {emoji}";
"lng_business_about_sponsored_url" = "https://ads.telegram.org";
"lng_location_title" = "Location";
"lng_location_about" = "Display the location of your business on your account.";
@@ -2834,7 +2854,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_in_dlg_audio_count#other" = "{count} audio";
"lng_ban_user" = "Ban User";
"lng_ban_users" = "Ban users";
"lng_restrict_users" = "Restrict users";
"lng_delete_all_from_user" = "Delete all from {user}";
"lng_delete_all_from_users" = "Delete all from users";
"lng_restrict_user_part" = "Partially restrict this user {emoji}";
"lng_restrict_users_part" = "Partially restrict users {emoji}";
"lng_restrict_user_full" = "Fully ban this user {emoji}";
"lng_restrict_users_full" = "Fully ban users {emoji}";
"lng_restrict_users_part_single_header" = "What can this user do?";
"lng_restrict_users_part_header#one" = "What can {count} selected user do?";
"lng_restrict_users_part_header#other" = "What can {count} selected users do?";
"lng_report_spam" = "Report Spam";
"lng_report_spam_and_leave" = "Report spam and leave";
"lng_report_spam_done" = "Thank you for your report.";
@@ -3062,6 +3092,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_unread_bar_some" = "Unread messages";
"lng_maps_point" = "Location";
"lng_live_location" = "Live Location";
"lng_live_location_now" = "updated just now";
"lng_live_location_minutes#one" = "updated {count} minute ago";
"lng_live_location_minutes#other" = "updated {count} minutes ago";
"lng_live_location_hours#one" = "updated {count} hour ago";
"lng_live_location_hours#other" = "updated {count} hours ago";
"lng_live_location_today" = "updated today at {time}";
"lng_live_location_yesterday" = "updated yesterday at {time}";
"lng_live_location_date_time" = "updated {date} at {time}";
"lng_save_photo" = "Save image";
"lng_save_video" = "Save video";
"lng_save_audio_file" = "Save audio file";
@@ -3405,6 +3444,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_mediaview_forward" = "Forward";
"lng_mediaview_delete" = "Delete";
"lng_mediaview_save_to_profile" = "Save to Profile";
"lng_mediaview_pin_story_done" = "Story pinned";
"lng_mediaview_pin_story_about" = "Now it will be always shown on the top.";
"lng_mediaview_pin_stories_done#one" = "{count} story pinned";
"lng_mediaview_pin_stories_done#other" = "{count} stories pinned";
"lng_mediaview_pin_stories_about#one" = "Now it will be always shown on the top.";
"lng_mediaview_pin_stories_about#other" = "Now they will be always shown on the top.";
"lng_mediaview_unpin_story_done" = "Story unpinned.";
"lng_mediaview_unpin_stories_done#one" = "{count} story unpinned";
"lng_mediaview_unpin_stories_done#other" = "{count} stories unpinned";
"lng_mediaview_pin_limit#one" = "You can't pin more than {count} story.";
"lng_mediaview_pin_limit#other" = "You can't pin more than {count} stories.";
"lng_mediaview_archive_story" = "Archive Story";
"lng_mediaview_photos_all" = "View all photos";
"lng_mediaview_files_all" = "View all files";
@@ -4480,10 +4530,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_translate_settings_about" = "The 'Translate' button will appear when you open a context menu on a text message.";
"lng_translate_settings_one" = "Please choose at least one language so that it can be used as the \"Translate to\" language.";
"lng_launch_exe_warning" = "This file has a {extension} extension.\nAre you sure you want to run it?";
"lng_launch_exe_warning" = "This file has {extension} extension.\nAre you sure you want to run it?";
"lng_launch_other_warning" = "This file has {extension} extension.\nAre you sure you want to open it?";
"lng_launch_svg_warning" = "Opening this file can potentially expose your IP address to its sender. Continue?";
"lng_launch_exe_sure" = "Run";
"lng_launch_other_sure" = "Open";
"lng_launch_exe_dont_ask" = "Don't ask me again";
"lng_launch_dont_ask" = "Remember for this file type";
"lng_launch_dont_ask_settings" = "You can later edit trusted file types in Settings > Privacy and Security > File open confirmations.";
"lng_polls_anonymous" = "Anonymous Poll";
"lng_polls_public" = "Poll";
@@ -4695,6 +4749,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_view_button_boost" = "Boost";
"lng_view_button_giftcode" = "Open";
"lng_view_button_iv" = "Instant View";
"lng_view_button_stickerset" = "View stickers";
"lng_view_button_emojipack" = "View emoji";
"lng_sponsored_hide_ads" = "Hide";
"lng_sponsored_title" = "What are sponsored messages?";
@@ -5064,6 +5120,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_iv_share" = "Share";
"lng_iv_join_channel" = "Join";
"lng_iv_window_title" = "Instant View";
"lng_iv_wrong_layout" = "Wrong layout?";
"lng_limit_download_title" = "Download speed limited";
"lng_limit_download_subscribe" = "Subscribe to {link} and increase download speed {increase}.";
@@ -5078,6 +5135,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_limit_upload_increase_speed" = "by **{percent}**";
"lng_limit_upload_subscribe_link" = "Telegram Premium";
"lng_recent_frequent" = "Frequent contacts";
"lng_recent_frequent_all" = "Show all";
"lng_recent_frequent_collapse" = "Collapse";
"lng_recent_title" = "Recent";
"lng_recent_clear" = "Clear";
"lng_recent_clear_sure" = "Do you want to clear your search history?";
"lng_recent_remove" = "Remove from Recent";
"lng_recent_clear_all" = "Clear all";
"lng_recent_hide_top" = "Remove all & Disable";
"lng_recent_hide_sure" = "Are you sure you want to clear and disable frequent contacts list?\n\nYou can always turn this feature back on in Settings > Privacy > Suggest Frequent Contacts.";
"lng_recent_hide_button" = "Hide";
"lng_recent_none" = "Recent search results\nwill appear here.";
"lng_recent_chats" = "Chats";
"lng_recent_channels" = "Channels";
"lng_channels_none_title" = "No channels yet...";
"lng_channels_none_about" = "You are not currently subscribed to any channels.";
"lng_channels_your_title" = "Channels you joined";
"lng_channels_your_more" = "Show more";
"lng_channels_your_less" = "Show less";
"lng_channels_recommended" = "Recommended channels";
"lng_font_box_title" = "Choose font family";
"lng_font_default" = "Default";
"lng_font_system" = "System font";
"lng_font_not_found" = "Font not found.";
// Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program...";

View File

@@ -24,5 +24,7 @@
<file alias="chat_link.tgs">../../animations/chat_link.tgs</file>
<file alias="collectible_username.tgs">../../animations/collectible_username.tgs</file>
<file alias="collectible_phone.tgs">../../animations/collectible_phone.tgs</file>
<file alias="search.tgs">../../animations/search.tgs</file>
<file alias="noresults.tgs">../../animations/noresults.tgs</file>
</qresource>
</RCC>

View File

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

View File

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

View File

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

View File

@@ -236,14 +236,14 @@ void SendBotCallbackDataWithPassword(
} else {
return;
}
const auto box = std::make_shared<QPointer<PasscodeBox>>();
auto fields = PasscodeBox::CloudFields::From(state);
fields.customTitle = tr::lng_bots_password_confirm_title();
fields.customDescription
= tr::lng_bots_password_confirm_description(tr::now);
fields.customSubmitButton = tr::lng_passcode_submit();
fields.customCheckCallback = [=](
const Core::CloudPasswordResult &result) {
const Core::CloudPasswordResult &result,
QPointer<PasscodeBox> box) {
if (const auto button = getButton()) {
if (button->requestId) {
return;
@@ -257,18 +257,17 @@ void SendBotCallbackDataWithPassword(
return;
}
SendBotCallbackData(strongController, item, row, column, result, [=] {
if (*box) {
(*box)->closeBox();
if (box) {
box->closeBox();
}
}, [=](const QString &error) {
if (*box) {
(*box)->handleCustomCheckError(error);
if (box) {
box->handleCustomCheckError(error);
}
});
}
};
auto object = Box<PasscodeBox>(session, fields);
*box = Ui::MakeWeak(object.data());
show->showBox(std::move(object), Ui::LayerOption::CloseOther);
}, *lifetime);
}

View File

@@ -112,8 +112,8 @@ void ApplyLastList(
channel->mgInfo->lastAdmins.clear();
channel->mgInfo->lastRestricted.clear();
channel->mgInfo->lastParticipants.clear();
channel->mgInfo->lastParticipantsStatus =
MegagroupInfo::LastParticipantsUpToDate
channel->mgInfo->lastParticipantsStatus
= MegagroupInfo::LastParticipantsUpToDate
| MegagroupInfo::LastParticipantsOnceReceived;
auto botStatus = channel->mgInfo->botStatus;
@@ -212,7 +212,7 @@ void ApplyBotsList(
}
[[nodiscard]] ChatParticipants::Channels ParseSimilar(
not_null<ChannelData*> channel,
not_null<Main::Session*> session,
const MTPmessages_Chats &chats) {
auto result = ChatParticipants::Channels();
std::vector<not_null<ChannelData*>>();
@@ -220,13 +220,13 @@ void ApplyBotsList(
const auto &list = data.vchats().v;
result.list.reserve(list.size());
for (const auto &chat : list) {
const auto peer = channel->owner().processChat(chat);
const auto peer = session->data().processChat(chat);
if (const auto channel = peer->asChannel()) {
result.list.push_back(channel);
}
}
if constexpr (MTPDmessages_chatsSlice::Is<decltype(data)>()) {
if (channel->session().premiumPossible()) {
if (session->premiumPossible()) {
result.more = data.vcount().v - data.vchats().v.size();
}
}
@@ -234,6 +234,12 @@ void ApplyBotsList(
return result;
}
[[nodiscard]] ChatParticipants::Channels ParseSimilar(
not_null<ChannelData*> channel,
const MTPmessages_Chats &chats) {
return ParseSimilar(&channel->session(), chats);
}
} // namespace
ChatParticipant::ChatParticipant(
@@ -351,7 +357,8 @@ QString ChatParticipant::rank() const {
}
ChatParticipants::ChatParticipants(not_null<ApiWrap*> api)
: _api(&api->instance()) {
: _session(&api->session())
, _api(&api->instance()) {
}
void ChatParticipants::requestForAdd(
@@ -585,6 +592,33 @@ ChatParticipants::Parsed ChatParticipants::ParseRecent(
return result;
}
void ChatParticipants::Restrict(
not_null<ChannelData*> channel,
not_null<PeerData*> participant,
ChatRestrictionsInfo oldRights,
ChatRestrictionsInfo newRights,
Fn<void()> onDone,
Fn<void()> onFail) {
channel->session().api().request(MTPchannels_EditBanned(
channel->inputChannel,
participant->input,
MTP_chatBannedRights(
MTP_flags(MTPDchatBannedRights::Flags::from_raw(
uint32(newRights.flags))),
MTP_int(newRights.until))
)).done([=](const MTPUpdates &result) {
channel->session().api().applyUpdates(result);
channel->applyEditBanned(participant, oldRights, newRights);
if (onDone) {
onDone();
}
}).fail([=] {
if (onFail) {
onFail();
}
}).send();
}
void ChatParticipants::requestSelf(not_null<ChannelData*> channel) {
if (_selfParticipantRequests.contains(channel)) {
return;
@@ -730,8 +764,11 @@ void ChatParticipants::loadSimilarChannels(not_null<ChannelData*> channel) {
return;
}
}
using Flag = MTPchannels_GetChannelRecommendations::Flag;
_similar[channel].requestId = _api.request(
MTPchannels_GetChannelRecommendations(channel->inputChannel)
MTPchannels_GetChannelRecommendations(
MTP_flags(Flag::f_channel),
channel->inputChannel)
).done([=](const MTPmessages_Chats &result) {
auto &similar = _similar[channel];
similar.requestId = 0;
@@ -766,4 +803,29 @@ auto ChatParticipants::similarLoaded() const
return _similarLoaded.events();
}
void ChatParticipants::loadRecommendations() {
if (_recommendationsLoaded.current() || _recommendations.requestId) {
return;
}
_recommendations.requestId = _api.request(
MTPchannels_GetChannelRecommendations(
MTP_flags(0),
MTP_inputChannelEmpty())
).done([=](const MTPmessages_Chats &result) {
_recommendations.requestId = 0;
auto parsed = ParseSimilar(_session, result);
_recommendations.channels = std::move(parsed);
_recommendations.channels.more = 0;
_recommendationsLoaded = true;
}).send();
}
const ChatParticipants::Channels &ChatParticipants::recommendations() const {
return _recommendations.channels;
}
rpl::producer<> ChatParticipants::recommendationsLoaded() const {
return _recommendationsLoaded.changes() | rpl::to_empty;
}
} // namespace Api

View File

@@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class ApiWrap;
class ChannelData;
namespace Main {
class Session;
} // namespace Main
namespace Ui {
class Show;
} // namespace Ui
@@ -96,6 +100,13 @@ public:
static Parsed ParseRecent(
not_null<ChannelData*> channel,
const TLMembers &data);
static void Restrict(
not_null<ChannelData*> channel,
not_null<PeerData*> participant,
ChatRestrictionsInfo oldRights,
ChatRestrictionsInfo newRights,
Fn<void()> onDone,
Fn<void()> onFail);
void add(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
@@ -134,12 +145,18 @@ public:
[[nodiscard]] auto similarLoaded() const
-> rpl::producer<not_null<ChannelData*>>;
void loadRecommendations();
[[nodiscard]] const Channels &recommendations() const;
[[nodiscard]] rpl::producer<> recommendationsLoaded() const;
private:
struct SimilarChannels {
Channels channels;
mtpRequestId requestId = 0;
};
const not_null<Main::Session*> _session;
MTP::Sender _api;
using PeerRequests = base::flat_map<PeerData*, mtpRequestId>;
@@ -165,6 +182,9 @@ private:
base::flat_map<not_null<ChannelData*>, SimilarChannels> _similar;
rpl::event_stream<not_null<ChannelData*>> _similarLoaded;
SimilarChannels _recommendations;
rpl::variable<bool> _recommendationsLoaded = false;
};
} // namespace Api

View File

@@ -62,6 +62,10 @@ void ConfirmPhone::resolve(
return bad("FirebaseSms");
}, [&](const MTPDauth_sentCodeTypeEmailCode &) {
return bad("EmailCode");
}, [&](const MTPDauth_sentCodeTypeSmsWord &) {
return bad("SmsWord");
}, [&](const MTPDauth_sentCodeTypeSmsPhrase &) {
return bad("SmsPhrase");
}, [&](const MTPDauth_sentCodeTypeSetUpEmailRequired &) {
return bad("SetUpEmailRequired");
});

View File

@@ -58,25 +58,33 @@ void HandleWithdrawalButton(
state->loading = false;
auto fields = PasscodeBox::CloudFields::From(pass);
fields.customTitle =
tr::lng_channel_earn_balance_password_title();
fields.customDescription =
tr::lng_channel_earn_balance_password_description(tr::now);
fields.customTitle
= tr::lng_channel_earn_balance_password_title();
fields.customDescription
= tr::lng_channel_earn_balance_password_description(tr::now);
fields.customSubmitButton = tr::lng_passcode_submit();
fields.customCheckCallback = crl::guard(button, [=](
const Core::CloudPasswordResult &result) {
const Core::CloudPasswordResult &result,
QPointer<PasscodeBox> box) {
const auto done = [=](const QString &result) {
if (!result.isEmpty()) {
UrlClickHandler::Open(result);
if (box) {
box->closeBox();
}
}
};
const auto fail = [=](const QString &error) {
show->showToast(error);
};
session->api().request(
MTPstats_GetBroadcastRevenueWithdrawalUrl(
channel->inputChannel,
result.result
)).done([=](const MTPstats_BroadcastRevenueWithdrawalUrl &r) {
const auto url = qs(r.data().vurl());
if (!url.isEmpty()) {
UrlClickHandler::Open(url);
}
done(qs(r.data().vurl()));
}).fail([=](const MTP::Error &error) {
show->showToast(error.type());
fail(error.type());
}).send();
});
show->show(Box<PasscodeBox>(session, fields));

View File

@@ -12,12 +12,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_text_entities.h"
#include "ui/boxes/confirm_box.h"
#include "data/business/data_shortcut_messages.h"
#include "data/components/scheduled_messages.h"
#include "data/data_file_origin.h"
#include "data/data_histories.h"
#include "data/data_scheduled_messages.h"
#include "data/data_session.h"
#include "data/data_web_page.h"
#include "history/history.h"
#include "history/history_item.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "mtproto/mtproto_response.h"
@@ -29,20 +29,20 @@ namespace {
using namespace rpl::details;
template <typename T>
constexpr auto WithId =
is_callable_plain_v<T, Fn<void()>, mtpRequestId>;
constexpr auto WithId
= is_callable_plain_v<T, Fn<void()>, mtpRequestId>;
template <typename T>
constexpr auto WithoutId =
is_callable_plain_v<T, Fn<void()>>;
constexpr auto WithoutId
= is_callable_plain_v<T, Fn<void()>>;
template <typename T>
constexpr auto WithoutCallback =
is_callable_plain_v<T>;
constexpr auto WithoutCallback
= is_callable_plain_v<T>;
template <typename T>
constexpr auto ErrorWithId =
is_callable_plain_v<T, QString, mtpRequestId>;
constexpr auto ErrorWithId
= is_callable_plain_v<T, QString, mtpRequestId>;
template <typename T>
constexpr auto ErrorWithoutId =
is_callable_plain_v<T, QString>;
constexpr auto ErrorWithoutId
= is_callable_plain_v<T, QString>;
template <typename DoneCallback, typename FailCallback>
mtpRequestId EditMessage(
@@ -95,7 +95,7 @@ mtpRequestId EditMessage(
: emptyFlag);
const auto id = item->isScheduled()
? session->data().scheduledMessages().lookupId(item)
? session->scheduledMessages().lookupId(item)
: item->isBusinessShortcut()
? session->data().shortcutMessages().lookupId(item)
: item->id;
@@ -253,12 +253,100 @@ mtpRequestId EditTextMessage(
Data::WebPageDraft webpage,
SendOptions options,
Fn<void(mtpRequestId requestId)> done,
Fn<void(const QString &, mtpRequestId requestId)> fail) {
Fn<void(const QString &error, mtpRequestId requestId)> fail,
std::optional<bool> spoilerMediaOverride) {
if (spoilerMediaOverride) {
const auto spoiler = *spoilerMediaOverride;
if (const auto media = item->media()) {
auto takeInputMedia = Fn<std::optional<MTPInputMedia>()>(nullptr);
auto takeFileReference = Fn<QByteArray()>(nullptr);
if (const auto photo = media->photo()) {
using Flag = MTPDinputMediaPhoto::Flag;
const auto flags = Flag()
| (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag())
| (spoiler ? Flag::f_spoiler : Flag());
takeInputMedia = [=] {
return MTP_inputMediaPhoto(
MTP_flags(flags),
photo->mtpInput(),
MTP_int(media->ttlSeconds()));
};
takeFileReference = [=] { return photo->fileReference(); };
} else if (const auto document = media->document()) {
using Flag = MTPDinputMediaDocument::Flag;
const auto flags = Flag()
| (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag())
| (spoiler ? Flag::f_spoiler : Flag());
takeInputMedia = [=] {
return MTP_inputMediaDocument(
MTP_flags(flags),
document->mtpInput(),
MTP_int(media->ttlSeconds()),
MTPstring()); // query
};
takeFileReference = [=] { return document->fileReference(); };
}
const auto usedFileReference = takeFileReference
? takeFileReference()
: QByteArray();
const auto origin = item->fullId();
const auto api = &item->history()->session().api();
const auto performRequest = [=](
const auto &repeatRequest,
mtpRequestId originalRequestId) -> mtpRequestId {
const auto handleReference = [=](
const QString &error,
mtpRequestId requestId) {
if (error.startsWith(u"FILE_REFERENCE_"_q)) {
api->refreshFileReference(origin, [=](const auto &) {
if (takeFileReference &&
(takeFileReference() != usedFileReference)) {
repeatRequest(
repeatRequest,
originalRequestId
? originalRequestId
: requestId);
} else {
fail(error, requestId);
}
});
} else {
fail(error, requestId);
}
};
const auto callback = [=](
Fn<void()> applyUpdates,
mtpRequestId requestId) {
applyUpdates();
done(originalRequestId ? originalRequestId : requestId);
};
const auto requestId = EditMessage(
item,
caption,
webpage,
options,
callback,
handleReference,
takeInputMedia ? takeInputMedia() : std::nullopt);
return originalRequestId ? originalRequestId : requestId;
};
return performRequest(performRequest, 0);
}
}
const auto callback = [=](Fn<void()> applyUpdates, mtpRequestId id) {
applyUpdates();
done(id);
};
return EditMessage(item, caption, webpage, options, callback, fail);
return EditMessage(
item,
caption,
webpage,
options,
callback,
fail,
std::nullopt);
}
} // namespace Api

View File

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

View File

@@ -34,7 +34,7 @@ namespace {
constexpr auto kSharedMediaLimit = 100;
[[nodiscard]] SendMediaReady PreparePeerPhoto(
[[nodiscard]] std::shared_ptr<FilePrepareResult> PreparePeerPhoto(
MTP::DcId dcId,
PeerId peerId,
QImage &&image) {
@@ -80,24 +80,17 @@ constexpr auto kSharedMediaLimit = 100;
MTPVector<MTPVideoSize>(),
MTP_int(dcId));
QString file, filename;
int64 filesize = 0;
QByteArray data;
return SendMediaReady(
SendMediaType::Photo,
file,
filename,
filesize,
data,
id,
id,
u"jpg"_q,
peerId,
photo,
photoThumbs,
MTP_documentEmpty(MTP_long(0)),
jpeg);
auto result = MakePreparedFile({
.id = id,
.type = SendMediaType::Photo,
});
result->type = SendMediaType::Photo;
result->setFileData(jpeg);
result->thumbId = id;
result->thumbname = "thumb.jpg";
result->photo = photo;
result->photoThumbs = photoThumbs;
return result;
}
[[nodiscard]] std::optional<MTPVideoSize> PrepareMtpMarkup(
@@ -239,7 +232,7 @@ void PeerPhoto::upload(
_api.instance().mainDcId(),
peer->id,
base::take(photo.image));
_session->uploader().uploadMedia(fakeId, ready);
_session->uploader().upload(fakeId, ready);
}
}

View File

@@ -602,6 +602,41 @@ bool PremiumGiftCodeOptions::giveawayGiftsPurchaseAvailable() const {
false);
}
SponsoredToggle::SponsoredToggle(not_null<Main::Session*> session)
: _api(&session->api().instance()) {
}
rpl::producer<bool> SponsoredToggle::toggled() {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
_api.request(MTPusers_GetFullUser(
MTP_inputUserSelf()
)).done([=](const MTPusers_UserFull &result) {
consumer.put_next_copy(
result.data().vfull_user().data().is_sponsored_enabled());
}).fail([=] { consumer.put_next(false); }).send();
return lifetime;
};
}
rpl::producer<rpl::no_value, QString> SponsoredToggle::setToggled(bool v) {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
_api.request(MTPaccount_ToggleSponsoredMessages(
MTP_bool(v)
)).done([=] {
consumer.put_done();
}).fail([=](const MTP::Error &error) {
consumer.put_error_copy(error.type());
}).send();
return lifetime;
};
}
RequirePremiumState ResolveRequiresPremiumToWrite(
not_null<PeerData*> peer,
History *maybeHistory) {

View File

@@ -216,6 +216,18 @@ private:
};
class SponsoredToggle final {
public:
explicit SponsoredToggle(not_null<Main::Session*> session);
[[nodiscard]] rpl::producer<bool> toggled();
[[nodiscard]] rpl::producer<rpl::no_value, QString> setToggled(bool);
private:
MTP::Sender _api;
};
enum class RequirePremiumState {
Unknown,
Yes,

View File

@@ -24,16 +24,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Api {
namespace {
SendMediaReady PrepareRingtoneDocument(
std::shared_ptr<FilePrepareResult> PrepareRingtoneDocument(
MTP::DcId dcId,
const QString &filename,
const QString &filemime,
const QByteArray &content) {
const auto id = base::RandomValue<DocumentId>();
auto attributes = QVector<MTPDocumentAttribute>(
1,
MTP_documentAttributeFilename(MTP_string(filename)));
const auto id = base::RandomValue<DocumentId>();
const auto document = MTP_document(
auto result = MakePreparedFile({
.id = id,
.type = SendMediaType::File,
});
result->filename = filename;
result->content = content;
result->filesize = content.size();
result->setFileData(content);
result->document = MTP_document(
MTP_flags(0),
MTP_long(id),
MTP_long(0),
@@ -45,21 +54,7 @@ SendMediaReady PrepareRingtoneDocument(
MTPVector<MTPVideoSize>(),
MTP_int(dcId),
MTP_vector<MTPDocumentAttribute>(std::move(attributes)));
return SendMediaReady(
SendMediaType::File,
QString(), // filepath
filename,
content.size(),
content,
id,
0,
QString(),
PeerId(),
MTP_photoEmpty(MTP_long(0)),
PreparedPhotoThumbs(),
document,
QByteArray());
return result;
}
} // namespace
@@ -102,7 +97,7 @@ void Ringtones::upload(
_uploads.erase(already);
}
_uploads.emplace(fakeId, uploadedData);
_session->uploader().uploadMedia(fakeId, ready);
_session->uploader().upload(fakeId, ready);
}
void Ringtones::ready(const FullMsgId &msgId, const MTPInputFile &file) {

View File

@@ -353,7 +353,7 @@ void FillMessagePostFlags(
void SendConfirmedFile(
not_null<Main::Session*> session,
const std::shared_ptr<FileLoadResult> &file) {
const std::shared_ptr<FilePrepareResult> &file) {
const auto isEditing = (file->type != SendMediaType::Audio)
&& (file->to.replaceMediaOf != 0);
const auto newId = FullMsgId(

View File

@@ -14,7 +14,7 @@ class Session;
class History;
class PhotoData;
class DocumentData;
struct FileLoadResult;
struct FilePrepareResult;
namespace Api {
@@ -40,6 +40,6 @@ void FillMessagePostFlags(
void SendConfirmedFile(
not_null<Main::Session*> session,
const std::shared_ptr<FileLoadResult> &file);
const std::shared_ptr<FilePrepareResult> &file);
} // namespace Api

View File

@@ -760,14 +760,14 @@ rpl::producer<rpl::no_value, QString> EarnStatistics::request() {
channel()->inputChannel
)).done([=](const MTPstats_BroadcastRevenueStats &result) {
const auto &data = result.data();
const auto &balances = data.vbalances().data();
_data = Data::EarnStatistics{
.topHoursGraph = StatisticalGraphFromTL(
data.vtop_hours_graph()),
.revenueGraph = StatisticalGraphFromTL(data.vrevenue_graph()),
.currentBalance = data.vcurrent_balance().v,
.availableBalance = data.vavailable_balance().v,
.overallRevenue = data.voverall_revenue().v,
.currentBalance = balances.vcurrent_balance().v,
.availableBalance = balances.vavailable_balance().v,
.overallRevenue = balances.voverall_revenue().v,
.usdRate = data.vusd_rate().v,
};
@@ -860,6 +860,7 @@ void EarnStatistics::requestHistory(
.token = Data::EarnHistorySlice::OffsetToken(nextToken),
});
}).fail([=] {
done({});
_requestId = 0;
}).send();
}

View File

@@ -7,12 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_text_entities.h"
#include "main/main_session.h"
#include "data/data_document.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "data/stickers/data_custom_emoji.h"
#include "data/stickers/data_stickers_set.h"
#include "data/data_session.h"
#include "data/data_document.h"
#include "data/data_user.h"
#include "main/main_session.h"
namespace Api {
namespace {
@@ -62,67 +62,163 @@ using namespace TextUtilities;
EntitiesInText EntitiesFromMTP(
Main::Session *session,
const QVector<MTPMessageEntity> &entities) {
if (entities.isEmpty()) {
return {};
}
auto result = EntitiesInText();
if (!entities.isEmpty()) {
result.reserve(entities.size());
for (const auto &entity : entities) {
switch (entity.type()) {
case mtpc_messageEntityUrl: { auto &d = entity.c_messageEntityUrl(); result.push_back({ EntityType::Url, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityTextUrl: { auto &d = entity.c_messageEntityTextUrl(); result.push_back({ EntityType::CustomUrl, d.voffset().v, d.vlength().v, qs(d.vurl()) }); } break;
case mtpc_messageEntityEmail: { auto &d = entity.c_messageEntityEmail(); result.push_back({ EntityType::Email, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityHashtag: { auto &d = entity.c_messageEntityHashtag(); result.push_back({ EntityType::Hashtag, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityCashtag: { auto &d = entity.c_messageEntityCashtag(); result.push_back({ EntityType::Cashtag, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityPhone: break; // Skipping phones.
case mtpc_messageEntityMention: { auto &d = entity.c_messageEntityMention(); result.push_back({ EntityType::Mention, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityMentionName: if (session) {
const auto &d = entity.c_messageEntityMentionName();
const auto userId = UserId(d.vuser_id());
const auto user = session->data().userLoaded(userId);
const auto data = MentionNameDataFromFields({
.selfId = session->userId().bare,
.userId = userId.bare,
.accessHash = user ? user->accessHash() : 0,
});
result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data });
} break;
case mtpc_inputMessageEntityMentionName: if (session) {
const auto &d = entity.c_inputMessageEntityMentionName();
const auto data = d.vuser_id().match([&](
const MTPDinputUserSelf &) {
return MentionNameDataFromFields({
.selfId = session->userId().bare,
.userId = session->userId().bare,
.accessHash = session->user()->accessHash(),
});
}, [&](const MTPDinputUser &data) {
return MentionNameDataFromFields({
.selfId = session->userId().bare,
.userId = UserId(data.vuser_id()).bare,
.accessHash = data.vaccess_hash().v,
});
}, [&](const auto &) {
return QString();
});
if (!data.isEmpty()) {
result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data });
}
} break;
case mtpc_messageEntityBotCommand: { auto &d = entity.c_messageEntityBotCommand(); result.push_back({ EntityType::BotCommand, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityBold: { auto &d = entity.c_messageEntityBold(); result.push_back({ EntityType::Bold, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityItalic: { auto &d = entity.c_messageEntityItalic(); result.push_back({ EntityType::Italic, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityUnderline: { auto &d = entity.c_messageEntityUnderline(); result.push_back({ EntityType::Underline, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityStrike: { auto &d = entity.c_messageEntityStrike(); result.push_back({ EntityType::StrikeOut, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityCode: { auto &d = entity.c_messageEntityCode(); result.push_back({ EntityType::Code, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back({ EntityType::Pre, d.voffset().v, d.vlength().v, qs(d.vlanguage()) }); } break;
case mtpc_messageEntityBlockquote: { auto &d = entity.c_messageEntityBlockquote(); result.push_back({ EntityType::Blockquote, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityBankCard: break; // Skipping cards. // #TODO entities
case mtpc_messageEntitySpoiler: { auto &d = entity.c_messageEntitySpoiler(); result.push_back({ EntityType::Spoiler, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityCustomEmoji: {
const auto &d = entity.c_messageEntityCustomEmoji();
result.push_back({ EntityType::CustomEmoji, d.voffset().v, d.vlength().v, CustomEmojiEntityData(d) });
} break;
result.reserve(entities.size());
for (const auto &entity : entities) {
entity.match([&](const MTPDmessageEntityUnknown &d) {
}, [&](const MTPDmessageEntityMention &d) {
result.push_back({
EntityType::Mention,
d.voffset().v,
d.vlength().v,
});
}, [&](const MTPDmessageEntityHashtag &d) {
result.push_back({
EntityType::Hashtag,
d.voffset().v,
d.vlength().v,
});
}, [&](const MTPDmessageEntityBotCommand &d) {
result.push_back({
EntityType::BotCommand,
d.voffset().v,
d.vlength().v,
});
}, [&](const MTPDmessageEntityUrl &d) {
result.push_back({
EntityType::Url,
d.voffset().v,
d.vlength().v,
});
}, [&](const MTPDmessageEntityEmail &d) {
result.push_back({
EntityType::Email,
d.voffset().v,
d.vlength().v,
});
}, [&](const MTPDmessageEntityBold &d) {
result.push_back({
EntityType::Bold,
d.voffset().v,
d.vlength().v,
});
}, [&](const MTPDmessageEntityItalic &d) {
result.push_back({
EntityType::Italic,
d.voffset().v,
d.vlength().v,
});
}, [&](const MTPDmessageEntityCode &d) {
result.push_back({
EntityType::Code,
d.voffset().v,
d.vlength().v,
});
}, [&](const MTPDmessageEntityPre &d) {
result.push_back({
EntityType::Pre,
d.voffset().v,
d.vlength().v,
qs(d.vlanguage()),
});
}, [&](const MTPDmessageEntityTextUrl &d) {
result.push_back({
EntityType::CustomUrl,
d.voffset().v,
d.vlength().v,
qs(d.vurl()),
});
}, [&](const MTPDmessageEntityMentionName &d) {
if (!session) {
return;
}
}
const auto userId = UserId(d.vuser_id());
const auto user = session->data().userLoaded(userId);
const auto data = MentionNameDataFromFields({
.selfId = session->userId().bare,
.userId = userId.bare,
.accessHash = user ? user->accessHash() : 0,
});
result.push_back({
EntityType::MentionName,
d.voffset().v,
d.vlength().v,
data,
});
}, [&](const MTPDinputMessageEntityMentionName &d) {
if (!session) {
return;
}
const auto data = d.vuser_id().match([&](
const MTPDinputUserSelf &) {
return MentionNameDataFromFields({
.selfId = session->userId().bare,
.userId = session->userId().bare,
.accessHash = session->user()->accessHash(),
});
}, [&](const MTPDinputUser &data) {
return MentionNameDataFromFields({
.selfId = session->userId().bare,
.userId = UserId(data.vuser_id()).bare,
.accessHash = data.vaccess_hash().v,
});
}, [](const auto &) {
return QString();
});
if (!data.isEmpty()) {
result.push_back({
EntityType::MentionName,
d.voffset().v,
d.vlength().v,
data,
});
}
}, [&](const MTPDmessageEntityPhone &d) {
// Skipping phones.
}, [&](const MTPDmessageEntityCashtag &d) {
result.push_back({
EntityType::Cashtag,
d.voffset().v,
d.vlength().v,
});
}, [&](const MTPDmessageEntityUnderline &d) {
result.push_back({
EntityType::Underline,
d.voffset().v,
d.vlength().v,
});
}, [&](const MTPDmessageEntityStrike &d) {
result.push_back({
EntityType::StrikeOut,
d.voffset().v,
d.vlength().v,
});
}, [&](const MTPDmessageEntityBankCard &d) {
// Skipping cards. // #TODO entities
}, [&](const MTPDmessageEntitySpoiler &d) {
result.push_back({
EntityType::Spoiler,
d.voffset().v,
d.vlength().v,
});
}, [&](const MTPDmessageEntityCustomEmoji &d) {
result.push_back({
EntityType::CustomEmoji,
d.voffset().v,
d.vlength().v,
CustomEmojiEntityData(d),
});
}, [&](const MTPDmessageEntityBlockquote &d) {
result.push_back({
EntityType::Blockquote,
d.voffset().v,
d.vlength().v,
});
});
}
return result;
}
@@ -134,7 +230,9 @@ MTPVector<MTPMessageEntity> EntitiesToMTP(
auto v = QVector<MTPMessageEntity>();
v.reserve(entities.size());
for (const auto &entity : entities) {
if (entity.length() <= 0) continue;
if (entity.length() <= 0) {
continue;
}
if (option == ConvertOption::SkipLocal
&& entity.type() != EntityType::Bold
//&& entity.type() != EntityType::Semibold // Not in API.
@@ -154,28 +252,76 @@ MTPVector<MTPMessageEntity> EntitiesToMTP(
auto offset = MTP_int(entity.offset());
auto length = MTP_int(entity.length());
switch (entity.type()) {
case EntityType::Url: v.push_back(MTP_messageEntityUrl(offset, length)); break;
case EntityType::CustomUrl: v.push_back(MTP_messageEntityTextUrl(offset, length, MTP_string(entity.data()))); break;
case EntityType::Email: v.push_back(MTP_messageEntityEmail(offset, length)); break;
case EntityType::Hashtag: v.push_back(MTP_messageEntityHashtag(offset, length)); break;
case EntityType::Cashtag: v.push_back(MTP_messageEntityCashtag(offset, length)); break;
case EntityType::Mention: v.push_back(MTP_messageEntityMention(offset, length)); break;
case EntityType::Url: {
v.push_back(MTP_messageEntityUrl(offset, length));
} break;
case EntityType::CustomUrl: {
v.push_back(
MTP_messageEntityTextUrl(
offset,
length,
MTP_string(entity.data())));
} break;
case EntityType::Email: {
v.push_back(MTP_messageEntityEmail(offset, length));
} break;
case EntityType::Hashtag: {
v.push_back(MTP_messageEntityHashtag(offset, length));
} break;
case EntityType::Cashtag: {
v.push_back(MTP_messageEntityCashtag(offset, length));
} break;
case EntityType::Mention: {
v.push_back(MTP_messageEntityMention(offset, length));
} break;
case EntityType::MentionName: {
if (const auto valid = MentionNameEntity(session, offset, length, entity.data())) {
const auto valid = MentionNameEntity(
session,
offset,
length,
entity.data());
if (valid) {
v.push_back(*valid);
}
} break;
case EntityType::BotCommand: v.push_back(MTP_messageEntityBotCommand(offset, length)); break;
case EntityType::Bold: v.push_back(MTP_messageEntityBold(offset, length)); break;
case EntityType::Italic: v.push_back(MTP_messageEntityItalic(offset, length)); break;
case EntityType::Underline: v.push_back(MTP_messageEntityUnderline(offset, length)); break;
case EntityType::StrikeOut: v.push_back(MTP_messageEntityStrike(offset, length)); break;
case EntityType::Code: v.push_back(MTP_messageEntityCode(offset, length)); break; // #TODO entities
case EntityType::Pre: v.push_back(MTP_messageEntityPre(offset, length, MTP_string(entity.data()))); break;
case EntityType::Blockquote: v.push_back(MTP_messageEntityBlockquote(offset, length)); break;
case EntityType::Spoiler: v.push_back(MTP_messageEntitySpoiler(offset, length)); break;
case EntityType::BotCommand: {
v.push_back(MTP_messageEntityBotCommand(offset, length));
} break;
case EntityType::Bold: {
v.push_back(MTP_messageEntityBold(offset, length));
} break;
case EntityType::Italic: {
v.push_back(MTP_messageEntityItalic(offset, length));
} break;
case EntityType::Underline: {
v.push_back(MTP_messageEntityUnderline(offset, length));
} break;
case EntityType::StrikeOut: {
v.push_back(MTP_messageEntityStrike(offset, length));
} break;
case EntityType::Code: {
// #TODO entities.
v.push_back(MTP_messageEntityCode(offset, length));
} break;
case EntityType::Pre: {
v.push_back(
MTP_messageEntityPre(
offset,
length,
MTP_string(entity.data())));
} break;
case EntityType::Blockquote: {
v.push_back(MTP_messageEntityBlockquote(offset, length));
} break;
case EntityType::Spoiler: {
v.push_back(MTP_messageEntitySpoiler(offset, length));
} break;
case EntityType::CustomEmoji: {
if (const auto valid = CustomEmojiEntity(offset, length, entity.data())) {
const auto valid = CustomEmojiEntity(
offset,
length,
entity.data());
if (valid) {
v.push_back(*valid);
}
} break;

View File

@@ -21,6 +21,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/mtproto_config.h"
#include "mtproto/mtproto_dc_options.h"
#include "data/business/data_shortcut_messages.h"
#include "data/components/scheduled_messages.h"
#include "data/components/top_peers.h"
#include "data/notify/data_notify_settings.h"
#include "data/stickers/data_stickers.h"
#include "data/data_saved_messages.h"
@@ -37,7 +39,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_histories.h"
#include "data/data_folder.h"
#include "data/data_forum.h"
#include "data/data_scheduled_messages.h"
#include "data/data_send_action.h"
#include "data/data_stories.h"
#include "data/data_message_reactions.h"
@@ -94,7 +95,7 @@ void ProcessScheduledMessageWithElapsedTime(
// Note that when a message is scheduled until online
// while the recipient is already online, the server sends
// an ordinary new message with skipped "from_scheduled" flag.
session->data().scheduledMessages().checkEntitiesAndUpdate(data);
session->scheduledMessages().checkEntitiesAndUpdate(data);
}
}
@@ -1464,7 +1465,9 @@ void Updates::applyUpdates(
if (const auto id = owner.messageIdByRandomId(randomId)) {
const auto local = owner.message(id);
if (local && local->isScheduled()) {
owner.scheduledMessages().sendNowSimpleMessage(d, local);
session().scheduledMessages().sendNowSimpleMessage(
d,
local);
}
}
const auto wasAlready = (lookupMessage() != nullptr);
@@ -1561,7 +1564,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
auto &owner = session().data();
if (const auto local = owner.message(id)) {
if (local->isScheduled()) {
session().data().scheduledMessages().apply(d, local);
session().scheduledMessages().apply(d, local);
} else if (local->isBusinessShortcut()) {
session().data().shortcutMessages().apply(d, local);
} else {
@@ -1575,6 +1578,11 @@ void Updates::feedUpdate(const MTPUpdate &update) {
} else {
if (existing) {
existing->destroy();
} else {
// Not the server-side date, but close enough.
session().topPeers().increment(
local->history()->peer,
local->date());
}
local->setRealId(d.vid().v);
}
@@ -1771,12 +1779,12 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateNewScheduledMessage: {
const auto &d = update.c_updateNewScheduledMessage();
session().data().scheduledMessages().apply(d);
session().scheduledMessages().apply(d);
} break;
case mtpc_updateDeleteScheduledMessages: {
const auto &d = update.c_updateDeleteScheduledMessages();
session().data().scheduledMessages().apply(d);
session().scheduledMessages().apply(d);
} break;
case mtpc_updateQuickReplies: {

View File

@@ -307,8 +307,8 @@ void UserPrivacy::reload(Key key) {
}
void UserPrivacy::pushPrivacy(Key key, const TLRules &rules) {
const auto &saved = (_privacyValues[key] =
TLToRules(rules, _session->data()));
const auto &saved
= (_privacyValues[key] = TLToRules(rules, _session->data()));
const auto i = _privacyChanges.find(key);
if (i != end(_privacyChanges)) {
i->second.fire_copy(saved);

View File

@@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_user_names.h"
#include "api/api_websites.h"
#include "data/business/data_shortcut_messages.h"
#include "data/components/scheduled_messages.h"
#include "data/notify/data_notify_settings.h"
#include "data/data_changes.h"
#include "data/data_web_page.h"
@@ -43,7 +44,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_forum.h"
#include "data/data_saved_sublist.h"
#include "data/data_search_controller.h"
#include "data/data_scheduled_messages.h"
#include "data/data_session.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
@@ -542,7 +542,7 @@ void ApiWrap::sendMessageFail(
}
}
} else if (error == u"SCHEDULE_STATUS_PRIVATE"_q) {
auto &scheduled = _session->data().scheduledMessages();
auto &scheduled = _session->scheduledMessages();
Assert(peer->isUser());
if (const auto item = scheduled.lookupItem(peer->id, itemId.msg)) {
scheduled.removeSending(item);
@@ -1544,8 +1544,8 @@ void ApiWrap::saveStickerSets(
writeRecent = true;
}
const auto isAttached =
(removedSetId == Data::Stickers::CloudRecentAttachedSetId);
const auto isAttached
= (removedSetId == Data::Stickers::CloudRecentAttachedSetId);
const auto flags = isAttached
? MTPmessages_ClearRecentStickers::Flag::f_attached
: MTPmessages_ClearRecentStickers::Flags(0);
@@ -2447,8 +2447,8 @@ void ApiWrap::refreshFileReference(
_session->data().peer(storyId.peer)->input,
MTP_vector<MTPint>(1, MTP_int(storyId.story))));
} else if (item->isScheduled()) {
const auto &scheduled = _session->data().scheduledMessages();
const auto realId = scheduled.lookupId(item);
const auto realId = _session->scheduledMessages().lookupId(
item);
request(MTPmessages_GetScheduledMessages(
item->history()->peer->input,
MTP_vector<MTPint>(1, MTP_int(realId))));
@@ -2494,8 +2494,8 @@ void ApiWrap::refreshFileReference(
}, [&](Data::FileOriginPeerPhoto data) {
fail();
}, [&](Data::FileOriginStickerSet data) {
const auto isRecentAttached =
(data.setId == Data::Stickers::CloudRecentAttachedSetId);
const auto isRecentAttached
= (data.setId == Data::Stickers::CloudRecentAttachedSetId);
if (data.setId == Data::Stickers::CloudRecentSetId
|| data.setId == Data::Stickers::RecentSetId
|| isRecentAttached) {

View File

@@ -605,8 +605,8 @@ void GroupInfoBox::prepare() {
_navigation->session().api().selfDestruct().reload();
const auto top = addTopButton(st::infoTopBarMenu);
const auto menu =
top->lifetime().make_state<base::unique_qptr<Ui::PopupMenu>>();
const auto menu
= top->lifetime().make_state<base::unique_qptr<Ui::PopupMenu>>();
top->setClickedCallback([=] {
*menu = base::make_unique_q<Ui::PopupMenu>(
top,
@@ -1306,8 +1306,8 @@ void SetupChannelBox::handleChange() {
&& (ch < 'a' || ch > 'z')
&& (ch < '0' || ch > '9')
&& ch != '_') {
const auto badSymbols =
tr::lng_create_channel_link_bad_symbols(tr::now);
const auto badSymbols
= tr::lng_create_channel_link_bad_symbols(tr::now);
if (_errorText != badSymbols) {
_errorText = badSymbols;
update();
@@ -1317,8 +1317,8 @@ void SetupChannelBox::handleChange() {
}
}
if (name.size() < Ui::EditPeer::kMinUsernameLength) {
const auto tooShort =
tr::lng_create_channel_link_too_short(tr::now);
const auto tooShort
= tr::lng_create_channel_link_too_short(tr::now);
if (_errorText != tooShort) {
_errorText = tooShort;
update();

View File

@@ -593,11 +593,11 @@ void BackgroundPreviewBox::uploadForPeer(bool both) {
const auto ready = Window::Theme::PrepareWallPaper(
session->mainDcId(),
_paper.localThumbnail()->original());
const auto documentId = ready.id;
const auto documentId = ready->id;
_uploadId = FullMsgId(
session->userPeerId(),
session->data().nextLocalMessageId());
session->uploader().uploadMedia(_uploadId, ready);
session->uploader().upload(_uploadId, ready);
if (_uploadLifetime) {
return;
}
@@ -940,7 +940,7 @@ int BackgroundPreviewBox::textsTop() const {
- st::historyPaddingBottom
- (_service ? _service->height() : 0)
- _text1->height()
- (forChannel() ? _text2->height() : 0);
- (forChannel() ? 0 : _text2->height());
}
QRect BackgroundPreviewBox::radialRect() const {

View File

@@ -546,7 +546,7 @@ rightsToggle: Toggle(defaultToggle) {
vsize: 5px;
vshift: 1px;
stroke: 2px;
duration: 120;
duration: universalDuration;
}
rightsButton: SettingsButton(defaultSettingsButton) {
@@ -691,6 +691,10 @@ createPollOptionField: InputField(createPollField) {
placeholderMargins: margins(2px, 0px, 2px, 0px);
heightMax: 68px;
}
createPollOptionFieldPremium: InputField(createPollOptionField) {
textMargins: margins(22px, 11px, 68px, 11px);
}
createPollOptionFieldPremiumEmojiPosition: point(15px, -1px);
createPollSolutionField: InputField(createPollField) {
textMargins: margins(0px, 4px, 0px, 4px);
border: 1px;
@@ -704,7 +708,7 @@ createPollOptionRemove: CrossButton {
cross: CrossAnimation {
size: 22px;
skip: 6px;
stroke: 1.;
stroke: 1.5;
minScale: 0.3;
}
crossFg: boxTitleCloseFg;
@@ -718,6 +722,7 @@ createPollOptionRemove: CrossButton {
}
}
createPollOptionRemovePosition: point(11px, 9px);
createPollOptionEmojiPositionSkip: 4px;
createPollWarning: FlatLabel(defaultFlatLabel) {
textFg: windowSubTextFg;
palette: TextPalette(defaultTextPalette) {
@@ -1074,3 +1079,23 @@ collectibleBox: Box(defaultBox) {
buttonHeight: 36px;
button: collectibleCopy;
}
moderateBoxUserpic: UserpicButton(defaultUserpicButton) {
size: size(34px, 42px);
photoSize: 34px;
photoPosition: point(0px, 4px);
}
moderateBoxExpand: icon {{ "chat/reply_type_group", boxTextFg }};
moderateBoxExpandHeight: 20px;
moderateBoxExpandRight: 10px;
moderateBoxExpandInnerSkip: 2px;
moderateBoxExpandFont: font(11px);
moderateBoxExpandToggleSize: 4px;
moderateBoxExpandToggleFourStrokes: 3px;
moderateBoxExpandIcon: icon{{ "info/edit/expand_arrow_small-flip_vertical", windowActiveTextFg }};
moderateBoxExpandIconDown: icon{{ "info/edit/expand_arrow_small", windowActiveTextFg }};
moderateBoxDividerLabel: FlatLabel(boxDividerLabel) {
palette: TextPalette(defaultTextPalette) {
selectLinkFg: windowActiveTextFg;
}
}

View File

@@ -7,33 +7,41 @@ 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/wrap/fade_wrap.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/text/text_utilities.h"
#include "ui/vertical_list.h"
#include "main/main_session.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "base/call_delayed.h"
#include "base/event_filter.h"
#include "base/random.h"
#include "base/unique_qptr.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/message_field.h"
#include "menu/menu_send.h"
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "data/data_poll.h"
#include "data/data_user.h"
#include "data/stickers/data_custom_emoji.h"
#include "history/view/history_view_schedule_box.h"
#include "base/unique_qptr.h"
#include "base/event_filter.h"
#include "base/call_delayed.h"
#include "base/random.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "menu/menu_send.h"
#include "ui/controls/emoji_button.h"
#include "ui/rect.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/vertical_list.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/shadow.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "window/window_session_controller.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h" // defaultComposeFiles.
#include "styles/style_layers.h"
#include "styles/style_settings.h"
namespace {
@@ -46,12 +54,107 @@ constexpr auto kSolutionLimit = 200;
constexpr auto kWarnSolutionLimit = 60;
constexpr auto kErrorLimit = 99;
[[nodiscard]] not_null<Ui::EmojiButton*> AddEmojiToggleToField(
not_null<Ui::InputField*> field,
not_null<Ui::BoxContent*> box,
not_null<Window::SessionController*> controller,
not_null<ChatHelpers::TabbedPanel*> emojiPanel,
QPoint shift) {
const auto emojiToggle = Ui::CreateChild<Ui::EmojiButton>(
field->parentWidget(),
st::defaultComposeFiles.emoji);
const auto fade = Ui::CreateChild<Ui::FadeAnimation>(
emojiToggle,
emojiToggle,
0.5);
{
const auto fadeTarget = Ui::CreateChild<Ui::RpWidget>(emojiToggle);
fadeTarget->resize(emojiToggle->size());
fadeTarget->paintRequest(
) | rpl::start_with_next([=](const QRect &rect) {
auto p = QPainter(fadeTarget);
if (fade->animating()) {
p.fillRect(fadeTarget->rect(), st::boxBg);
}
fade->paint(p);
}, fadeTarget->lifetime());
rpl::single(false) | rpl::then(
field->focusedChanges()
) | rpl::start_with_next([=](bool shown) {
if (shown) {
fade->fadeIn(st::universalDuration);
} else {
fade->fadeOut(st::universalDuration);
}
}, emojiToggle->lifetime());
fade->fadeOut(1);
fade->finish();
}
const auto outer = box->getDelegate()->outerContainer();
const auto allow = [](not_null<DocumentData*>) { return true; };
InitMessageFieldHandlers(
controller,
field,
Window::GifPauseReason::Layer,
allow);
Ui::Emoji::SuggestionsController::Init(
outer,
field,
&controller->session(),
Ui::Emoji::SuggestionsController::Options{
.suggestCustomEmoji = true,
.allowCustomWithoutPremium = allow,
});
const auto updateEmojiPanelGeometry = [=] {
const auto parent = emojiPanel->parentWidget();
const auto global = emojiToggle->mapToGlobal({ 0, 0 });
const auto local = parent->mapFromGlobal(global);
const auto right = local.x() + emojiToggle->width() * 3;
const auto isDropDown = local.y() < parent->height() / 2;
emojiPanel->setDropDown(isDropDown);
if (isDropDown) {
emojiPanel->moveTopRight(
local.y() + emojiToggle->height(),
right);
} else {
emojiPanel->moveBottomRight(local.y(), right);
}
};
rpl::combine(
box->sizeValue(),
field->geometryValue()
) | rpl::start_with_next([=](QSize outer, QRect inner) {
emojiToggle->moveToLeft(
rect::right(inner) + shift.x(),
inner.y() + shift.y());
emojiToggle->update();
}, emojiToggle->lifetime());
emojiToggle->installEventFilter(emojiPanel);
emojiToggle->addClickHandler([=] {
updateEmojiPanelGeometry();
emojiPanel->toggleAnimated();
});
const auto filterCallback = [=](not_null<QEvent*> event) {
if (event->type() == QEvent::Enter) {
updateEmojiPanelGeometry();
}
return base::EventFilterResult::Continue;
};
base::install_event_filter(emojiToggle, filterCallback);
return emojiToggle;
}
class Options {
public:
Options(
not_null<QWidget*> outer,
not_null<Ui::BoxContent*> box,
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session,
not_null<Window::SessionController*> controller,
ChatHelpers::TabbedPanel *emojiPanel,
bool chooseCorrectEnabled);
[[nodiscard]] bool hasOptions() const;
@@ -140,9 +243,10 @@ private:
[[nodiscard]] auto createChooseCorrectGroup()
-> std::shared_ptr<Ui::RadiobuttonGroup>;
not_null<QWidget*> _outer;
not_null<Ui::BoxContent*> _box;
not_null<Ui::VerticalLayout*> _container;
const not_null<Main::Session*> _session;
const not_null<Window::SessionController*> _controller;
ChatHelpers::TabbedPanel * const _emojiPanel;
std::shared_ptr<Ui::RadiobuttonGroup> _chooseCorrectGroup;
int _position = 0;
std::vector<std::unique_ptr<Option>> _list;
@@ -154,6 +258,7 @@ private:
rpl::event_stream<not_null<QWidget*>> _scrollToWidget;
rpl::event_stream<> _backspaceInFront;
rpl::event_stream<> _tabbed;
rpl::lifetime _emojiPanelLifetime;
};
@@ -193,8 +298,9 @@ not_null<Ui::FlatLabel*> CreateWarningLabel(
if (value >= 0) {
result->setText(QString::number(value));
} else {
constexpr auto kMinus = QChar(0x2212);
result->setMarkedText(Ui::Text::Colorized(
QString::number(value)));
kMinus + QString::number(std::abs(value))));
}
result->setVisible(shown);
}));
@@ -223,7 +329,9 @@ Options::Option::Option(
, _field(
Ui::CreateChild<Ui::InputField>(
_content.get(),
st::createPollOptionField,
session->user()->isPremium()
? st::createPollOptionFieldPremium
: st::createPollOptionField,
Ui::InputField::Mode::NoNewlines,
tr::lng_polls_create_option_add())) {
InitField(outer, _field, session);
@@ -299,7 +407,7 @@ void Options::Option::createRemove() {
const auto remove = Ui::CreateChild<Ui::CrossButton>(
field.get(),
st::createPollOptionRemove);
remove->hide(anim::type::instant);
remove->show(anim::type::instant);
const auto toggle = lifetime.make_state<rpl::variable<bool>>(false);
_removeAlways = lifetime.make_state<rpl::variable<bool>>(false);
@@ -309,6 +417,7 @@ void Options::Option::createRemove() {
// Don't capture 'this'! Because Option is a value type.
*toggle = !field->getLastText().isEmpty();
}, field->lifetime());
#if 0
rpl::combine(
toggle->value(),
_removeAlways->value(),
@@ -316,6 +425,7 @@ void Options::Option::createRemove() {
) | rpl::start_with_next([=](bool shown) {
remove->toggle(shown, anim::type::normal);
}, remove->lifetime());
#endif
field->widthValue(
) | rpl::start_with_next([=](int width) {
@@ -456,10 +566,16 @@ void Options::Option::removePlaceholder() const {
PollAnswer Options::Option::toPollAnswer(int index) const {
Expects(index >= 0 && index < kMaxOptionsCount);
const auto text = field()->getTextWithTags();
auto result = PollAnswer{
field()->getLastText().trimmed(),
QByteArray(1, ('0' + index))
TextWithEntities{
.text = text.text,
.entities = TextUtilities::ConvertTextTagsToEntities(text.tags),
},
QByteArray(1, ('0' + index)),
};
TextUtilities::Trim(result.text);
result.correct = _correct ? _correct->entity()->Checkbox::checked() : false;
return result;
}
@@ -469,13 +585,15 @@ rpl::producer<Qt::MouseButton> Options::Option::removeClicks() const {
}
Options::Options(
not_null<QWidget*> outer,
not_null<Ui::BoxContent*> box,
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session,
not_null<Window::SessionController*> controller,
ChatHelpers::TabbedPanel *emojiPanel,
bool chooseCorrectEnabled)
: _outer(outer)
: _box(box)
, _container(container)
, _session(session)
, _controller(controller)
, _emojiPanel(emojiPanel)
, _chooseCorrectGroup(chooseCorrectEnabled
? createChooseCorrectGroup()
: nullptr)
@@ -645,12 +763,40 @@ void Options::addEmptyOption() {
(*(_list.end() - 2))->toggleRemoveAlways(true);
}
_list.push_back(std::make_unique<Option>(
_outer,
_box,
_container,
_session,
&_controller->session(),
_position + _list.size() + _destroyed.size(),
_chooseCorrectGroup));
const auto field = _list.back()->field();
if (const auto emojiPanel = _emojiPanel) {
const auto emojiToggle = AddEmojiToggleToField(
field,
_box,
_controller,
emojiPanel,
QPoint(
-st::createPollOptionFieldPremium.textMargins.right(),
st::createPollOptionEmojiPositionSkip));
emojiToggle->shownValue() | rpl::start_with_next([=](bool shown) {
if (!shown) {
return;
}
_emojiPanelLifetime.destroy();
emojiPanel->selector()->emojiChosen(
) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
if (field->hasFocus()) {
Ui::InsertEmojiAtCursor(field->textCursor(), data.emoji);
}
}, _emojiPanelLifetime);
emojiPanel->selector()->customEmojiChosen(
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
if (field->hasFocus()) {
Data::InsertCustomEmoji(field, data.document);
}
}, _emojiPanelLifetime);
}, emojiToggle->lifetime());
}
field->submits(
) | rpl::start_with_next([=] {
const auto index = findField(field);
@@ -697,7 +843,7 @@ void Options::addEmptyOption() {
});
_list.back()->removeClicks(
) | rpl::take(1) | rpl::start_with_next([=] {
) | rpl::start_with_next([=] {
Ui::PostponeCall(crl::guard(field, [=] {
Expects(!_list.empty());
@@ -789,19 +935,63 @@ not_null<Ui::InputField*> CreatePollBox::setupQuestion(
using namespace Settings;
const auto session = &_controller->session();
const auto isPremium = session->user()->isPremium();
Ui::AddSubsectionTitle(container, tr::lng_polls_create_question());
const auto question = container->add(
object_ptr<Ui::InputField>(
container,
st::createPollField,
Ui::InputField::Mode::MultiLine,
tr::lng_polls_create_question_placeholder()),
st::createPollFieldPadding);
st::createPollFieldPadding
+ (isPremium
? QMargins(0, 0, st::defaultComposeFiles.emoji.inner.width, 0)
: QMargins()));
InitField(getDelegate()->outerContainer(), question, session);
question->setMaxLength(kQuestionLimit + kErrorLimit);
question->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
question->customTab(true);
if (isPremium) {
using Selector = ChatHelpers::TabbedSelector;
const auto outer = getDelegate()->outerContainer();
_emojiPanel = base::make_unique_q<ChatHelpers::TabbedPanel>(
outer,
_controller,
object_ptr<Selector>(
nullptr,
_controller->uiShow(),
Window::GifPauseReason::Layer,
Selector::Mode::EmojiOnly));
const auto emojiPanel = _emojiPanel.get();
emojiPanel->setDesiredHeightValues(
1.,
st::emojiPanMinHeight / 2,
st::emojiPanMinHeight);
emojiPanel->hide();
emojiPanel->selector()->setCurrentPeer(session->user());
const auto emojiToggle = AddEmojiToggleToField(
question,
this,
_controller,
emojiPanel,
st::createPollOptionFieldPremiumEmojiPosition);
emojiPanel->selector()->emojiChosen(
) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
if (question->hasFocus()) {
Ui::InsertEmojiAtCursor(question->textCursor(), data.emoji);
}
}, emojiToggle->lifetime());
emojiPanel->selector()->customEmojiChosen(
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
if (question->hasFocus()) {
Data::InsertCustomEmoji(question, data.document);
}
}, emojiToggle->lifetime());
}
const auto warning = CreateWarningLabel(
container,
question,
@@ -910,9 +1100,10 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
st::defaultSubsectionTitle),
st::createPollFieldTitlePadding);
const auto options = lifetime().make_state<Options>(
getDelegate()->outerContainer(),
this,
container,
&_controller->session(),
_controller,
_emojiPanel ? _emojiPanel.get() : nullptr,
(_chosen & PollData::Flag::Quiz));
auto limit = options->usedCount() | rpl::after_next([=](int count) {
setCloseByEscape(!count);
@@ -1029,9 +1220,13 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
};
const auto collectResult = [=] {
const auto textWithTags = question->getTextWithTags();
using Flag = PollData::Flag;
auto result = PollData(&_controller->session().data(), id);
result.question = question->getLastText().trimmed();
result.question.text = textWithTags.text;
result.question.entities = TextUtilities::ConvertTextTagsToEntities(
textWithTags.tags);
TextUtilities::Trim(result.question);
result.answers = options->toPollAnswers();
const auto solutionWithTags = quiz->checked()
? solution->getTextWithAppliedMarkdown()

View File

@@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
struct PollData;
namespace ChatHelpers {
class TabbedPanel;
} // namespace ChatHelpers
namespace Ui {
class VerticalLayout;
} // namespace Ui
@@ -72,6 +76,7 @@ private:
const PollData::Flags _disabled = PollData::Flags();
const Api::SendType _sendType = Api::SendType();
const SendMenu::Type _sendMenuType;
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;
Fn<void()> _setInnerFocus;
Fn<rpl::producer<bool>()> _dataIsValidValue;
rpl::event_stream<Result> _submitRequests;

View File

@@ -232,8 +232,8 @@ void DeleteMessagesBox::prepare() {
if (hasScheduledMessages()) {
} else if (auto revoke = revokeText(peer)) {
const auto &settings = Core::App().settings();
const auto revokeByDefault =
!settings.rememberedDeleteMessageOnlyForYou();
const auto revokeByDefault
= !settings.rememberedDeleteMessageOnlyForYou();
_revoke.create(
this,
revoke->checkbox,
@@ -379,13 +379,7 @@ auto DeleteMessagesBox::revokeText(not_null<PeerData*> peer) const
return result;
}
const auto items = ranges::views::all(
_ids
) | ranges::views::transform([&](FullMsgId id) {
return peer->owner().message(id);
}) | ranges::views::filter([](HistoryItem *item) {
return (item != nullptr);
}) | ranges::to_vector;
const auto items = peer->owner().idsToItems(_ids);
if (items.size() != _ids.size()) {
// We don't have information about all messages.

View File

@@ -206,6 +206,9 @@ void EditPhotoImage(
Storage::UpdateImageDetails(file, previewWidth, sideLimit);
done(std::move(list));
};
if (!large) {
return;
}
const auto fileImage = std::make_shared<Image>(*large);
auto editor = base::make_unique_q<Editor::PhotoEditor>(
parent,

View File

@@ -159,10 +159,7 @@ public:
-> rpl::producer<RowSelectionChange>;
private:
[[nodiscard]] std::unique_ptr<PeerListRow> createRow() const;
const not_null<Main::Session*> _session;
bool _premiums = false;
rpl::event_stream<> _selectionChanged;
rpl::event_stream<RowSelectionChange> _rowSelectionChanges;
@@ -209,8 +206,7 @@ bool PremiumsRow::useForumLikeUserpic() const {
TypesController::TypesController(
not_null<Main::Session*> session,
bool premiums)
: _session(session)
, _premiums(premiums) {
: _session(session) {
}
Main::Session &TypesController::session() const {
@@ -331,6 +327,9 @@ auto PrivacyExceptionsBoxController::preparePremiumsRowList()
_deselectOption = [=](PeerListRowId itemId) {
if (const auto row = _typesDelegate->peerListFindRow(itemId)) {
if (itemId == kPremiumsRowId) {
_selected.premiums = false;
}
_typesDelegate->peerListSetRowChecked(row, false);
}
};

View File

@@ -16,7 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peers/prepare_short_info_box.h"
#include "boxes/peers/replace_boost_box.h" // BoostsForGift.
#include "boxes/premium_preview_box.h" // ShowPremiumPreviewBox.
#include "core/ui_integration.h" // Core::MarkedTextContext.
#include "data/data_boosts.h"
#include "data/data_changes.h"
#include "data/data_channel.h"
@@ -48,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/gradient_round_button.h"
#include "ui/widgets/label_with_custom_emoji.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/table_layout.h"
@@ -319,21 +319,20 @@ void GiftBox(
std::move(titleLabel)),
st::premiumGiftTitlePadding);
auto textLabel = object_ptr<Ui::FlatLabel>(box, st::premiumPreviewAbout);
tr::lng_premium_gift_about(
lt_user,
user->session().changes().peerFlagsValue(
user,
Data::PeerUpdate::Flag::Name
) | rpl::map([=] { return TextWithEntities{ user->firstName }; }),
Ui::Text::RichLangValue
) | rpl::map(
BoostsForGiftText({ user })
) | rpl::start_with_next([
raw = textLabel.data(),
session = &user->session()](const TextWithEntities &t) {
raw->setMarkedText(t, Core::MarkedTextContext{ .session = session });
}, textLabel->lifetime());
auto textLabel = Ui::CreateLabelWithCustomEmoji(
box,
tr::lng_premium_gift_about(
lt_user,
user->session().changes().peerFlagsValue(
user,
Data::PeerUpdate::Flag::Name
) | rpl::map([=] { return TextWithEntities{ user->firstName }; }),
Ui::Text::RichLangValue
) | rpl::map(
BoostsForGiftText({ user })
),
{ .session = &user->session() },
st::premiumPreviewAbout);
textLabel->setTextColorOverride(stTitle.textFg->c);
textLabel->resizeToWidth(available);
box->addRow(
@@ -536,14 +535,12 @@ void GiftsBox(
const auto label = box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
box,
object_ptr<Ui::FlatLabel>(box, st::premiumPreviewAbout)),
Ui::CreateLabelWithCustomEmoji(
box,
std::move(text),
{ .session = session },
st::premiumPreviewAbout)),
padding)->entity();
std::move(
text
) | rpl::start_with_next([=](const TextWithEntities &t) {
using namespace Core;
label->setMarkedText(t, MarkedTextContext{ .session = session });
}, label->lifetime());
label->setTextColorOverride(stTitle.textFg->c);
label->resizeToWidth(available);
}

View File

@@ -0,0 +1,856 @@
/*
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/moderate_messages_box.h"
#include "api/api_chat_participants.h"
#include "api/api_messages_search.h"
#include "apiwrap.h"
#include "base/event_filter.h"
#include "base/timer.h"
#include "boxes/delete_messages_box.h"
#include "boxes/peers/edit_peer_permissions_box.h"
#include "core/application.h"
#include "core/ui_integration.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_chat_participant_status.h"
#include "data/data_histories.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "data/stickers/data_custom_emoji.h"
#include "history/history.h"
#include "history/history_item.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/boxes/confirm_box.h"
#include "ui/controls/userpic_button.h"
#include "ui/effects/ripple_animation.h"
#include "ui/effects/toggle_arrow.h"
#include "ui/layers/generic_box.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/rect_part.h"
#include "ui/text/text_utilities.h"
#include "ui/vertical_list.h"
#include "ui/widgets/checkbox.h"
#include "ui/wrap/slide_wrap.h"
#include "styles/style_boxes.h"
#include "styles/style_layers.h"
#include "styles/style_window.h"
namespace {
using Participants = std::vector<not_null<PeerData*>>;
struct ModerateOptions final {
bool allCanBan = false;
bool allCanDelete = false;
Participants participants;
};
ModerateOptions CalculateModerateOptions(const HistoryItemsList &items) {
Expects(!items.empty());
auto result = ModerateOptions{
.allCanBan = true,
.allCanDelete = true,
};
const auto peer = items.front()->history()->peer;
for (const auto &item : items) {
if (!result.allCanBan && !result.allCanDelete) {
return {};
}
if (peer != item->history()->peer) {
return {};
}
if (!item->suggestBanReport()) {
result.allCanBan = false;
}
if (!item->suggestDeleteAllReport()) {
result.allCanDelete = false;
}
if (const auto p = item->from()) {
if (!ranges::contains(result.participants, not_null{ p })) {
result.participants.push_back(p);
}
}
}
return result;
}
[[nodiscard]] rpl::producer<int> MessagesCountValue(
not_null<History*> history,
not_null<PeerData*> from) {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
auto search = lifetime.make_state<Api::MessagesSearch>(history);
consumer.put_next(0);
search->messagesFounds(
) | rpl::start_with_next([=](const Api::FoundMessages &found) {
consumer.put_next_copy(found.total);
}, lifetime);
search->searchMessages({ .from = from });
return lifetime;
};
}
class Button final : public Ui::RippleButton {
public:
Button(not_null<QWidget*> parent, int count);
void setChecked(bool checked);
[[nodiscard]] bool checked() const;
[[nodiscard]] static QSize ComputeSize(int);
private:
void paintEvent(QPaintEvent *event) override;
QImage prepareRippleMask() const override;
QPoint prepareRippleStartPosition() const override;
const int _count;
const QString _text;
bool _checked = false;
Ui::Animations::Simple _animation;
};
Button::Button(not_null<QWidget*> parent, int count)
: RippleButton(parent, st::defaultRippleAnimation)
, _count(count)
, _text(QString::number(std::abs(_count))) {
}
QSize Button::ComputeSize(int count) {
return QSize(
st::moderateBoxExpandHeight
+ st::moderateBoxExpand.width()
+ st::moderateBoxExpandInnerSkip * 4
+ st::moderateBoxExpandFont->width(
QString::number(std::abs(count)))
+ st::moderateBoxExpandToggleSize,
st::moderateBoxExpandHeight);
}
void Button::setChecked(bool checked) {
if (_checked == checked) {
return;
}
_checked = checked;
_animation.stop();
_animation.start(
[=] { update(); },
checked ? 0 : 1,
checked ? 1 : 0,
st::slideWrapDuration);
}
bool Button::checked() const {
return _checked;
}
void Button::paintEvent(QPaintEvent *event) {
auto p = Painter(this);
auto hq = PainterHighQualityEnabler(p);
Ui::RippleButton::paintRipple(p, QPoint());
const auto radius = height() / 2;
p.setPen(Qt::NoPen);
st::moderateBoxExpand.paint(
p,
radius,
(height() - st::moderateBoxExpand.height()) / 2,
width());
const auto innerSkip = st::moderateBoxExpandInnerSkip;
p.setBrush(Qt::NoBrush);
p.setPen(st::boxTextFg);
p.setFont(st::moderateBoxExpandFont);
p.drawText(
QRect(
innerSkip + radius + st::moderateBoxExpand.width(),
0,
width(),
height()),
_text,
style::al_left);
const auto path = Ui::ToggleUpDownArrowPath(
width() - st::moderateBoxExpandToggleSize - radius,
height() / 2,
st::moderateBoxExpandToggleSize,
st::moderateBoxExpandToggleFourStrokes,
_animation.value(_checked ? 1. : 0.));
p.fillPath(path, st::boxTextFg);
}
QImage Button::prepareRippleMask() const {
return Ui::RippleAnimation::RoundRectMask(size(), size().height() / 2);
}
QPoint Button::prepareRippleStartPosition() const {
return mapFromGlobal(QCursor::pos());
}
} // namespace
void CreateModerateMessagesBox(
not_null<Ui::GenericBox*> box,
const HistoryItemsList &items,
Fn<void()> confirmed) {
struct Controller final {
rpl::event_stream<bool> toggleRequestsFromTop;
rpl::event_stream<bool> toggleRequestsFromInner;
rpl::event_stream<bool> checkAllRequests;
Fn<Participants()> collectRequests;
};
const auto [allCanBan, allCanDelete, participants]
= CalculateModerateOptions(items);
const auto inner = box->verticalLayout();
Assert(!participants.empty());
const auto confirms = inner->lifetime().make_state<rpl::event_stream<>>();
const auto isSingle = participants.size() == 1;
const auto buttonPadding = isSingle
? QMargins()
: QMargins(0, 0, Button::ComputeSize(participants.size()).width(), 0);
const auto session = &items.front()->history()->session();
const auto historyPeerId = items.front()->history()->peer->id;
using Request = Fn<void(not_null<PeerData*>, not_null<ChannelData*>)>;
const auto sequentiallyRequest = [=](
Request request,
Participants participants) {
constexpr auto kSmallDelayMs = 5;
const auto participantIds = ranges::views::all(
participants
) | ranges::views::transform([](not_null<PeerData*> peer) {
return peer->id;
}) | ranges::to_vector;
const auto lifetime = std::make_shared<rpl::lifetime>();
const auto counter = lifetime->make_state<int>(0);
const auto timer = lifetime->make_state<base::Timer>();
timer->setCallback(crl::guard(session, [=] {
if ((*counter) < participantIds.size()) {
const auto peer = session->data().peer(historyPeerId);
const auto channel = peer ? peer->asChannel() : nullptr;
const auto from = session->data().peer(
participantIds[*counter]);
if (channel && from) {
request(from, channel);
}
(*counter)++;
} else {
lifetime->destroy();
}
}));
timer->callEach(kSmallDelayMs);
};
const auto handleConfirmation = [=](
not_null<Ui::Checkbox*> checkbox,
not_null<Controller*> controller,
Request request) {
confirms->events() | rpl::start_with_next([=] {
if (checkbox->checked() && controller->collectRequests) {
sequentiallyRequest(request, controller->collectRequests());
}
}, checkbox->lifetime());
};
const auto isEnter = [=](not_null<QEvent*> event) {
if (event->type() == QEvent::KeyPress) {
if (const auto k = static_cast<QKeyEvent*>(event.get())) {
return (k->key() == Qt::Key_Enter)
|| (k->key() == Qt::Key_Return);
}
}
return false;
};
base::install_event_filter(box, [=](not_null<QEvent*> event) {
if (isEnter(event)) {
box->triggerButton(0);
return base::EventFilterResult::Cancel;
}
return base::EventFilterResult::Continue;
});
const auto handleSubmition = [=](not_null<Ui::Checkbox*> checkbox) {
base::install_event_filter(box, [=](not_null<QEvent*> event) {
if (!isEnter(event) || !checkbox->checked()) {
return base::EventFilterResult::Continue;
}
box->uiShow()->show(Ui::MakeConfirmBox({
.text = tr::lng_gigagroup_warning_title(),
.confirmed = [=](Fn<void()> close) {
box->triggerButton(0);
close();
},
.confirmText = tr::lng_box_yes(),
.cancelText = tr::lng_box_no(),
}));
return base::EventFilterResult::Cancel;
});
};
const auto createParticipantsList = [&](
not_null<Controller*> controller) {
const auto wrap = inner->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
inner,
object_ptr<Ui::VerticalLayout>(inner)));
wrap->toggle(false, anim::type::instant);
controller->toggleRequestsFromTop.events(
) | rpl::start_with_next([=](bool toggled) {
wrap->toggle(toggled, anim::type::normal);
}, wrap->lifetime());
const auto container = wrap->entity();
Ui::AddSkip(container);
auto &lifetime = wrap->lifetime();
const auto clicks = lifetime.make_state<rpl::event_stream<>>();
const auto checkboxes = ranges::views::all(
participants
) | ranges::views::transform([&](not_null<PeerData*> peer) {
const auto line = container->add(
object_ptr<Ui::AbstractButton>(container));
const auto &st = st::moderateBoxUserpic;
line->resize(line->width(), st.size.height());
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
line,
peer,
st);
const auto checkbox = Ui::CreateChild<Ui::Checkbox>(
line,
peer->name(),
false,
st::defaultBoxCheckbox);
line->widthValue(
) | rpl::start_with_next([=](int width) {
userpic->moveToLeft(
st::boxRowPadding.left()
+ checkbox->checkRect().width()
+ st::defaultBoxCheckbox.textPosition.x(),
0);
const auto skip = st::defaultBoxCheckbox.textPosition.x();
checkbox->resizeToWidth(width
- rect::right(userpic)
- skip
- st::boxRowPadding.right());
checkbox->moveToLeft(
rect::right(userpic) + skip,
((userpic->height() - checkbox->height()) / 2)
+ st::defaultBoxCheckbox.margin.top());
}, checkbox->lifetime());
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
checkbox->setAttribute(Qt::WA_TransparentForMouseEvents);
line->setClickedCallback([=] {
checkbox->setChecked(!checkbox->checked());
clicks->fire({});
});
return checkbox;
}) | ranges::to_vector;
clicks->events(
) | rpl::start_with_next([=] {
controller->toggleRequestsFromInner.fire_copy(
ranges::any_of(checkboxes, &Ui::Checkbox::checked));
}, container->lifetime());
controller->checkAllRequests.events(
) | rpl::start_with_next([=](bool checked) {
for (const auto &c : checkboxes) {
c->setChecked(checked);
}
}, container->lifetime());
controller->collectRequests = [=] {
auto result = Participants();
for (auto i = 0; i < checkboxes.size(); i++) {
if (checkboxes[i]->checked()) {
result.push_back(participants[i]);
}
}
return result;
};
};
const auto appendList = [&](
not_null<Ui::Checkbox*> checkbox,
not_null<Controller*> controller) {
if (isSingle) {
const auto p = participants.front();
controller->collectRequests = [=] { return Participants{ p }; };
return;
}
const auto count = int(participants.size());
const auto button = Ui::CreateChild<Button>(inner, count);
button->resize(Button::ComputeSize(count));
const auto overlay = Ui::CreateChild<Ui::AbstractButton>(inner);
checkbox->geometryValue(
) | rpl::start_with_next([=](const QRect &rect) {
overlay->setGeometry(rect);
overlay->raise();
button->moveToRight(
st::moderateBoxExpandRight,
rect.top() + (rect.height() - button->height()) / 2,
box->width());
button->raise();
}, button->lifetime());
controller->toggleRequestsFromInner.events(
) | rpl::start_with_next([=](bool toggled) {
checkbox->setChecked(toggled);
}, checkbox->lifetime());
button->setClickedCallback([=] {
button->setChecked(!button->checked());
controller->toggleRequestsFromTop.fire_copy(button->checked());
});
overlay->setClickedCallback([=] {
checkbox->setChecked(!checkbox->checked());
controller->checkAllRequests.fire_copy(checkbox->checked());
});
createParticipantsList(controller);
};
Ui::AddSkip(inner);
const auto title = box->addRow(
object_ptr<Ui::FlatLabel>(
box,
(items.size() == 1)
? tr::lng_selected_delete_sure_this()
: tr::lng_selected_delete_sure(
lt_count,
rpl::single(items.size()) | tr::to_count()),
st::boxLabel));
Ui::AddSkip(inner);
Ui::AddSkip(inner);
Ui::AddSkip(inner);
{
const auto report = box->addRow(
object_ptr<Ui::Checkbox>(
box,
tr::lng_report_spam(tr::now),
false,
st::defaultBoxCheckbox),
st::boxRowPadding + buttonPadding);
const auto controller = box->lifetime().make_state<Controller>();
appendList(report, controller);
handleSubmition(report);
const auto ids = items.front()->from()->owner().itemsToIds(items);
handleConfirmation(report, controller, [=](
not_null<PeerData*> p,
not_null<ChannelData*> c) {
auto filtered = ranges::views::all(
ids
) | ranges::views::transform([](const FullMsgId &id) {
return MTP_int(id.msg);
}) | ranges::to<QVector<MTPint>>();
c->session().api().request(
MTPchannels_ReportSpam(
c->inputChannel,
p->input,
MTP_vector<MTPint>(std::move(filtered)))
).send();
});
}
if (allCanDelete) {
Ui::AddSkip(inner);
Ui::AddSkip(inner);
const auto deleteAll = inner->add(
object_ptr<Ui::Checkbox>(
inner,
!(isSingle)
? tr::lng_delete_all_from_users(
tr::now,
Ui::Text::WithEntities)
: tr::lng_delete_all_from_user(
tr::now,
lt_user,
Ui::Text::Bold(items.front()->from()->name()),
Ui::Text::WithEntities),
false,
st::defaultBoxCheckbox),
st::boxRowPadding + buttonPadding);
if (isSingle) {
const auto history = items.front()->history();
tr::lng_selected_delete_sure(
lt_count,
rpl::combine(
MessagesCountValue(history, participants.front()),
deleteAll->checkedValue()
) | rpl::map([s = items.size()](int all, bool checked) {
return float64((checked && all) ? all : s);
})
) | rpl::start_with_next([=](const QString &text) {
title->setText(text);
title->resizeToWidth(inner->width()
- rect::m::sum::h(st::boxRowPadding));
}, title->lifetime());
}
const auto controller = box->lifetime().make_state<Controller>();
appendList(deleteAll, controller);
handleSubmition(deleteAll);
handleConfirmation(deleteAll, controller, [=](
not_null<PeerData*> p,
not_null<ChannelData*> c) {
p->session().api().deleteAllFromParticipant(c, p);
});
}
if (allCanBan) {
auto ownedWrap = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
inner,
object_ptr<Ui::VerticalLayout>(inner));
Ui::AddSkip(inner);
Ui::AddSkip(inner);
const auto ban = inner->add(
object_ptr<Ui::Checkbox>(
box,
rpl::conditional(
ownedWrap->toggledValue(),
tr::lng_context_restrict_user(),
rpl::conditional(
rpl::single(isSingle),
tr::lng_ban_user(),
tr::lng_ban_users())),
false,
st::defaultBoxCheckbox),
st::boxRowPadding + buttonPadding);
const auto controller = box->lifetime().make_state<Controller>();
appendList(ban, controller);
handleSubmition(ban);
Ui::AddSkip(inner);
Ui::AddSkip(inner);
const auto wrap = inner->add(std::move(ownedWrap));
const auto container = wrap->entity();
wrap->toggle(false, anim::type::instant);
const auto session = &participants.front()->session();
const auto emojiMargin = QMargins(
-st::moderateBoxExpandInnerSkip,
-st::moderateBoxExpandInnerSkip / 2,
0,
0);
const auto emojiUp = Ui::Text::SingleCustomEmoji(
session->data().customEmojiManager().registerInternalEmoji(
st::moderateBoxExpandIcon,
emojiMargin,
false));
const auto emojiDown = Ui::Text::SingleCustomEmoji(
session->data().customEmojiManager().registerInternalEmoji(
st::moderateBoxExpandIconDown,
emojiMargin,
false));
auto label = object_ptr<Ui::FlatLabel>(
inner,
QString(),
st::moderateBoxDividerLabel);
const auto raw = label.data();
auto &lifetime = wrap->lifetime();
const auto scrollLifetime = lifetime.make_state<rpl::lifetime>();
label->setClickHandlerFilter([=](
const ClickHandlerPtr &handler,
Qt::MouseButton button) {
if (button != Qt::LeftButton) {
return false;
}
wrap->toggle(!wrap->toggled(), anim::type::normal);
{
inner->heightValue() | rpl::start_with_next([=] {
if (!wrap->animating()) {
scrollLifetime->destroy();
Ui::PostponeCall(crl::guard(box, [=] {
box->scrollToY(std::numeric_limits<int>::max());
}));
} else {
box->scrollToY(std::numeric_limits<int>::max());
}
}, *scrollLifetime);
}
return true;
});
wrap->toggledValue(
) | rpl::map([isSingle, emojiUp, emojiDown](bool toggled) {
return ((toggled && isSingle)
? tr::lng_restrict_user_part
: (toggled && !isSingle)
? tr::lng_restrict_users_part
: isSingle
? tr::lng_restrict_user_full
: tr::lng_restrict_users_full)(
lt_emoji,
rpl::single(toggled ? emojiUp : emojiDown),
Ui::Text::WithEntities);
}) | rpl::flatten_latest(
) | rpl::start_with_next([=](const TextWithEntities &text) {
raw->setMarkedText(
Ui::Text::Link(text, u"internal:"_q),
Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = [=] { raw->update(); },
});
}, label->lifetime());
Ui::AddSkip(inner);
inner->add(object_ptr<Ui::DividerLabel>(
inner,
std::move(label),
st::defaultBoxDividerLabelPadding,
RectPart::Top | RectPart::Bottom));
using Flag = ChatRestriction;
using Flags = ChatRestrictions;
const auto peer = items.front()->history()->peer;
const auto chat = peer->asChat();
const auto channel = peer->asChannel();
const auto defaultRestrictions = chat
? chat->defaultRestrictions()
: channel->defaultRestrictions();
const auto prepareFlags = FixDependentRestrictions(
defaultRestrictions
| ((channel && channel->isPublic())
? (Flag::ChangeInfo | Flag::PinMessages)
: Flags(0)));
const auto disabledMessages = [&] {
auto result = base::flat_map<Flags, QString>();
{
const auto disabled = FixDependentRestrictions(
defaultRestrictions
| ((channel && channel->isPublic())
? (Flag::ChangeInfo | Flag::PinMessages)
: Flags(0)));
result.emplace(
disabled,
tr::lng_rights_restriction_for_all(tr::now));
}
return result;
}();
auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
box,
rpl::conditional(
rpl::single(isSingle),
tr::lng_restrict_users_part_single_header(),
tr::lng_restrict_users_part_header(
lt_count,
rpl::single(participants.size()) | tr::to_count())),
prepareFlags,
disabledMessages,
{ .isForum = peer->isForum() });
std::move(changes) | rpl::start_with_next([=] {
ban->setChecked(true);
}, ban->lifetime());
Ui::AddSkip(container);
Ui::AddDivider(container);
Ui::AddSkip(container);
container->add(std::move(checkboxes));
// Handle confirmation manually.
confirms->events() | rpl::start_with_next([=] {
if (ban->checked() && controller->collectRequests) {
const auto kick = !wrap->toggled();
const auto restrictions = getRestrictions();
const auto request = [=](
not_null<PeerData*> peer,
not_null<ChannelData*> channel) {
if (!kick) {
Api::ChatParticipants::Restrict(
channel,
peer,
ChatRestrictionsInfo(), // Unused.
ChatRestrictionsInfo(restrictions, 0),
nullptr,
nullptr);
} else {
channel->session().api().chatParticipants().kick(
channel,
peer,
{ channel->restrictions(), 0 });
}
};
sequentiallyRequest(request, controller->collectRequests());
}
}, ban->lifetime());
}
const auto close = crl::guard(box, [=] { box->closeBox(); });
{
const auto data = &participants.front()->session().data();
const auto ids = data->itemsToIds(items);
box->addButton(tr::lng_box_delete(), [=] {
confirms->fire({});
if (confirmed) {
confirmed();
}
data->histories().deleteMessages(ids, true);
data->sendHistoryChangeNotifications();
close();
});
}
box->addButton(tr::lng_cancel(), close);
}
bool CanCreateModerateMessagesBox(const HistoryItemsList &items) {
const auto options = CalculateModerateOptions(items);
return (options.allCanBan || options.allCanDelete)
&& !options.participants.empty();
}
void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
const auto container = box->verticalLayout();
const auto maybeUser = peer->asUser();
Ui::AddSkip(container);
Ui::AddSkip(container);
base::install_event_filter(box, [=](not_null<QEvent*> event) {
if (event->type() == QEvent::KeyPress) {
if (const auto k = static_cast<QKeyEvent*>(event.get())) {
if ((k->key() == Qt::Key_Enter)
|| (k->key() == Qt::Key_Return)) {
box->uiShow()->show(Ui::MakeConfirmBox({
.text = tr::lng_gigagroup_warning_title(),
.confirmed = [=](Fn<void()> close) {
box->triggerButton(0);
close();
},
.confirmText = tr::lng_box_yes(),
.cancelText = tr::lng_box_no(),
}));
}
}
}
return base::EventFilterResult::Continue;
});
const auto line = container->add(object_ptr<Ui::RpWidget>(container));
const auto &st = st::mainMenuUserpic;
line->resize(line->width(), st.size.height());
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
line,
peer,
st);
userpic->showSavedMessagesOnSelf(true);
const auto label = Ui::CreateChild<Ui::FlatLabel>(
line,
peer->isSelf()
? tr::lng_saved_messages() | Ui::Text::ToBold()
: maybeUser
? tr::lng_profile_delete_conversation() | Ui::Text::ToBold()
: rpl::single(Ui::Text::Bold(peer->name())) | rpl::type_erased(),
box->getDelegate()->style().title);
line->widthValue(
) | rpl::start_with_next([=](int width) {
userpic->moveToLeft(st::boxRowPadding.left(), 0);
const auto skip = st::defaultBoxCheckbox.textPosition.x();
label->resizeToWidth(width
- rect::right(userpic)
- skip
- st::boxRowPadding.right());
label->moveToLeft(
rect::right(userpic) + skip,
((userpic->height() - label->height()) / 2));
}, label->lifetime());
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
label->setAttribute(Qt::WA_TransparentForMouseEvents);
Ui::AddSkip(container);
Ui::AddSkip(container);
box->addRow(
object_ptr<Ui::FlatLabel>(
container,
peer->isSelf()
? tr::lng_sure_delete_saved_messages()
: maybeUser
? tr::lng_sure_delete_history(
lt_contact,
rpl::single(peer->name()))
: (peer->isChannel() && !peer->isMegagroup())
? tr::lng_sure_leave_channel()
: tr::lng_sure_leave_group(),
st::boxLabel));
const auto maybeCheckbox = [&]() -> Ui::Checkbox* {
if (!peer->canRevokeFullHistory()) {
return nullptr;
}
Ui::AddSkip(container);
Ui::AddSkip(container);
return box->addRow(
object_ptr<Ui::Checkbox>(
container,
maybeUser
? tr::lng_delete_for_other_check(
tr::now,
lt_user,
TextWithEntities{ maybeUser->firstName },
Ui::Text::RichLangValue)
: tr::lng_delete_for_everyone_check(
tr::now,
Ui::Text::WithEntities),
false,
st::defaultBoxCheckbox));
}();
Ui::AddSkip(container);
auto buttonText = maybeUser
? tr::lng_box_delete()
: !maybeCheckbox
? tr::lng_box_leave()
: maybeCheckbox->checkedValue() | rpl::map([](bool checked) {
return checked ? tr::lng_box_delete() : tr::lng_box_leave();
}) | rpl::flatten_latest();
const auto close = crl::guard(box, [=] { box->closeBox(); });
box->addButton(std::move(buttonText), [=] {
const auto revoke = maybeCheckbox && maybeCheckbox->checked();
Core::App().closeChatFromWindows(peer);
// Don't delete old history by default,
// because Android app doesn't.
//
//if (const auto from = peer->migrateFrom()) {
// peer->session().api().deleteConversation(from, false);
//}
peer->session().api().deleteConversation(peer, revoke);
close();
}, st::attentionBoxButton);
box->addButton(tr::lng_cancel(), close);
}

View File

@@ -0,0 +1,23 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class PeerData;
namespace Ui {
class GenericBox;
} // namespace Ui
void CreateModerateMessagesBox(
not_null<Ui::GenericBox*> box,
const HistoryItemsList &items,
Fn<void()> confirmed);
[[nodiscard]] bool CanCreateModerateMessagesBox(const HistoryItemsList &);
void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer);

View File

@@ -740,7 +740,7 @@ void PasscodeBox::submitOnlyCheckCloudPassword(const QString &oldPassword) {
void PasscodeBox::sendOnlyCheckCloudPassword(const QString &oldPassword) {
checkPassword(oldPassword, [=](const Core::CloudPasswordResult &check) {
if (const auto onstack = _cloudFields.customCheckCallback) {
onstack(check);
onstack(check, Ui::MakeWeak(this));
} else {
Assert(_cloudFields.turningOff);
sendClearCloudPassword(check);

View File

@@ -51,7 +51,10 @@ public:
TimeId pendingResetDate = 0;
// Check cloud password for some action.
Fn<void(const Core::CloudPasswordResult &)> customCheckCallback;
using CustomCheck = Fn<void(
const Core::CloudPasswordResult &,
QPointer<PasscodeBox>)>;
CustomCheck customCheckCallback;
rpl::producer<QString> customTitle;
std::optional<QString> customDescription;
rpl::producer<QString> customSubmitButton;

View File

@@ -734,6 +734,10 @@ auto PeerListRow::generateNameWords() const
return peer()->nameWords();
}
const style::PeerListItem &PeerListRow::computeSt(
const style::PeerListItem &st) const {
return st;
}
void PeerListRow::invalidatePixmapsCache() {
if (_checkbox) {
@@ -816,9 +820,14 @@ void PeerListRow::stopLastRipple() {
}
}
void PeerListRow::paintRipple(Painter &p, int x, int y, int outerWidth) {
void PeerListRow::paintRipple(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int outerWidth) {
if (_ripple) {
_ripple->paint(p, x, y, outerWidth);
_ripple->paint(p, x, y, outerWidth, &st.button.ripple.color->c);
if (_ripple->empty()) {
_ripple.reset();
}
@@ -1039,11 +1048,9 @@ void PeerListContent::changeCheckState(
not_null<PeerListRow*> row,
bool checked,
anim::type animated) {
row->setChecked(
checked,
_st.item.checkbox,
animated,
[=] { updateRow(row); });
row->setChecked(checked, _st.item.checkbox, animated, [=] {
updateRow(row);
});
}
void PeerListContent::setRowHidden(not_null<PeerListRow*> row, bool hidden) {
@@ -1691,7 +1698,9 @@ crl::time PeerListContent::paintRow(
const auto row = getRow(index);
Assert(row != nullptr);
row->lazyInitialize(_st.item);
const auto &st = row->computeSt(_st.item);
row->lazyInitialize(st);
const auto outerWidth = width();
auto refreshStatusAt = row->refreshStatusTime();
@@ -1719,8 +1728,8 @@ crl::time PeerListContent::paintRow(
const auto opacity = row->opacity();
const auto &bg = selected
? _st.item.button.textBgOver
: _st.item.button.textBg;
? st.button.textBgOver
: st.button.textBg;
if (opacity < 1.) {
p.setOpacity(opacity);
}
@@ -1731,36 +1740,37 @@ crl::time PeerListContent::paintRow(
});
p.fillRect(0, 0, outerWidth, _rowHeight, bg);
row->paintRipple(p, 0, 0, outerWidth);
row->paintRipple(p, st, 0, 0, outerWidth);
row->paintUserpic(
p,
_st.item,
_st.item.photoPosition.x(),
_st.item.photoPosition.y(),
st,
st.photoPosition.x(),
st.photoPosition.y(),
outerWidth);
p.setPen(st::contactsNameFg);
const auto skipRight = _st.item.photoPosition.x();
const auto skipRight = st.photoPosition.x();
const auto rightActionSize = row->rightActionSize();
const auto rightActionMargins = rightActionSize.isEmpty()
? QMargins()
: row->rightActionMargins();
const auto &name = row->name();
const auto namex = _st.item.namePosition.x();
const auto namey = _st.item.namePosition.y();
const auto namePosition = st.namePosition;
const auto namex = namePosition.x();
const auto namey = namePosition.y();
auto namew = outerWidth - namex - skipRight;
if (!rightActionSize.isEmpty()
&& (namey < rightActionMargins.top() + rightActionSize.height())
&& (namey + _st.item.nameStyle.font->height
&& (namey + st.nameStyle.font->height
> rightActionMargins.top())) {
namew -= rightActionMargins.left()
+ rightActionSize.width()
+ rightActionMargins.right()
- skipRight;
}
const auto statusx = _st.item.statusPosition.x();
const auto statusy = _st.item.statusPosition.y();
const auto statusx = st.statusPosition.x();
const auto statusy = st.statusPosition.y();
auto statusw = outerWidth - statusx - skipRight;
if (!rightActionSize.isEmpty()
&& (statusy < rightActionMargins.top() + rightActionSize.height())
@@ -1782,7 +1792,7 @@ crl::time PeerListContent::paintRow(
width(),
selected);
auto nameCheckedRatio = row->disabled() ? 0. : row->checkedRatio();
p.setPen(anim::pen(_st.item.nameFg, _st.item.nameFgChecked, nameCheckedRatio));
p.setPen(anim::pen(st.nameFg, st.nameFgChecked, nameCheckedRatio));
name.drawLeftElided(p, namex, namey, namew, width());
p.setFont(st::contactsStatusFont);
@@ -1801,17 +1811,17 @@ crl::time PeerListContent::paintRow(
if (highlightedWidth > availableWidth) {
highlightedPart = st::contactsStatusFont->elided(highlightedPart, availableWidth);
}
p.setPen(_st.item.statusFgActive);
p.setPen(st.statusFgActive);
p.drawTextLeft(statusx, statusy, width(), highlightedPart);
} else {
grayedPart = st::contactsStatusFont->elided(grayedPart, availableWidth - highlightedWidth);
p.setPen(_st.item.statusFgActive);
p.setPen(st.statusFgActive);
p.drawTextLeft(statusx, statusy, width(), highlightedPart);
p.setPen(selected ? _st.item.statusFgOver : _st.item.statusFg);
p.setPen(selected ? st.statusFgOver : st.statusFg);
p.drawTextLeft(statusx + highlightedWidth, statusy, width(), grayedPart);
}
} else {
row->paintStatusText(p, _st.item, statusx, statusy, statusw, width(), selected);
row->paintStatusText(p, st, statusx, statusy, statusw, width(), selected);
}
row->elementsPaint(
@@ -1907,10 +1917,30 @@ void PeerListContent::selectSkipPage(int height, int direction) {
selectSkip(rowsToSkip * direction);
}
void PeerListContent::selectLast() {
const auto rowsCount = shownRowsCount();
const auto newSelectedIndex = rowsCount - 1;
_selected.index.value = newSelectedIndex;
_selected.element = 0;
if (newSelectedIndex >= 0) {
auto top = (newSelectedIndex > 0) ? getRowTop(RowIndex(newSelectedIndex)) : 0;
auto bottom = (newSelectedIndex + 1 < rowsCount) ? getRowTop(RowIndex(newSelectedIndex + 1)) : height();
_scrollToRequests.fire({ top, bottom });
}
update();
_selectedIndex = _selected.index.value;
}
rpl::producer<int> PeerListContent::selectedIndexValue() const {
return _selectedIndex.value();
}
int PeerListContent::selectedIndex() const {
return _selectedIndex.current();
}
bool PeerListContent::hasSelection() const {
return _selected.index.value >= 0;
}
@@ -2095,6 +2125,16 @@ bool PeerListContent::submitted() {
return false;
}
PeerListRowId PeerListContent::updateFromParentDrag(QPoint globalPosition) {
selectByMouse(globalPosition);
const auto row = getRow(_selected.index);
return row ? row->id() : 0;
}
void PeerListContent::dragLeft() {
clearSelection();
}
void PeerListContent::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {

View File

@@ -100,6 +100,8 @@ public:
-> const base::flat_set<QChar> &;
[[nodiscard]] virtual auto generateNameWords() const
-> const base::flat_set<QString> &;
[[nodiscard]] virtual const style::PeerListItem &computeSt(
const style::PeerListItem &st) const;
virtual void preloadUserpic();
@@ -228,7 +230,12 @@ public:
QPoint point,
UpdateCallback &&updateCallback);
void stopLastRipple();
void paintRipple(Painter &p, int x, int y, int outerWidth);
void paintRipple(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int outerWidth);
void paintUserpic(
Painter &p,
const style::PeerListItem &st,
@@ -601,6 +608,7 @@ public:
};
SkipResult selectSkip(int direction);
void selectSkipPage(int height, int direction);
void selectLast();
enum class Mode {
Default,
@@ -609,6 +617,7 @@ public:
void setMode(Mode mode);
[[nodiscard]] rpl::producer<int> selectedIndexValue() const;
[[nodiscard]] int selectedIndex() const;
[[nodiscard]] bool hasSelection() const;
[[nodiscard]] bool hasPressed() const;
void clearSelection();
@@ -616,6 +625,9 @@ public:
void searchQueryChanged(QString query);
bool submitted();
PeerListRowId updateFromParentDrag(QPoint globalPosition);
void dragLeft();
// Interface for the controller.
void appendRow(std::unique_ptr<PeerListRow> row);
void appendSearchRow(std::unique_ptr<PeerListRow> row);

View File

@@ -598,19 +598,17 @@ void EditAdminBox::requestTransferPassword(not_null<ChannelData*> channel) {
) | rpl::take(
1
) | rpl::start_with_next([=](const Core::CloudPasswordState &state) {
const auto box = std::make_shared<QPointer<PasscodeBox>>();
auto fields = PasscodeBox::CloudFields::From(state);
fields.customTitle = tr::lng_rights_transfer_password_title();
fields.customDescription
= tr::lng_rights_transfer_password_description(tr::now);
fields.customSubmitButton = tr::lng_passcode_submit();
fields.customCheckCallback = crl::guard(this, [=](
const Core::CloudPasswordResult &result) {
sendTransferRequestFrom(*box, channel, result);
const Core::CloudPasswordResult &result,
QPointer<PasscodeBox> box) {
sendTransferRequestFrom(box, channel, result);
});
*box = getDelegate()->show(Box<PasscodeBox>(
&channel->session(),
fields));
getDelegate()->show(Box<PasscodeBox>(&channel->session(), fields));
}, lifetime());
}

View File

@@ -166,33 +166,6 @@ void SaveChannelAdmin(
}).send();
}
void SaveChannelRestriction(
not_null<ChannelData*> channel,
not_null<PeerData*> participant,
ChatRestrictionsInfo oldRights,
ChatRestrictionsInfo newRights,
Fn<void()> onDone,
Fn<void()> onFail) {
channel->session().api().request(MTPchannels_EditBanned(
channel->inputChannel,
participant->input,
MTP_chatBannedRights(
MTP_flags(MTPDchatBannedRights::Flags::from_raw(
uint32(newRights.flags))),
MTP_int(newRights.until))
)).done([=](const MTPUpdates &result) {
channel->session().api().applyUpdates(result);
channel->applyEditBanned(participant, oldRights, newRights);
if (onDone) {
onDone();
}
}).fail([=] {
if (onFail) {
onFail();
}
}).send();
}
void SaveChatParticipantKick(
not_null<ChatData*> chat,
not_null<UserData*> user,
@@ -275,7 +248,7 @@ Fn<void(
ChatRestrictionsInfo newRights) {
const auto done = [=] { if (onDone) onDone(newRights); };
const auto saveForChannel = [=](not_null<ChannelData*> channel) {
SaveChannelRestriction(
Api::ChatParticipants::Restrict(
channel,
participant,
oldRights,

View File

@@ -312,6 +312,7 @@ PreviewWrap::PreviewWrap(
nullptr, // document
WebPageCollage(),
nullptr, // iv
nullptr, // stickerSet
0, // duration
QString(), // author
false, // hasLargeMedia
@@ -1427,14 +1428,15 @@ void SetupPeerColorSample(
void AddPeerColorButton(
not_null<Ui::VerticalLayout*> container,
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> peer) {
not_null<PeerData*> peer,
const style::SettingsButton &st) {
auto label = peer->isSelf()
? tr::lng_settings_theme_name_color()
: tr::lng_edit_channel_color();
const auto button = AddButtonWithIcon(
container,
rpl::duplicate(label),
st::settingsColorButton,
st,
{ &st::menuIconChangeColors });
const auto style = std::make_shared<Ui::ChatStyle>(

View File

@@ -48,7 +48,8 @@ void EditPeerColorBox(
void AddPeerColorButton(
not_null<Ui::VerticalLayout*> container,
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> peer);
not_null<PeerData*> peer,
const style::SettingsButton &st);
void CheckBoostLevel(
std::shared_ptr<ChatHelpers::Show> show,

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "ui/layers/generic_box.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "styles/style_layers.h"
@@ -23,6 +24,17 @@ void EditPeerHistoryVisibilityBox(
Ui::RadioenumGroup<HistoryVisibility>
>(historyVisibilitySavedValue);
const auto addButton = [=](
not_null<Ui::RpWidget*> inner,
HistoryVisibility v) {
const auto button = Ui::CreateChild<Ui::AbstractButton>(inner.get());
inner->sizeValue(
) | rpl::start_with_next([=](const QSize &s) {
button->resize(s);
}, button->lifetime());
button->setClickedCallback([=] { historyVisibility->setValue(v); });
};
box->setTitle(tr::lng_manage_history_visibility_title());
box->addButton(tr::lng_settings_save(), [=] {
savedCallback(historyVisibility->current());
@@ -31,32 +43,36 @@ void EditPeerHistoryVisibilityBox(
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
box->addSkip(st::editPeerHistoryVisibilityTopSkip);
box->addRow(object_ptr<Ui::Radioenum<HistoryVisibility>>(
const auto visible = box->addRow(object_ptr<Ui::VerticalLayout>(box));
visible->add(object_ptr<Ui::Radioenum<HistoryVisibility>>(
box,
historyVisibility,
HistoryVisibility::Visible,
tr::lng_manage_history_visibility_shown(tr::now),
st::defaultBoxCheckbox));
box->addRow(
visible->add(
object_ptr<Ui::FlatLabel>(
box,
tr::lng_manage_history_visibility_shown_about(),
st::editPeerPrivacyLabel),
st::editPeerPreHistoryLabelMargins + st::boxRowPadding);
st::editPeerPreHistoryLabelMargins);
addButton(visible, HistoryVisibility::Visible);
box->addSkip(st::editPeerHistoryVisibilityTopSkip);
box->addRow(object_ptr<Ui::Radioenum<HistoryVisibility>>(
const auto hidden = box->addRow(object_ptr<Ui::VerticalLayout>(box));
hidden->add(object_ptr<Ui::Radioenum<HistoryVisibility>>(
box,
historyVisibility,
HistoryVisibility::Hidden,
tr::lng_manage_history_visibility_hidden(tr::now),
st::defaultBoxCheckbox));
box->addRow(
hidden->add(
object_ptr<Ui::FlatLabel>(
box,
(isLegacy
? tr::lng_manage_history_visibility_hidden_legacy
: tr::lng_manage_history_visibility_hidden_about)(),
st::editPeerPrivacyLabel),
st::editPeerPreHistoryLabelMargins + st::boxRowPadding);
st::editPeerPreHistoryLabelMargins);
addButton(hidden, HistoryVisibility::Hidden);
}

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_user_names.h"
#include "main/main_session.h"
#include "ui/boxes/confirm_box.h"
#include "base/event_filter.h"
#include "boxes/peers/edit_participants_box.h"
#include "boxes/peers/edit_peer_color_box.h"
#include "boxes/peers/edit_peer_common.h"
@@ -27,6 +28,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/stickers_box.h"
#include "boxes/username_box.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "data/data_channel.h"
@@ -47,6 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_app_config.h"
#include "settings/settings_common.h"
#include "ui/boxes/boost_box.h"
#include "ui/controls/emoji_button.h"
#include "ui/controls/userpic_button.h"
#include "ui/rp_widget.h"
#include "ui/vertical_list.h"
@@ -61,6 +65,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/vertical_layout.h"
#include "window/window_session_controller.h"
#include "api/api_invite_links.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_layers.h"
#include "styles/style_menu_icons.h"
#include "styles/style_boxes.h"
@@ -533,7 +538,7 @@ object_ptr<Ui::RpWidget> Controller::createTitleEdit() {
_wrap,
object_ptr<Ui::InputField>(
_wrap,
st::defaultInputField,
st::editPeerTitleField,
(_isBot
? tr::lng_dlg_new_bot_name
: _isGroup
@@ -555,6 +560,76 @@ object_ptr<Ui::RpWidget> Controller::createTitleEdit() {
submitTitle();
}, result->entity()->lifetime());
{
const auto field = result->entity();
const auto container = _box->getDelegate()->outerContainer();
using Selector = ChatHelpers::TabbedSelector;
using PanelPtr = base::unique_qptr<ChatHelpers::TabbedPanel>;
const auto emojiPanelPtr = field->lifetime().make_state<PanelPtr>(
base::make_unique_q<ChatHelpers::TabbedPanel>(
container,
ChatHelpers::TabbedPanelDescriptor{
.ownedSelector = object_ptr<Selector>(
nullptr,
ChatHelpers::TabbedSelectorDescriptor{
.show = _navigation->uiShow(),
.st = st::defaultComposeControls.tabbed,
.level = Window::GifPauseReason::Layer,
.mode = Selector::Mode::PeerTitle,
}),
}));
const auto emojiPanel = emojiPanelPtr->get();
emojiPanel->setDesiredHeightValues(
1.,
st::emojiPanMinHeight / 2,
st::emojiPanMinHeight);
emojiPanel->hide();
emojiPanel->selector()->setCurrentPeer(_peer);
emojiPanel->selector()->emojiChosen(
) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
Ui::InsertEmojiAtCursor(field->textCursor(), data.emoji);
field->setFocus();
}, field->lifetime());
emojiPanel->setDropDown(true);
const auto emojiToggle = Ui::CreateChild<Ui::EmojiButton>(
field,
st::defaultComposeControls.files.emoji);
emojiToggle->show();
emojiToggle->installEventFilter(emojiPanel);
emojiToggle->addClickHandler([=] { emojiPanel->toggleAnimated(); });
const auto updateEmojiPanelGeometry = [=] {
const auto parent = emojiPanel->parentWidget();
const auto global = emojiToggle->mapToGlobal({ 0, 0 });
const auto local = parent->mapFromGlobal(global);
emojiPanel->moveTopRight(
local.y() + emojiToggle->height(),
local.x() + emojiToggle->width() * 3);
};
base::install_event_filter(container, [=](not_null<QEvent*> event) {
const auto type = event->type();
if (type == QEvent::Move || type == QEvent::Resize) {
crl::on_main(field, [=] { updateEmojiPanelGeometry(); });
}
return base::EventFilterResult::Continue;
});
field->widthValue() | rpl::start_with_next([=](int width) {
const auto &p = st::editPeerTitleEmojiPosition;
emojiToggle->moveToRight(p.x(), p.y(), width);
updateEmojiPanelGeometry();
}, emojiToggle->lifetime());
base::install_event_filter(emojiToggle, [=](not_null<QEvent*> event) {
if (event->type() == QEvent::Enter) {
updateEmojiPanelGeometry();
}
return base::EventFilterResult::Continue;
});
}
_controls.title = result->entity();
return result;
}
@@ -941,7 +1016,11 @@ void Controller::fillColorIndexButton() {
Expects(_controls.buttonsLayout != nullptr);
const auto show = _navigation->uiShow();
AddPeerColorButton(_controls.buttonsLayout, show, _peer);
AddPeerColorButton(
_controls.buttonsLayout,
_navigation->uiShow(),
_peer,
st::managePeerColorsButton);
}
void Controller::fillSignaturesButton() {
@@ -983,8 +1062,8 @@ void Controller::fillHistoryVisibilityButton() {
: HistoryVisibility::Visible;
_channelHasLocationOriginalValue = channel && channel->hasLocation();
const auto updateHistoryVisibility =
std::make_shared<rpl::event_stream<HistoryVisibility>>();
const auto updateHistoryVisibility
= std::make_shared<rpl::event_stream<HistoryVisibility>>();
const auto boxCallback = crl::guard(this, [=](HistoryVisibility checked) {
updateHistoryVisibility->fire(std::move(checked));
@@ -1698,8 +1777,8 @@ void Controller::saveUsernamesOrder() {
channel->setUsernames(ranges::views::all(
newUsernames
) | ranges::views::transform([&](QString username) {
const auto editable =
(channel->editableUsername() == username);
const auto editable
= (channel->editableUsername() == username);
return Data::Username{
.username = std::move(username),
.active = true,

View File

@@ -227,9 +227,9 @@ QImage QrExact(const Qr::Data &data, int pixel, QColor color) {
p.drawImage(
skip,
skip,
Intro::details::TelegramLogoImage().scaled(
logoSize * style::DevicePixelRatio(),
logoSize * style::DevicePixelRatio(),
Window::LogoNoMargin().scaled(
logoSize,
logoSize,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation));
}

View File

@@ -7,30 +7,33 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/peers/edit_peer_reactions.h"
#include "apiwrap.h"
#include "base/event_filter.h"
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "data/data_chat.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_document.h"
#include "data/data_peer_values.h" // UniqueReactionsLimit.
#include "data/data_session.h"
#include "data/data_user.h"
#include "history/view/reactions/history_view_reactions_selector.h"
#include "main/main_session.h"
#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/boxes/boost_box.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/checkbox.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/vertical_list.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/wrap/slide_wrap.h"
#include "window/window_session_controller.h"
#include "window/window_session_controller_link_info.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_info.h"
#include "styles/style_settings.h"
#include "styles/style_layers.h"
#include "styles/style_settings.h"
#include <QtWidgets/QTextEdit>
#include <QtGui/QTextBlock>
@@ -705,12 +708,16 @@ void EditAllowedReactionsBox(
}
};
changed(selected.empty() ? DefaultSelected() : std::move(selected), {});
Ui::AddSubsectionTitle(
reactions,
enabled
? tr::lng_manage_peer_reactions_available()
: tr::lng_manage_peer_reactions_some_title(),
st::manageGroupReactionsFieldPadding);
reactions->add(AddReactionsSelector(reactions, {
.outer = box->getDelegate()->outerContainer(),
.controller = args.navigation->parentController(),
.title = (enabled
? tr::lng_manage_peer_reactions_available()
: tr::lng_manage_peer_reactions_some_title()),
.title = tr::lng_manage_peer_reactions_available_ph(),
.list = all,
.selected = state->selected,
.callback = changed,
@@ -726,6 +733,7 @@ void EditAllowedReactionsBox(
}
});
const auto reactionsLimit = container->lifetime().make_state<int>(0);
if (!isGroup) {
AddReactionsText(
container,
@@ -733,9 +741,109 @@ void EditAllowedReactionsBox(
args.allowedCustomReactions,
state->customCount.value(),
args.askForBoosts);
const auto session = &args.navigation->parentController()->session();
const auto wrap = container->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container,
object_ptr<Ui::VerticalLayout>(container)));
const auto max = Data::UniqueReactionsLimit(session->user());
const auto inactiveColor = std::make_optional(st::windowSubTextFg->c);
const auto activeColor = std::make_optional(
st::windowActiveTextFg->c);
const auto inner = wrap->entity();
Ui::AddSkip(inner);
Ui::AddSubsectionTitle(
inner,
tr::lng_manage_peer_reactions_max_title(),
st::manageGroupReactionsMaxSubtitlePadding);
Ui::AddSkip(inner);
const auto line = inner->add(
object_ptr<Ui::RpWidget>(inner),
st::boxRowPadding);
Ui::AddSkip(inner);
Ui::AddSkip(inner);
const auto left = Ui::CreateChild<Ui::FlatLabel>(
line,
QString::number(1),
st::defaultFlatLabel);
const auto center = Ui::CreateChild<Ui::FlatLabel>(
line,
st::defaultFlatLabel);
const auto right = Ui::CreateChild<Ui::FlatLabel>(
line,
QString::number(max),
st::defaultFlatLabel);
const auto slider = Ui::CreateChild<Ui::MediaSlider>(
line,
st::settingsScale);
rpl::combine(
line->sizeValue(),
left->sizeValue(),
center->sizeValue(),
right->sizeValue()
) | rpl::start_with_next([=](
const QSize &s,
const QSize &leftSize,
const QSize &centerSize,
const QSize &rightSize) {
const auto sliderHeight = st::settingsScale.seekSize.height();
line->resize(
line->width(),
leftSize.height() + sliderHeight * 2);
{
const auto r = line->rect();
slider->setGeometry(
0,
r.height() - sliderHeight * 1.5,
r.width(),
sliderHeight);
}
left->moveToLeft(0, 0);
right->moveToRight(0, 0);
center->moveToLeft((s.width() - centerSize.width()) / 2, 0);
}, line->lifetime());
const auto updateLabels = [=](int limit) {
left->setTextColorOverride((limit <= 1)
? activeColor
: inactiveColor);
center->setText(tr::lng_manage_peer_reactions_max_slider(
tr::now,
lt_count,
limit));
center->setTextColorOverride(activeColor);
right->setTextColorOverride((limit >= max)
? activeColor
: inactiveColor);
(*reactionsLimit) = limit;
};
const auto current = args.allowed.maxCount
? std::clamp(1, args.allowed.maxCount, max)
: max / 2;
slider->setPseudoDiscrete(
max,
[=](int index) { return index + 1; },
current,
updateLabels,
updateLabels);
updateLabels(current);
wrap->toggleOn(rpl::single(
optionInitial != Option::None
) | rpl::then(
state->selectorState.value(
) | rpl::map(rpl::mappers::_1 == SelectorState::Active)));
Ui::AddDividerText(inner, tr::lng_manage_peer_reactions_max_about());
}
const auto collect = [=] {
auto result = AllowedReactions();
result.maxCount = (*reactionsLimit);
if (isGroup
? (state->option.current() == Option::Some)
: (enabled->toggled())) {
@@ -783,6 +891,7 @@ void SaveAllowedReactions(
Data::ReactionToMTP
) | ranges::to<QVector<MTPReaction>>;
using Flag = MTPmessages_SetChatAvailableReactions::Flag;
using Type = Data::AllowedReactionsType;
const auto updated = (allowed.type != Type::Some)
? MTP_chatReactionsAll(MTP_flags((allowed.type == Type::Default)
@@ -792,14 +901,18 @@ void SaveAllowedReactions(
? MTP_chatReactionsNone()
: MTP_chatReactionsSome(MTP_vector<MTPReaction>(ids));
peer->session().api().request(MTPmessages_SetChatAvailableReactions(
allowed.maxCount ? MTP_flags(Flag::f_reactions_limit) : MTP_flags(0),
peer->input,
updated
updated,
MTP_int(allowed.maxCount)
)).done([=](const MTPUpdates &result) {
peer->session().api().applyUpdates(result);
auto parsed = Data::Parse(updated);
parsed.maxCount = allowed.maxCount;
if (const auto chat = peer->asChat()) {
chat->setAllowedReactions(Data::Parse(updated));
chat->setAllowedReactions(parsed);
} else if (const auto channel = peer->asChannel()) {
channel->setAllowedReactions(Data::Parse(updated));
channel->setAllowedReactions(parsed);
} else {
Unexpected("Invalid peer type in SaveAllowedReactions.");
}

View File

@@ -913,7 +913,7 @@ void PreviewBox(
auto businessOrder = Settings::BusinessFeaturesOrder(&show->session());
state->order = ranges::contains(businessOrder, descriptor.section)
? std::move(businessOrder)
: ranges::contains(businessOrder, descriptor.section)
: ranges::contains(premiumOrder, descriptor.section)
? std::move(premiumOrder)
: std::vector{ descriptor.section };
@@ -1240,8 +1240,8 @@ void DecorateListPromoBox(
box->setStyle(st::premiumPreviewDoubledLimitsBox);
box->widthValue(
) | rpl::start_with_next([=](int width) {
const auto &padding =
st::premiumPreviewDoubledLimitsBox.buttonPadding;
const auto &padding
= st::premiumPreviewDoubledLimitsBox.buttonPadding;
button->resizeToWidth(width
- padding.left()
- padding.right());

View File

@@ -90,7 +90,7 @@ QString ExtractRingtoneName(not_null<DocumentData*> document) {
}
const auto name = document->filename();
if (!name.isEmpty()) {
const auto extension = Data::FileExtension(name);
const auto extension = Core::FileExtension(name);
if (extension.isEmpty()) {
return name;
} else if (name.size() > extension.size() + 1) {

View File

@@ -234,7 +234,10 @@ void ShareBox::prepareCommentField() {
const auto field = _comment->entity();
field->submits(
) | rpl::start_with_next([=] { submit({}); }, field->lifetime());
) | rpl::start_with_next([=] {
submit({});
}, field->lifetime());
if (const auto show = uiShow(); show->valid()) {
InitMessageFieldHandlers(
_descriptor.session,
@@ -246,6 +249,14 @@ void ShareBox::prepareCommentField() {
}
field->setSubmitSettings(Core::App().settings().sendSubmitWay());
field->changes() | rpl::start_with_next([=] {
if (!field->getLastText().isEmpty()) {
setCloseByOutsideClick(false);
} else if (_inner->selected().empty()) {
setCloseByOutsideClick(true);
}
}, field->lifetime());
Ui::SendPendingMoveResizeEvents(_comment);
if (_bottomWidget) {
Ui::SendPendingMoveResizeEvents(_bottomWidget);
@@ -255,8 +266,6 @@ void ShareBox::prepareCommentField() {
void ShareBox::prepare() {
prepareCommentField();
setCloseByOutsideClick(false);
_select->resizeToWidth(st::boxWideWidth);
Ui::SendPendingMoveResizeEvents(_select);
@@ -313,6 +322,12 @@ void ShareBox::prepare() {
not_null<Data::Thread*> thread,
bool checked) {
innerSelectedChanged(thread, checked);
if (checked) {
setCloseByOutsideClick(false);
} else if (_inner->selected().empty()
&& _comment->entity()->getLastText().isEmpty()) {
setCloseByOutsideClick(true);
}
});
Ui::Emoji::SuggestionsController::Init(
@@ -1678,6 +1693,101 @@ void FastShareMessage(
Ui::LayerOption::CloseOther);
}
void FastShareLink(
not_null<Window::SessionController*> controller,
const QString &url) {
FastShareLink(controller->uiShow(), url);
}
void FastShareLink(
std::shared_ptr<Main::SessionShow> show,
const QString &url) {
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
const auto sending = std::make_shared<bool>();
auto copyCallback = [=] {
QGuiApplication::clipboard()->setText(url);
show->showToast(tr::lng_background_link_copied(tr::now));
};
auto submitCallback = [=](
std::vector<not_null<::Data::Thread*>> &&result,
TextWithTags &&comment,
Api::SendOptions options,
::Data::ForwardOptions) {
if (*sending || result.empty()) {
return;
}
const auto error = [&] {
for (const auto thread : result) {
const auto error = GetErrorTextForSending(
thread,
{ .text = &comment });
if (!error.isEmpty()) {
return std::make_pair(error, thread);
}
}
return std::make_pair(QString(), result.front());
}();
if (!error.first.isEmpty()) {
auto text = TextWithEntities();
if (result.size() > 1) {
text.append(
Ui::Text::Bold(error.second->chatListName())
).append("\n\n");
}
text.append(error.first);
if (const auto weak = *box) {
weak->getDelegate()->show(Ui::MakeConfirmBox({
.text = text,
.inform = true,
}));
}
return;
}
*sending = true;
if (!comment.text.isEmpty()) {
comment.text = url + "\n" + comment.text;
const auto add = url.size() + 1;
for (auto &tag : comment.tags) {
tag.offset += add;
}
} else {
comment.text = url;
}
auto &api = show->session().api();
for (const auto thread : result) {
auto message = Api::MessageToSend(
Api::SendAction(thread, options));
message.textWithTags = comment;
message.action.clearDraft = false;
api.sendMessage(std::move(message));
}
if (*box) {
(*box)->closeBox();
}
show->showToast(tr::lng_share_done(tr::now));
};
auto filterCallback = [](not_null<::Data::Thread*> thread) {
if (const auto user = thread->peer()->asUser()) {
if (user->canSendIgnoreRequirePremium()) {
return true;
}
}
return ::Data::CanSend(thread, ChatRestriction::SendOther);
};
*box = show->show(
Box<ShareBox>(ShareBox::Descriptor{
.session = &show->session(),
.copyCallback = std::move(copyCallback),
.submitCallback = std::move(submitCallback),
.filterCallback = std::move(filterCallback),
.premiumRequiredError = SharePremiumRequiredError(),
}),
Ui::LayerOption::KeepOther,
anim::type::normal);
}
auto SharePremiumRequiredError()
-> Fn<RecipientPremiumRequiredError(not_null<UserData*>)> {
return WritePremiumRequiredError;

View File

@@ -37,6 +37,7 @@ struct SendOptions;
namespace Main {
class Session;
class SessionShow;
} // namespace Main
namespace Dialogs {
@@ -68,6 +69,12 @@ void ShareGameScoreByHash(
void FastShareMessage(
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item);
void FastShareLink(
not_null<Window::SessionController*> controller,
const QString &url);
void FastShareLink(
std::shared_ptr<Main::SessionShow> show,
const QString &url);
struct RecipientPremiumRequiredError;
[[nodiscard]] auto SharePremiumRequiredError()

View File

@@ -569,8 +569,8 @@ void StickerSetBox::updateButtons() {
if (!_inner->shortName().isEmpty()) {
const auto top = addTopButton(st::infoTopBarMenu);
const auto menu =
std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
const auto menu
= std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
top->setClickedCallback([=] {
*menu = base::make_unique_q<Ui::PopupMenu>(
top,
@@ -613,8 +613,8 @@ void StickerSetBox::updateButtons() {
_show->showBox(std::move(box));
}
};
const auto menu =
std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
const auto menu
= std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
top->setClickedCallback([=] {
*menu = base::make_unique_q<Ui::PopupMenu>(
top,
@@ -1032,7 +1032,7 @@ void StickerSetBox::Inner::contextMenuEvent(QContextMenuEvent *e) {
_menu.get(),
type,
SendMenu::DefaultSilentCallback(sendSelected),
SendMenu::DefaultScheduleCallback(this, type, sendSelected),
SendMenu::DefaultScheduleCallback(_show, type, sendSelected),
SendMenu::DefaultWhenOnlineCallback(sendSelected));
const auto show = _show;

View File

@@ -7,31 +7,29 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "calls/calls_call.h"
#include "main/main_session.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "boxes/abstract_box.h"
#include "ui/boxes/confirm_box.h"
#include "ui/boxes/rate_call_box.h"
#include "calls/calls_instance.h"
#include "base/battery_saving.h"
#include "base/openssl_help.h"
#include "base/platform/base_platform_info.h"
#include "base/random.h"
#include "mtproto/mtproto_dh_utils.h"
#include "mtproto/mtproto_config.h"
#include "boxes/abstract_box.h"
#include "calls/calls_instance.h"
#include "calls/calls_panel.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "window/window_controller.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "lang/lang_keys.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "media/audio/media_audio_track.h"
#include "base/platform/base_platform_info.h"
#include "calls/calls_panel.h"
#include "mtproto/mtproto_config.h"
#include "mtproto/mtproto_dh_utils.h"
#include "ui/boxes/confirm_box.h"
#include "ui/boxes/rate_call_box.h"
#include "webrtc/webrtc_create_adm.h"
#include "webrtc/webrtc_environment.h"
#include "webrtc/webrtc_video_track.h"
#include "webrtc/webrtc_create_adm.h"
#include "data/data_user.h"
#include "data/data_session.h"
#include "window/window_controller.h"
#include <tgcalls/Instance.h>
#include <tgcalls/VideoCaptureInterface.h>
@@ -945,8 +943,8 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
tgcalls::Descriptor descriptor = {
.version = versionString,
.config = tgcalls::Config{
.initializationTimeout =
serverConfig.callConnectTimeoutMs / 1000.,
.initializationTimeout
= serverConfig.callConnectTimeoutMs / 1000.,
.receiveTimeout = serverConfig.callPacketTimeoutMs / 1000.,
.dataSaving = tgcalls::DataSaving::Never,
.enableP2P = call.is_p2p_allowed(),
@@ -1072,6 +1070,7 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
Core::App().mediaDevices().setCaptureMuted(muted);
}, _instanceLifetime);
#if 0
Core::App().batterySaving().value(
) | rpl::start_with_next([=](bool isSaving) {
crl::on_main(weak, [=] {
@@ -1080,6 +1079,7 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
}
});
}, _instanceLifetime);
#endif
}
void Call::handleControllerStateChange(tgcalls::State state) {

View File

@@ -277,11 +277,11 @@ private:
MTP::Sender _api;
Type _type = Type::Outgoing;
rpl::variable<State> _state = State::Starting;
rpl::variable<RemoteAudioState> _remoteAudioState =
RemoteAudioState::Active;
rpl::variable<RemoteAudioState> _remoteAudioState
= RemoteAudioState::Active;
rpl::variable<Webrtc::VideoState> _remoteVideoState;
rpl::variable<RemoteBatteryState> _remoteBatteryState =
RemoteBatteryState::Normal;
rpl::variable<RemoteBatteryState> _remoteBatteryState
= RemoteBatteryState::Normal;
rpl::event_stream<Error> _errors;
FinishType _finishAfterRequestingCall = FinishType::None;
bool _answerAfterDhConfigReceived = false;

View File

@@ -162,8 +162,8 @@ private:
object_ptr<Ui::FlatLabel> _status;
object_ptr<Ui::RpWidget> _fingerprint = { nullptr };
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _remoteAudioMute = { nullptr };
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _remoteLowBattery =
{ nullptr };
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _remoteLowBattery
= { nullptr };
std::unique_ptr<Userpic> _userpic;
std::unique_ptr<VideoBubble> _outgoingVideoBubble;
QPixmap _bottomShadow;

View File

@@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "boxes/abstract_box.h"
#include "base/timer.h"
#include "styles/style_basic.h"
#include "styles/style_calls.h"
#include "styles/style_chat_helpers.h" // style::GroupCallUserpics
#include "styles/style_layers.h"
@@ -49,7 +50,6 @@ enum class BarState {
namespace {
constexpr auto kUpdateDebugTimeoutMs = crl::time(500);
constexpr auto kSwitchStateDuration = 120;
constexpr auto kMinorBlobAlpha = 76. / 255.;
@@ -374,7 +374,7 @@ void TopBar::initControls() {
};
_switchStateAnimation.stop();
const auto duration = (to - from) * kSwitchStateDuration;
const auto duration = (to - from) * st::universalDuration;
_switchStateAnimation.start(
_switchStateCallback,
from,

View File

@@ -331,6 +331,7 @@ void Panel::Incoming::RendererGL::paint(
shadow.texture.left(), shadow.texture.top(),
};
_contentBuffer->bind();
_contentBuffer->write(0, coords, sizeof(coords));
const auto bottomShadowArea = QRect(

View File

@@ -783,6 +783,7 @@ void Viewport::RendererGL::paintTile(
: &*_frameProgram.yuv420;
const auto uniformViewport = QSizeF(_viewport) * _factor;
program->bind();
program->setUniformValue("viewport", uniformViewport);
program->setUniformValue(
"frameBg",
@@ -1078,6 +1079,7 @@ void Viewport::RendererGL::drawDownscalePass(
? &*_downscaleProgram.argb32
: &*_downscaleProgram.yuv420;
program->bind();
FillTexturedRectangle(f, program);
}

View File

@@ -25,8 +25,8 @@ const auto kSpeakerThreshold = std::vector<float>{
50.0f / kMaxVolumePercent,
150.0f / kMaxVolumePercent };
constexpr auto kVolumeStickedValues =
std::array<std::pair<float64, float64>, 7>{{
constexpr auto kVolumeStickedValues
= std::array<std::pair<float64, float64>, 7>{{
{ 25. / kMaxVolumePercent, 2. / kMaxVolumePercent },
{ 50. / kMaxVolumePercent, 2. / kMaxVolumePercent },
{ 75. / kMaxVolumePercent, 2. / kMaxVolumePercent },
@@ -93,8 +93,8 @@ MenuVolumeItem::MenuVolumeItem(
const auto volume = _localMuted
? 0
: base::SafeRound(_slider->value() * kMaxVolumePercent);
const auto muteProgress =
_crossLineAnimation.value((!volume) ? 1. : 0.);
const auto muteProgress
= _crossLineAnimation.value((!volume) ? 1. : 0.);
const auto selected = isSelected();
p.fillRect(clip, selected ? st.itemBgOver : st.itemBg);
@@ -174,8 +174,8 @@ MenuVolumeItem::MenuVolumeItem(
return;
}
if (_waitingForUpdateVolume) {
const auto localVolume =
base::SafeRound(_slider->value() * _maxVolume);
const auto localVolume
= base::SafeRound(_slider->value() * _maxVolume);
if ((localVolume != newVolume)
&& (_cloudVolume == newVolume)) {
_changeVolumeRequests.fire(int(localVolume));

View File

@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/checkbox.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/labels.h"
#include "styles/style_basic.h"
#include "styles/style_calls.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@@ -26,7 +27,6 @@ namespace {
constexpr auto kRoundRadius = 9;
constexpr auto kMaxGroupCallLength = 40;
constexpr auto kSwitchDuration = 200;
constexpr auto kSelectDuration = 120;
class GraphicButton final : public Ui::AbstractButton {
public:
@@ -103,7 +103,7 @@ void GraphicButton::setToggled(bool value) {
[=] { update(); },
_toggled ? 0. : 1.,
_toggled ? 1. : 0.,
kSelectDuration);
st::universalDuration);
}
void GraphicButton::paintEvent(QPaintEvent *e) {

View File

@@ -587,11 +587,9 @@ void ChooseSourceProcess::setupGeometryWithParent(
not_null<QWidget*> parent) {
_window->createWinId();
const auto parentScreen = [&] {
if (!::Platform::IsWayland()) {
if (const auto screen = QGuiApplication::screenAt(
if (const auto screen = QGuiApplication::screenAt(
parent->geometry().center())) {
return screen;
}
return screen;
}
return parent->screen();
}();

View File

@@ -785,6 +785,12 @@ emojiScroll: ScrollArea(defaultSolidScroll) {
reactPanelScroll: ScrollArea(emojiScroll) {
deltab: 7px;
}
reactPanelScrollRounded: ScrollArea(emojiScroll) {
width: 8px;
deltax: 3px;
deltat: 14px;
deltab: 14px;
}
choosePeerGroupIcon: icon {{ "info/edit/create_group", lightButtonFg }};
choosePeerChannelIcon: icon {{ "info/edit/create_channel", lightButtonFg }};
@@ -983,7 +989,7 @@ historyComposeField: InputField(defaultInputField) {
placeholderFg: placeholderFg;
placeholderFgActive: placeholderFgActive;
placeholderFgError: placeholderFgActive;
placeholderMargins: margins(7px, 5px, 7px, 5px);
placeholderMargins: margins(2px, 0px, 2px, 0px);
placeholderAlign: align(topleft);
placeholderScale: 0.;
placeholderFont: normalFont;
@@ -1108,8 +1114,6 @@ historyRecordVoiceFgOver: historyComposeIconFgOver;
historyRecordVoiceFgInactive: attentionButtonFg;
historyRecordVoiceFgActive: windowBgActive;
historyRecordVoiceFgActiveIcon: windowFgActive;
historyRecordVoiceShowDuration: 120;
historyRecordVoiceDuration: 120;
historyRecordVoice: icon {{ "chat/input_record", historyRecordVoiceFg }};
historyRecordVoiceOver: icon {{ "chat/input_record", historyRecordVoiceFgOver }};
historyRecordVoiceOnceBg: icon {{ "voice_lock/audio_once_bg", historySendIconFg }};

View File

@@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h"
#include "data/data_channel.h"
#include "data/data_document.h"
#include "data/data_file_origin.h"
#include "data/data_peer_values.h"
#include "data/stickers/data_stickers.h"
#include "data/stickers/data_custom_emoji.h"
@@ -51,12 +52,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_chat_helpers.h"
#include "styles/style_menu_icons.h"
#include <QtWidgets/QApplication>
namespace ChatHelpers {
namespace {
constexpr auto kCollapsedRows = 3;
constexpr auto kAppearDuration = 0.3;
constexpr auto kCustomSearchLimit = 256;
constexpr auto kColorPickerDelay = crl::time(500);
using Core::RecentEmojiId;
using Core::RecentEmojiDocument;
@@ -464,7 +468,8 @@ EmojiListWidget::EmojiListWidget(
std::move(descriptor.paused))
, _show(std::move(descriptor.show))
, _features(descriptor.features)
, _mode(descriptor.mode)
, _onlyUnicodeEmoji(descriptor.mode == Mode::PeerTitle)
, _mode(_onlyUnicodeEmoji ? Mode::Full : descriptor.mode)
, _api(&session().mtp())
, _staticCount(_mode == Mode::Full ? kEmojiSectionCount : 1)
, _premiumIcon(_mode == Mode::EmojiStatus
@@ -477,7 +482,8 @@ EmojiListWidget::EmojiListWidget(
, _overBg(st::emojiPanRadius, st().overBg)
, _collapsedBg(st::emojiPanExpand.height / 2, st().headerFg)
, _picker(this, st())
, _showPickerTimer([=] { showPicker(); }) {
, _showPickerTimer([=] { showPicker(); })
, _previewTimer([=] { showPreview(); }) {
setMouseTracking(true);
if (st().bg->c.alpha() > 0) {
setAttribute(Qt::WA_OpaquePaintEvent);
@@ -485,7 +491,8 @@ EmojiListWidget::EmojiListWidget(
if (_mode != Mode::RecentReactions
&& _mode != Mode::BackgroundEmoji
&& _mode != Mode::ChannelStatus) {
&& _mode != Mode::ChannelStatus
&& !_onlyUnicodeEmoji) {
setupSearch();
}
@@ -566,12 +573,17 @@ EmojiListWidget::~EmojiListWidget() {
void EmojiListWidget::setupSearch() {
const auto session = &_show->session();
const auto type = (_mode == Mode::EmojiStatus)
? TabbedSearchType::Status
: (_mode == Mode::UserpicBuilder)
? TabbedSearchType::ProfilePhoto
: TabbedSearchType::Emoji;
_search = MakeSearch(this, st(), [=](std::vector<QString> &&query) {
_nextSearchQuery = std::move(query);
InvokeQueued(this, [=] {
applyNextSearchQuery();
});
}, session, (_mode == Mode::EmojiStatus), _mode == Mode::UserpicBuilder);
}, session, type);
}
void EmojiListWidget::applyNextSearchQuery() {
@@ -624,6 +636,15 @@ void EmojiListWidget::applyNextSearchQuery() {
}
}
void EmojiListWidget::showPreview() {
if (const auto over = std::get_if<OverEmoji>(&_pressed)) {
if (const auto custom = lookupCustomEmoji(over)) {
_show->showMediaPreview(custom->stickerSetOrigin(), custom);
_previewShown = true;
}
}
}
std::vector<EmojiPtr> EmojiListWidget::collectPlainSearchResults() {
return SearchEmoji(_searchQuery, _searchEmoji);
}
@@ -1038,7 +1059,7 @@ void EmojiListWidget::fillRecent() {
const auto test = session().isTestMode();
for (const auto &one : list) {
const auto document = std::get_if<RecentEmojiDocument>(&one.id.data);
if (document && document->test != test) {
if (document && ((document->test != test) || _onlyUnicodeEmoji)) {
continue;
}
_recent.push_back({
@@ -1113,13 +1134,11 @@ void EmojiListWidget::fillRecentMenu(
not_null<Ui::PopupMenu*> menu,
int section,
int index) {
if (section != int(Section::Recent)) {
return;
}
const auto recent = (section == int(Section::Recent));
const auto addAction = Ui::Menu::CreateAddActionCallback(menu);
const auto over = OverEmoji{ section, index };
const auto emoji = lookupOverEmoji(&over);
const auto custom = lookupCustomEmoji(index, section);
const auto custom = lookupCustomEmoji(&over);
if (custom && custom->sticker()) {
const auto sticker = custom->sticker();
const auto emoji = sticker->alt;
@@ -1136,18 +1155,21 @@ void EmojiListWidget::fillRecentMenu(
TextUtilities::SetClipboardText(data);
}, &st::menuIconCopy);
}
if (setId && _features.openStickerSets) {
if (recent && setId && _features.openStickerSets) {
addAction(
tr::lng_emoji_view_pack(tr::now),
crl::guard(this, [=] { displaySet(setId); }),
&st::menuIconShowAll);
}
} else if (emoji) {
} else if (recent && emoji) {
addAction(tr::lng_emoji_copy(tr::now), [=] {
const auto text = emoji->text();
TextUtilities::SetClipboardText({ text, { text } });
}, &st::menuIconCopy);
}
if (!recent) {
return;
}
auto id = RecentEmojiId{ emoji };
if (custom) {
id.data = RecentEmojiDocument{
@@ -1173,7 +1195,7 @@ void EmojiListWidget::fillRecentMenu(
.confirmed = crl::guard(this, sure),
.confirmText = tr::lng_emoji_reset_recent_button(tr::now),
.labelStyle = &st().boxLabel,
}));
}));
};
addAction({
.text = tr::lng_emoji_reset_recent(tr::now),
@@ -1492,6 +1514,11 @@ bool EmojiListWidget::checkPickerHide() {
return false;
}
DocumentData *EmojiListWidget::lookupCustomEmoji(
const OverEmoji *over) const {
return over ? lookupCustomEmoji(over->index, over->section) : nullptr;
}
DocumentData *EmojiListWidget::lookupCustomEmoji(
int index,
int section) const {
@@ -1594,13 +1621,19 @@ void EmojiListWidget::mousePressEvent(QMouseEvent *e) {
if (!Core::App().settings().hasChosenEmojiVariant(emoji)) {
showPicker();
} else {
_showPickerTimer.callOnce(500);
_previewTimer.cancel();
_showPickerTimer.callOnce(kColorPickerDelay);
}
} else if (lookupCustomEmoji(over)) {
_showPickerTimer.cancel();
_previewTimer.callOnce(QApplication::startDragTime());
}
}
}
void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
_previewTimer.cancel();
auto pressed = _pressed;
setPressed(v::null);
_lastMousePos = e->globalPos();
@@ -1625,7 +1658,10 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
_picker->hide();
}
if (v::is_null(_selected) || _selected != pressed) {
if (_previewShown) {
_previewShown = false;
return;
} else if (v::is_null(_selected) || _selected != pressed) {
return;
}
@@ -1644,7 +1680,7 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
return;
}
selectEmoji(lookupChosen(emoji, over));
} else if (const auto custom = lookupCustomEmoji(index, section)) {
} else if (const auto custom = lookupCustomEmoji(over)) {
selectCustom(lookupChosen(custom, over));
}
} else if (const auto set = std::get_if<OverSet>(&pressed)) {
@@ -2101,7 +2137,9 @@ void EmojiListWidget::refreshCustom() {
auto old = base::take(_custom);
const auto session = &this->session();
const auto premiumPossible = session->premiumPossible();
const auto premiumMayBeBought = premiumPossible
const auto onlyUnicodeEmoji = _onlyUnicodeEmoji || !premiumPossible;
const auto premiumMayBeBought = (!onlyUnicodeEmoji)
&& premiumPossible
&& !session->premium()
&& !_allowWithoutPremium;
const auto owner = &session->data();
@@ -2161,7 +2199,7 @@ void EmojiListWidget::refreshCustom() {
}
return true;
}();
if (premium && !premiumPossible) {
if (premium && onlyUnicodeEmoji) {
return;
} else if (valid) {
i->thumbnailDocument = it->second->lookupThumbnailDocument();
@@ -2195,7 +2233,7 @@ void EmojiListWidget::refreshCustom() {
}
}
}
if (premium && !premiumPossible) {
if (premium && onlyUnicodeEmoji) {
return;
}
_custom.push_back({
@@ -2478,7 +2516,9 @@ bool EmojiListWidget::eventHook(QEvent *e) {
void EmojiListWidget::updateSelected() {
if (!v::is_null(_pressed) || !v::is_null(_pickerSelected)) {
return;
if (!_previewShown) {
return;
}
}
auto newSelected = OverState{ v::null };
@@ -2537,6 +2577,13 @@ void EmojiListWidget::setSelected(OverState newSelected) {
} else {
_picker->showAnimated();
}
} else if (_previewShown && _pressed != _selected) {
if (const auto over = std::get_if<OverEmoji>(&_selected)) {
if (const auto custom = lookupCustomEmoji(over)) {
_pressed = _selected;
_show->showMediaPreview(custom->stickerSetOrigin(), custom);
}
}
}
}

View File

@@ -76,6 +76,7 @@ enum class EmojiListMode {
RecentReactions,
UserpicBuilder,
BackgroundEmoji,
PeerTitle,
};
struct EmojiListDescriptor {
@@ -287,6 +288,8 @@ private:
int index);
[[nodiscard]] EmojiPtr lookupOverEmoji(const OverEmoji *over) const;
[[nodiscard]] DocumentData *lookupCustomEmoji(
const OverEmoji *over) const;
[[nodiscard]] DocumentData *lookupCustomEmoji(
int index,
int section) const;
@@ -371,10 +374,13 @@ private:
DocumentId documentId,
uint64 setId);
void showPreview();
void applyNextSearchQuery();
const std::shared_ptr<Show> _show;
const ComposeFeatures _features;
const bool _onlyUnicodeEmoji;
Mode _mode = Mode::Full;
std::unique_ptr<Ui::TabbedSearch> _search;
MTP::Sender _api;
@@ -440,6 +446,8 @@ private:
object_ptr<EmojiColorPicker> _picker;
base::Timer _showPickerTimer;
base::Timer _previewTimer;
bool _previewShown = false;
rpl::event_stream<EmojiChosen> _chosen;
rpl::event_stream<FileChosen> _customChosen;

View File

@@ -38,7 +38,6 @@ namespace {
constexpr auto kShowExactDelay = crl::time(300);
constexpr auto kMaxNonScrolledEmoji = 7;
constexpr auto kAnimationDuration = crl::time(120);
} // namespace
@@ -528,7 +527,7 @@ void SuggestionsWidget::setSelected(int selected, anim::type animated) {
[=] { update(); },
_selected,
selected,
kAnimationDuration,
st::universalDuration,
anim::sineInOut);
if (_scrollMax > 0) {
const auto selectedMax = int(_rows.size()) - 3;
@@ -560,7 +559,7 @@ void SuggestionsWidget::scrollTo(int value, anim::type animated) {
[=] { update(); },
_scrollValue,
value,
kAnimationDuration,
st::universalDuration,
anim::sineInOut);
}
_scrollValue = value;

View File

@@ -437,8 +437,8 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
auto filterNotPassedByUsername = [this](UserData *user) -> bool {
if (PrimaryUsername(user).startsWith(_filter, Qt::CaseInsensitive)) {
const auto exactUsername =
(PrimaryUsername(user).size() == _filter.size());
const auto exactUsername
= (PrimaryUsername(user).size() == _filter.size());
return exactUsername;
}
return true;
@@ -446,8 +446,9 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
auto filterNotPassedByName = [&](UserData *user) -> bool {
for (const auto &nameWord : user->nameWords()) {
if (nameWord.startsWith(_filter, Qt::CaseInsensitive)) {
const auto exactUsername =
(PrimaryUsername(user).compare(_filter, Qt::CaseInsensitive) == 0);
const auto exactUsername = PrimaryUsername(user).compare(
_filter,
Qt::CaseInsensitive) == 0;
return exactUsername;
}
}
@@ -1378,7 +1379,7 @@ void FieldAutocomplete::Inner::contextMenuEvent(QContextMenuEvent *e) {
_menu,
type,
SendMenu::DefaultSilentCallback(send),
SendMenu::DefaultScheduleCallback(this, type, send),
SendMenu::DefaultScheduleCallback(_show, type, send),
SendMenu::DefaultWhenOnlineCallback(send));
if (!_menu->empty()) {

View File

@@ -394,7 +394,7 @@ base::unique_qptr<Ui::PopupMenu> GifsListWidget::fillContextMenu(
menu,
type,
SendMenu::DefaultSilentCallback(send),
SendMenu::DefaultScheduleCallback(this, type, send),
SendMenu::DefaultScheduleCallback(_show, type, send),
SendMenu::DefaultWhenOnlineCallback(send),
icons);
@@ -820,7 +820,7 @@ void GifsListWidget::setupSearch() {
: SearchEmojiSectionSetId();
refreshIcons();
searchForGifs(accumulated);
}, session);
}, session, TabbedSearchType::Emoji);
}
int32 GifsListWidget::showInlineRows(bool newResults) {

View File

@@ -431,10 +431,7 @@ bool HasSendText(not_null<const Ui::InputField*> field) {
const auto &text = field->getTextWithTags().text;
for (const auto &ch : text) {
const auto code = ch.unicode();
if (code != ' '
&& code != '\n'
&& code != '\r'
&& !IsReplacedBySpace(code)) {
if (!IsTrimmed(ch) && !IsReplacedBySpace(code)) {
return true;
}
}

View File

@@ -547,6 +547,15 @@ void StickersListWidget::sendSearchRequest() {
}
_search->setLoading(true);
if (_searchQuery == Ui::PremiumGroupFakeEmoticon()) {
_search->setLoading(false);
_searchRequestId = 0;
_searchCache.emplace(_searchQuery, std::vector<uint64>());
showSearchResults();
return;
}
const auto hash = uint64(0);
_searchRequestId = _api.request(MTPmessages_SearchStickerSets(
MTP_flags(0),
@@ -570,10 +579,14 @@ void StickersListWidget::searchForSets(
return;
}
_filteredStickers = session().data().stickers().getListByEmoji(
std::move(emoji),
0,
true);
if (query == Ui::PremiumGroupFakeEmoticon()) {
_filteredStickers = session().data().stickers().getPremiumList(0);
} else {
_filteredStickers = session().data().stickers().getListByEmoji(
std::move(emoji),
0,
true);
}
if (_searchQuery != cleaned) {
_search->setLoading(false);
if (const auto requestId = base::take(_searchRequestId)) {
@@ -1654,7 +1667,7 @@ base::unique_qptr<Ui::PopupMenu> StickersListWidget::fillContextMenu(
menu,
type,
SendMenu::DefaultSilentCallback(send),
SendMenu::DefaultScheduleCallback(this, type, send),
SendMenu::DefaultScheduleCallback(_show, type, send),
SendMenu::DefaultWhenOnlineCallback(send),
icons);
@@ -2604,15 +2617,20 @@ void StickersListWidget::beforeHiding() {
void StickersListWidget::setupSearch() {
const auto session = &_show->session();
const auto type = (_mode == Mode::UserpicBuilder)
? TabbedSearchType::ProfilePhoto
: (_mode == Mode::ChatIntro)
? TabbedSearchType::Greeting
: TabbedSearchType::Stickers;
_search = MakeSearch(this, st(), [=](std::vector<QString> &&query) {
auto set = base::flat_set<EmojiPtr>();
auto text = ranges::accumulate(query, QString(), [](
QString a,
QString b) {
QString a,
QString b) {
return a.isEmpty() ? b : (a + ' ' + b);
});
searchForSets(std::move(text), SearchEmoji(query, set));
}, session, false, (_mode == Mode::UserpicBuilder));
}, session, type);
}
void StickersListWidget::displaySet(uint64 setId) {

View File

@@ -65,6 +65,7 @@ enum class StickersListMode {
Full,
Masks,
UserpicBuilder,
ChatIntro,
};
struct StickersListDescriptor {

View File

@@ -302,21 +302,39 @@ void TabbedSelector::Tab::saveScrollTop() {
_scrollTop = widget()->getVisibleTop();
}
[[nodiscard]] rpl::producer<std::vector<Ui::EmojiGroup>> GreetingGroupFirst(
not_null<Data::Session*> owner) {
return owner->emojiStatuses().stickerGroupsValue(
) | rpl::map([](std::vector<Ui::EmojiGroup> &&groups) {
const auto i = ranges::find(
groups,
Ui::EmojiGroupType::Greeting,
&Ui::EmojiGroup::type);
if (i != begin(groups) && i != end(groups)) {
ranges::rotate(begin(groups), i, i + 1);
}
return std::move(groups);
});
}
std::unique_ptr<Ui::TabbedSearch> MakeSearch(
not_null<Ui::RpWidget*> parent,
const style::EmojiPan &st,
Fn<void(std::vector<QString>&&)> callback,
not_null<Main::Session*> session,
bool statusCategories,
bool profilePhotoCategories) {
TabbedSearchType type) {
using Descriptor = Ui::SearchDescriptor;
const auto owner = &session->data();
auto result = std::make_unique<Ui::TabbedSearch>(parent, st, Descriptor{
.st = st.search,
.groups = (profilePhotoCategories
.groups = ((type == TabbedSearchType::ProfilePhoto)
? owner->emojiStatuses().profilePhotoGroupsValue()
: statusCategories
: (type == TabbedSearchType::Status)
? owner->emojiStatuses().statusGroupsValue()
: (type == TabbedSearchType::Stickers)
? owner->emojiStatuses().stickerGroupsValue()
: (type == TabbedSearchType::Greeting)
? GreetingGroupFirst(owner)
: owner->emojiStatuses().emojiGroupsValue()),
.customEmojiFactory = owner->customEmojiManager().factory(
Data::CustomEmojiManager::SizeTag::SetIcon,
@@ -378,7 +396,7 @@ TabbedSelector::TabbedSelector(
tabs.reserve(2);
tabs.push_back(createTab(SelectorTab::Stickers, 0));
tabs.push_back(createTab(SelectorTab::Masks, 1));
} else if (_mode == Mode::StickersOnly) {
} else if (_mode == Mode::StickersOnly || _mode == Mode::ChatIntro) {
tabs.reserve(1);
tabs.push_back(createTab(SelectorTab::Stickers, 0));
} else {
@@ -389,7 +407,9 @@ TabbedSelector::TabbedSelector(
}())
, _currentTabType(full()
? session().settings().selectorTab()
: (mediaEditor() || _mode == Mode::StickersOnly)
: (mediaEditor()
|| _mode == Mode::StickersOnly
|| _mode == Mode::ChatIntro)
? SelectorTab::Stickers
: SelectorTab::Emoji)
, _hasEmojiTab(ranges::contains(_tabs, SelectorTab::Emoji, &Tab::type))
@@ -489,7 +509,7 @@ TabbedSelector::TabbedSelector(
_st.categoriesBg);
}, lifetime());
if (hasEmojiTab()) {
if (hasEmojiTab() && _mode == Mode::Full) {
session().data().stickers().emojiSetInstalled(
) | rpl::start_with_next([=](uint64 setId) {
_tabsSlider->setActiveSection(indexByType(SelectorTab::Emoji));
@@ -540,6 +560,8 @@ TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type, int index) {
? EmojiMode::FullReactions
: _mode == Mode::RecentReactions
? EmojiMode::RecentReactions
: _mode == Mode::PeerTitle
? EmojiMode::PeerTitle
: EmojiMode::Full),
.customTextColor = _customTextColor,
.paused = paused,
@@ -552,7 +574,9 @@ TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type, int index) {
using Descriptor = StickersListDescriptor;
return object_ptr<StickersListWidget>(this, Descriptor{
.show = _show,
.mode = StickersMode::Full,
.mode = (_mode == Mode::ChatIntro
? StickersMode::ChatIntro
: StickersMode::Full),
.paused = paused,
.st = &_st,
.features = _features,
@@ -958,6 +982,9 @@ void TabbedSelector::beforeHiding() {
_beforeHidingCallback(_currentTabType);
}
}
if (Ui::InFocusChain(this)) {
window()->setFocus();
}
}
void TabbedSelector::afterShown() {

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