Compare commits

...

107 Commits

Author SHA1 Message Date
Ilya Fedin
a1f9b5a96f Get GApplication out of experimental settings
GApplication will always be used on Linux now. GNotification gets a toggle instead.
2023-01-12 21:49:16 +04:00
John Preston
2887c0b564 Beta version 4.5.6.
- Try enabling non-fractional scale
High DPI support on Windows and Linux.
- Experimental setting for fractional scale
High DPI support on Windows and Linux.
- Fix navigation to bottom problems in groups you didn't join.
- Fix a crash in chat export settings changes.
- Fix a crash in sending some of JPEG images.
- Fix CJK fonts on Windows.
2023-01-12 21:35:44 +04:00
Ilya Fedin
9b7826ea0d Get GApplication out of experimental settings
GApplication will always be used on Linux now. GNotification gets a toggle instead.
2023-01-12 21:30:45 +04:00
Ilya Fedin
241be89e5c Enable the new experimental setting for Linux 2023-01-12 21:17:08 +04:00
John Preston
39075538fb Enable RoundPreferFloor HighDpi support on Windows.
Add an experimental setting for exact HighDPI on Windows.
2023-01-12 13:57:09 +04:00
John Preston
1592f70a7c Respect trackUnreadMessages in history jumps.
Fixes #25384.
2023-01-12 12:23:29 +04:00
John Preston
732bb25666 Fix read from clipboard after bot install. 2023-01-12 12:23:28 +04:00
John Preston
5cba1cdc64 Fix blurred background for small images.
Fixes #25707.
2023-01-12 11:45:45 +04:00
John Preston
d346925b9d Fix window title update on accounts switch. 2023-01-12 11:36:00 +04:00
John Preston
02f3985125 Inspect correct url() instead of dragText().
Fixes #25720.

Regression was introduced in 23387d6625.
2023-01-12 11:34:16 +04:00
John Preston
f3db43abc9 Fix sending compressed images. 2023-01-12 11:34:08 +04:00
John Preston
ecf61712cd Fix CJK font fallback on Windows.
Fixes #25714.
2023-01-12 11:34:02 +04:00
John Preston
b47c66155d Beta version 4.5.5.
- Fix crash in Settings.
2023-01-10 22:16:24 +04:00
John Preston
2fda96a375 Fix a crash in non-authorized Settings. 2023-01-10 22:10:58 +04:00
John Preston
12c2e42917 Beta version 4.5.4: Fix build with GCC. 2023-01-10 16:59:05 +04:00
John Preston
94a956ce19 Beta version 4.5.4: Fix build for Windows x64. 2023-01-10 16:47:57 +04:00
John Preston
704f64a0c9 Fix "Limit special config request types." 2023-01-10 16:47:41 +04:00
John Preston
f9ca7f4505 Beta version 4.5.4: Fix "Mark-as-read-inactive setting." 2023-01-10 16:24:27 +04:00
John Preston
0a3d31a91f Beta version 4.5.4: Try updating OpenAL on macOS. 2023-01-10 15:34:33 +04:00
John Preston
3c17fab15a Beta version 4.5.4: Mark-as-read-inactive setting.
In case an experimental setting of auto-scrolling is enabled,
just ignore the window activity check in marking chat as read..
2023-01-10 15:31:15 +04:00
John Preston
2efe409c60 Beta version 4.5.4.
- Allow wide range of interface scale options.
- Show opened chat name in the window title.
- Bug fixes and other minor improvements.
- Fix updating on macOS older than 10.14.
2023-01-10 15:23:19 +04:00
John Preston
1176421bf2 Limit special config request types. 2023-01-10 15:09:49 +04:00
John Preston
05911a7172 Fix initial scale counting from dpi. 2023-01-10 09:51:12 +04:00
Ilya Fedin
1326359745 Don't involve locale in filename timestamp generation
It was batch replaced as part of 7b5781b845, but it's not really semantically valid
2023-01-10 09:50:02 +04:00
John Preston
fc26457218 Show current chat name in the window title. 2023-01-09 18:39:07 +04:00
Ilya Fedin
173108a9cb Differ file download failure reasons
..and uncomment the code for handling incorrect permissions
2023-01-09 17:02:11 +04:00
Ilya Fedin
7307f0b1a5 Use temp directory for downloads in Linux sandbox by default 2023-01-09 16:09:57 +04:00
John Preston
c49dac57b7 Don't use window title for IPC. 2023-01-09 16:08:34 +04:00
Ilya Fedin
6288da2f3d Remove socket errors from Qt adapters 2023-01-09 15:01:14 +04:00
Ilya Fedin
ce37c6ef08 Implement lossless jpeg progression 2023-01-09 15:00:11 +04:00
John Preston
5f93725431 Relax controls hiding conditions in full screen.
Fixes #25499.
2023-01-09 14:03:27 +04:00
23rd
90dfdb0e1f Moved NeverFreedPointer to lib_base. 2023-01-09 12:51:32 +03:00
23rd
7cd330db9a Improved style of box for creation of linked chat. 2023-01-09 12:34:44 +03:00
23rd
b14ac5cafe Moved Info::Profile::FloatingIcon to td_ui. 2023-01-09 12:34:44 +03:00
John Preston
1fc929b78f Add some logs for jump-to-end bugs.
This is related to #25384.
2023-01-09 13:25:15 +04:00
John Preston
fd47fd4d9e Track shared media index better.
Fixes #25667.
2023-01-09 11:48:33 +04:00
John Preston
9b74958fab Fix send-as button appearance after joining the group. 2023-01-09 11:18:58 +04:00
John Preston
7091fb9448 Force weak linking of NaturalLanguage framework.
Fixes #25429.
2023-01-09 10:50:41 +04:00
John Preston
876cdcf26a Show "Photo set by you" in profile photos list. 2023-01-09 09:56:36 +04:00
John Preston
36eca970f2 Pass correct parent to choose folder file dialog.
I hope it fixes #25689, although it didn't work for me, not crash.
2023-01-09 09:44:58 +04:00
John Preston
21232e09a4 Fix a crash in topic from archive search.
Fixes #25609.
2023-01-09 09:36:20 +04:00
John Preston
2d9d373c7f Don't show "Report" for personal contact photos. 2023-01-09 09:20:36 +04:00
John Preston
23387d6625 Don't drag-n-drop "internal:" URLs. 2023-01-09 09:10:53 +04:00
John Preston
6137c64444 Fix scale preview on macOS. 2023-01-09 09:05:07 +04:00
John Preston
43a830f0af Fix in-window preview (Wayland / noCompositing). 2023-01-09 09:05:07 +04:00
John Preston
ff331c040a Allow huge range of interface scales. 2023-01-09 09:05:07 +04:00
John Preston
3532e187fd Write more DPI logs. 2023-01-09 09:05:07 +04:00
23rd
6467ba7739 Fixed painting of photo with spoiler in SendFilesBox on Retina. 2023-01-09 09:05:07 +04:00
23rd
8de3b2c0d3 Fixed painting of non personal photo in UserpicButton on Retina. 2023-01-09 09:05:07 +04:00
23rd
deeb022e0b Fixed possible crash on deleting own channel. 2023-01-09 09:05:07 +04:00
23rd
9e0e28dc45 Improved style of box for editing of linked chat. 2023-01-09 09:05:07 +04:00
23rd
c99ac0a264 Moved creation of divider with text and lottie to single place. 2023-01-09 09:05:07 +04:00
23rd
991fafb30e Fixed clickable online status in profile settings. 2023-01-09 09:05:06 +04:00
23rd
5cf5d4b4c4 Improved opening main menu from top left corner.
Fixed #17423.
2023-01-09 09:05:06 +04:00
23rd
38e42f9a95 Fixed ripple color of main menu button in narrow forum state. 2023-01-09 09:05:06 +04:00
23rd
9b7689993f Fixed phrase of global TTL for new chats. 2023-01-09 09:05:06 +04:00
23rd
0e3eddcb77 Slightly improved TTL badge in dialogs list. 2023-01-09 09:05:06 +04:00
23rd
3f829ef3b9 Accepted tg://login only for started domain. 2023-01-09 09:05:06 +04:00
23rd
de8d93ba73 Added support of multiline subtext for many usernames to info layer. 2023-01-09 09:05:06 +04:00
23rd
dad9f4b87d Flipped icon for rotation button in photo editor. 2023-01-09 09:05:06 +04:00
23rd
0f538e2606 Updated Qt to 5.15.8 on Windows. 2023-01-09 09:05:06 +04:00
John Preston
08fa6a9815 Version 4.5.3.
- Attempt to fix incoming video in calls from mobile apps.
2023-01-06 21:57:04 +04:00
John Preston
a7cf4027ea Attempt to fix calls incoming video. 2023-01-06 21:55:08 +04:00
Ilya Fedin
646c7ecceb Update Qt version for Linux in cmake 2023-01-06 12:57:54 +04:00
Ilya Fedin
3cbbe3d3c2 Update Qt to 6.4.2 on Linux 2023-01-05 23:00:13 +04:00
John Preston
0af26dd353 Capture mouse in PipeWire screen capture. 2023-01-05 10:09:44 +04:00
John Preston
159e366122 Choose screens/windows in Wayland screencapture.
Fixes #25674.
2023-01-05 09:55:46 +04:00
John Preston
b9081c26ba Use tg://settings/edit_profile instead /information. 2023-01-05 09:55:38 +04:00
John Preston
9933c6ba59 Mark topics as read using Ctrl+R shortcut.
Fixes #25669.
2023-01-05 09:53:51 +04:00
John Preston
eb0642f569 Version 4.5.2.
- Fix unread reactions button in private chats.
- Fix tile background saving after an app update.
- Allow Ctrl+6,7,8 to activate extra pinned chats.
2023-01-03 11:11:02 +04:00
John Preston
1cce35a5a5 Fix multiline checkbox geometry counting. 2023-01-03 11:06:40 +04:00
John Preston
aeb71e089a Fix tile background saving after an app update.
Fixes #25666, I hope fixes #16468, I hope fixes #5944.
2023-01-03 10:43:55 +04:00
John Preston
b962efeca3 Allow ctrl+6/7/8 to activate extra pinned chats.
Fixes #25647.
2023-01-03 09:59:42 +04:00
John Preston
eb6c350e72 Fix unread reactions button in chats with users.
Regression was introduced in 6a7f030ee7.

Fixes #25661.
2023-01-03 09:22:46 +04:00
John Preston
d496d41e7e Version 4.5.1: Fix excessive flood_wait trigger.
Regression was introduced in 1e8dfb7315.

Fixes #25494.
2023-01-02 17:33:39 +04:00
John Preston
19aa4f4acc Version 4.5.1.
- Fix crash in profile photo privacy edition.
- Allow sending photos larger than 1280px (in Experimental Settings).
2023-01-02 16:02:19 +04:00
John Preston
19350e3846 Open type="document" with photo as a photo.
Fixes #25600.
2023-01-02 15:08:36 +04:00
John Preston
741b524d71 Add description to an option (looks better). 2023-01-02 15:08:28 +04:00
John Preston
84288112fc Allow sending photos larger 1280 (experimental).
Improves #6520.
2023-01-02 14:26:41 +04:00
John Preston
7c537cd787 Revert "Removed downscaling of 2560px images before displaying them"
This reverts commit 0f3ec7893d.

Instead correct max limits of 2560x2560 will be used.
2023-01-02 14:26:41 +04:00
Ilya Fedin
c56977cbc1 Check autostart enabling success on Linux 2023-01-02 13:10:17 +04:00
John Preston
2afa2cd9ab Fix scroll reset bug in topics on message removal. 2023-01-02 12:26:20 +04:00
John Preston
442d0da5c1 Force autostart folder creation.
Also show an error if autostart couldn't be enabled.

Fixes #25608.
2023-01-02 12:26:20 +04:00
Ilya Fedin
db6bdf36af Update patches 2023-01-02 11:19:15 +04:00
Ilya Fedin
b246328dcf Use latest mesa in snap 2023-01-02 11:19:15 +04:00
John Preston
a27ea35edd Fix possible memory leak in jpeg inspecting. 2023-01-02 11:07:57 +04:00
John Preston
a7c4aea9ff Revert "Clear draft that failed to be saved."
This reverts commit 7866013ab6.

Loosing the current field text in case the server doesn't accept
the draft is worse than showing some sticked draft in the list.

We always can just hide the cloud draft in chats list in case you
can't edit it really if there are reports about them.
2023-01-02 10:50:59 +04:00
GitHub Action
1ba870a655 Update User-Agent for DNS to Chrome 108.0.5359.98. 2023-01-02 10:10:34 +04:00
GitHub Action
5bc3cf56fd Update copyright year to 2023. 2023-01-02 10:10:14 +04:00
John Preston
3c4cf2862b Fix crash in profile photo privacy edition.
Fixes #25645.
2023-01-02 10:09:31 +04:00
Daniel Novomeský
af69a7a01f Upgrade highway, libde265, libavif, libheif on Linux 2023-01-01 13:21:08 +04:00
Ilya Fedin
b9f7a501f5 Do pacman -Syu twice in prepare.py
So new databases are downloaded in case runtime updates and gets new repostiories
2023-01-01 13:19:11 +04:00
Ilya Fedin
322a085b70 Fix the check for Native Tools Command Prompt in prepare.py 2022-12-31 16:03:27 +04:00
Ilya Fedin
6c4dc34441 Fix build with various Windows locales 2022-12-31 16:02:28 +04:00
Ilya Fedin
efa287b786 Use text=True instead of decode() in prepare.py 2022-12-31 16:02:28 +04:00
John Preston
23e1c6128b Specify no non-exempt encryption usage in plist. 2022-12-30 17:30:18 +04:00
John Preston
bc71a2619a Version 4.5: Fix build with GCC. 2022-12-30 16:16:35 +04:00
John Preston
4f3510c47c Version 4.5: Fix search in topic. 2022-12-30 15:50:56 +04:00
John Preston
2adc20f07f Version 4.5.
- Media with spoiler effects.
You can wrap photos and videos you send in a fuzzy cover
by selecting media in the attachment menu
and tapping (...) > Hide With Spoiler.

- Setting pictures for your contacts.
You can choose your own picture for
a contact – only you will see it on their profile.

- Suggested profile pictures.
When editing your contacts, you can suggest
a photo for their profile. It will take them just two clicks
to add the picture you suggested.

- Public profile pictures.
If you only allow certain users to see your profile photos,
you can set a public picture for everyone else.

- Ultimate profile picture privacy.
You can set 'Who can see my profile photos' to 'Nobody'
and add some users or groups as exceptions.

- Member list privacy.
Owners of large groups can hide the list of members.
2022-12-30 14:55:09 +04:00
John Preston
b6ade7ce19 Fix spoiler / custom emoji in pinned bar unpause. 2022-12-30 14:27:00 +04:00
John Preston
cabed9587b Close PiP if message with video gets deleted. 2022-12-30 14:26:43 +04:00
John Preston
0ce01410a1 Fix crash in Pip-to-Viewer after message deletion.
Fixes #25262. Fixes #25522.
2022-12-30 14:18:04 +04:00
John Preston
d02819db13 Support spoilers in reply previews / pinned bar. 2022-12-30 14:06:20 +04:00
John Preston
46bae9ed74 Remove splits reverse. It was done on the server. 2022-12-30 10:49:51 +04:00
23rd
693ff3398e Fixed changing of button style between states within single intro step. 2022-12-29 23:25:53 +03:00
John Preston
567216f41f Fix crash in topic jump ripple animation.
Fixes #25500.
2022-12-29 17:48:33 +04:00
John Preston
1ef0791bc6 Fix OOM crash on wrong attached stickers hash.
Fixes #25495.
2022-12-29 17:11:24 +04:00
178 changed files with 2802 additions and 1259 deletions

View File

@@ -56,11 +56,11 @@ include(cmake/options.cmake)
if (NOT DESKTOP_APP_USE_PACKAGED)
if (WIN32)
set(qt_version 5.15.7)
set(qt_version 5.15.8)
elseif (APPLE)
set(qt_version 6.3.2)
else()
set(qt_version 6.4.1)
set(qt_version 6.4.2)
endif()
endif()
include(cmake/external/qt/package.cmake)

2
LEGAL
View File

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

View File

@@ -841,8 +841,6 @@ PRIVATE
info/profile/info_profile_cover.h
info/profile/info_profile_emoji_status_panel.cpp
info/profile/info_profile_emoji_status_panel.h
info/profile/info_profile_icon.cpp
info/profile/info_profile_icon.h
info/profile/info_profile_inner_widget.cpp
info/profile/info_profile_inner_widget.h
info/profile/info_profile_members.cpp
@@ -1139,7 +1137,6 @@ PRIVATE
platform/platform_integration.h
platform/platform_main_window.h
platform/platform_notifications_manager.h
platform/platform_specific.cpp
platform/platform_specific.h
platform/platform_tray.h
platform/platform_window_title.h
@@ -1201,6 +1198,8 @@ PRIVATE
settings/settings_privacy_controllers.h
settings/settings_privacy_security.cpp
settings/settings_privacy_security.h
settings/settings_scale_preview.cpp
settings/settings_scale_preview.h
settings/settings_type.h
storage/details/storage_file_utilities.cpp
storage/details/storage_file_utilities.h

Binary file not shown.

View File

@@ -755,7 +755,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_download_path" = "Download path";
"lng_download_path_temp" = "Temp folder";
"lng_download_path_default" = "Default folder";
"lng_download_path_unset" = "Unset";
"lng_download_path_clear" = "Clear all";
"lng_download_path_header" = "Choose download path";
"lng_download_path_default_radio" = "Telegram folder in system «Downloads»";
@@ -763,7 +762,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_download_path_dir_radio" = "Custom folder, cleared only manually";
"lng_download_path_choose" = "Choose download path";
"lng_sure_clear_downloads" = "Do you want to remove all downloaded files from temp folder? It is done automatically on logout or program uninstall.";
"lng_download_path_failed" = "File download could not be started.\n\nThis might be because the download location you've selected is invalid. Try changing the \"Download path\" in Settings.";
"lng_download_path_failed" = "File download could not be started.\n\nThe default download location will be used now. You can always change it in Settings > Advanced > Download Path.\n\nPlease try once again.";
"lng_download_path_settings" = "Settings";
"lng_download_finish_failed" = "File download could not be finished.\n\nWould you like to try again?";
"lng_download_path_clearing" = "Clearing...";
@@ -1332,8 +1331,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_manage_discussion_group_add" = "Add a group";
"lng_manage_linked_channel" = "Linked channel";
"lng_manage_linked_channel_restore" = "Restore linked channel";
"lng_manage_discussion_group_about" = "Select a group chat for discussion that will be displayed in your channel.";
"lng_manage_discussion_group_about_chosen" = "A link to {group} is shown to all subscribers in the bottom panel.";
"lng_manage_discussion_group_about" = "Select a group chat that will host comments from your channel.";
"lng_manage_discussion_group_about_chosen" = "{group} is selected as the group that hosts comments for your channel.";
"lng_manage_discussion_group_create" = "Create a new group";
"lng_manage_discussion_group_unlink" = "Unlink group";
"lng_manage_discussion_group_posted" = "Everything you post in the channel is forwarded to this group.";
@@ -1529,6 +1528,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_ttl_changed_you" = "You set messages to auto-delete in {duration}";
"lng_action_ttl_changed_channel" = "New messages will auto-delete in {duration}";
"lng_action_ttl_global" = "{from} uses a self-destruct timer for all chats. All new messages in this chat will be automatically deleted after {duration} they've been sent.";
"lng_action_ttl_global_me" = "You set a self-destruct timer for all chats. All new messages in this chat will be automatically deleted after {duration} theyve been sent.";
"lng_action_ttl_removed" = "{from} has set messages not to auto-delete";
"lng_action_ttl_removed_you" = "You disabled the auto-delete timer";
"lng_action_ttl_removed_channel" = "New messages will not auto-delete";
@@ -2498,6 +2498,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_mediaview_channel_photo" = "Channel Photo";
"lng_mediaview_profile_photo" = "Profile Photo";
"lng_mediaview_profile_public_photo" = "Public Photo";
"lng_mediaview_profile_photo_by_you" = "Photo set by you";
"lng_mediaview_file_n_of_amount" = "{file} {n} of {amount}";
"lng_mediaview_n_of_amount" = "Photo {n} of {amount}";
"lng_mediaview_doc_image" = "File";

View File

@@ -9,5 +9,6 @@
<file alias="cloud_password/hint.tgs">../../animations/cloud_password/hint.tgs</file>
<file alias="cloud_password/email.tgs">../../animations/cloud_password/email.tgs</file>
<file alias="ttl.tgs">../../animations/ttl.tgs</file>
<file alias="discussion.tgs">../../animations/discussion.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.4.3.0" />
Version="4.5.6.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,4,3,0
PRODUCTVERSION 4,4,3,0
FILEVERSION 4,5,6,0
PRODUCTVERSION 4,5,6,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.4.3.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2022"
VALUE "FileVersion", "4.5.6.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "4.4.3.0"
VALUE "ProductVersion", "4.5.6.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,4,3,0
PRODUCTVERSION 4,4,3,0
FILEVERSION 4,5,6,0
PRODUCTVERSION 4,5,6,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.4.3.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2022"
VALUE "FileVersion", "4.5.6.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "4.4.3.0"
VALUE "ProductVersion", "4.5.6.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -148,6 +148,7 @@ void EditMessageWithUploadedMedia(
MTPInputMedia media) {
const auto done = [=](Fn<void()> applyUpdates) {
if (item) {
item->removeFromSharedMediaIndex();
item->clearSavedMedia();
item->setIsLocalUpdateMedia(true);
applyUpdates();

View File

@@ -36,7 +36,7 @@ bool UnreadThings::trackMentions(Data::Thread *thread) const {
bool UnreadThings::trackReactions(Data::Thread *thread) const {
const auto peer = thread ? thread->peer().get() : nullptr;
return peer && (peer->isChat() || peer->isMegagroup());
return peer && (peer->isUser() || peer->isChat() || peer->isMegagroup());
}
void UnreadThings::preloadEnough(Data::Thread *thread) {

View File

@@ -2137,7 +2137,6 @@ void ApiWrap::saveDraftsToCloud() {
if (const auto cloudDraft = history->cloudDraft(topicRootId)) {
if (cloudDraft->saveRequestId == requestId) {
history->clearCloudDraft(topicRootId);
history->applyCloudDraft(topicRootId);
}
}
const auto i = _draftsSaveRequestIds.find(weak);

View File

@@ -147,7 +147,9 @@ void DownloadPathBox::setPathText(const QString &text) {
DownloadPathBox::Directory DownloadPathBox::typeFromPath(
const QString &path) {
if (path.isEmpty()) {
return Directory::Downloads;
return Core::App().canReadDefaultDownloadPath(true)
? Directory::Downloads
: Directory::Temp;
} else if (path == FileDialog::Tmp()) {
return Directory::Temp;
}

View File

@@ -451,7 +451,8 @@ void EditCaptionBox::setupPhotoEditorEventHandler() {
&file.information->media);
image->modifications = mods;
Storage::UpdateImageDetails(file, previewWidth);
const auto sideLimit = PhotoSideLimit();
Storage::UpdateImageDetails(file, previewWidth, sideLimit);
rebuildPreview();
};
const auto fileImage = std::make_shared<Image>(*large);

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "settings/settings_common.h"
#include "data/data_changes.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
@@ -25,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_info.h"
#include "styles/style_settings.h"
namespace {
@@ -208,88 +210,24 @@ void Controller::choose(not_null<ChatData*> chat) {
Ui::LayerOption::KeepOther);
}
object_ptr<Ui::RpWidget> SetupAbout(
not_null<QWidget*> parent,
[[nodiscard]] rpl::producer<TextWithEntities> About(
not_null<ChannelData*> channel,
ChannelData *chat) {
auto about = object_ptr<Ui::FlatLabel>(
parent,
QString(),
st::linkedChatAbout);
about->setMarkedText([&] {
if (!channel->isBroadcast()) {
return tr::lng_manage_linked_channel_about(
tr::now,
lt_channel,
Ui::Text::Bold(chat->name()),
Ui::Text::WithEntities);
} else if (chat != nullptr) {
return tr::lng_manage_discussion_group_about_chosen(
tr::now,
lt_group,
Ui::Text::Bold(chat->name()),
Ui::Text::WithEntities);
}
return tr::lng_manage_discussion_group_about(
tr::now,
if (!channel->isBroadcast()) {
return tr::lng_manage_linked_channel_about(
lt_channel,
rpl::single(Ui::Text::Bold(chat->name())),
Ui::Text::WithEntities);
}());
return about;
} else if (chat != nullptr) {
return tr::lng_manage_discussion_group_about_chosen(
lt_group,
rpl::single(Ui::Text::Bold(chat->name())),
Ui::Text::WithEntities);
}
return tr::lng_manage_discussion_group_about(Ui::Text::WithEntities);
}
object_ptr<Ui::RpWidget> SetupFooter(
not_null<QWidget*> parent,
not_null<ChannelData*> channel) {
return object_ptr<Ui::FlatLabel>(
parent,
(channel->isBroadcast()
? tr::lng_manage_discussion_group_posted
: tr::lng_manage_linked_channel_posted)(),
st::linkedChatAbout);
}
object_ptr<Ui::RpWidget> SetupCreateGroup(
not_null<QWidget*> parent,
not_null<Window::SessionNavigation*> navigation,
not_null<ChannelData*> channel,
Fn<void(ChannelData*)> callback) {
Expects(channel->isBroadcast());
auto result = object_ptr<Ui::SettingsButton>(
parent,
tr::lng_manage_discussion_group_create(
) | Ui::Text::ToUpper(),
st::infoCreateLinkedChatButton);
result->addClickHandler([=] {
const auto guarded = crl::guard(parent, callback);
Window::Show(navigation).showBox(
Box<GroupInfoBox>(
navigation,
GroupInfoBox::Type::Megagroup,
channel->name() + " Chat",
guarded),
Ui::LayerOption::KeepOther);
});
return result;
}
object_ptr<Ui::RpWidget> SetupUnlink(
not_null<QWidget*> parent,
not_null<ChannelData*> channel,
Fn<void(ChannelData*)> callback) {
auto result = object_ptr<Ui::SettingsButton>(
parent,
(channel->isBroadcast()
? tr::lng_manage_discussion_group_unlink
: tr::lng_manage_linked_channel_unlink)() | Ui::Text::ToUpper(),
st::infoUnlinkChatButton);
result->addClickHandler([=] {
callback(nullptr);
});
return result;
}
object_ptr<Ui::BoxContent> EditLinkedChatBox(
[[nodiscard]] object_ptr<Ui::BoxContent> EditLinkedChatBox(
not_null<Window::SessionNavigation*> navigation,
not_null<ChannelData*> channel,
ChannelData *chat,
@@ -298,27 +236,77 @@ object_ptr<Ui::BoxContent> EditLinkedChatBox(
Fn<void(ChannelData*)> callback) {
Expects((channel->isBroadcast() && canEdit) || (chat != nullptr));
const auto init = [=](not_null<PeerListBox*> box) {
class ListBox final : public PeerListBox {
public:
ListBox(
QWidget *parent,
std::unique_ptr<PeerListController> controller,
Fn<void(not_null<ListBox*>)> init)
: PeerListBox(
parent,
std::move(controller),
[=](not_null<PeerListBox*>) { init(this); }) {
}
void showFinished() override {
_showFinished.fire({});
}
rpl::producer<> showFinishes() const {
return _showFinished.events();
}
private:
rpl::event_stream<> _showFinished;
};
const auto init = [=](not_null<ListBox*> box) {
auto above = object_ptr<Ui::VerticalLayout>(box);
above->add(
SetupAbout(above, channel, chat),
st::linkedChatAboutPadding);
Settings::AddDividerTextWithLottie(
above,
box->showFinishes(),
About(channel, chat),
u"discussion"_q);
if (!chat) {
above->add(SetupCreateGroup(
Assert(channel->isBroadcast());
Settings::AddSkip(above);
Settings::AddButton(
above,
navigation,
channel,
callback));
tr::lng_manage_discussion_group_create(),
st::infoCreateLinkedChatButton,
{ &st::settingsIconChat, Settings::kIconLightBlue }
)->addClickHandler([=, parent = above.data()] {
const auto guarded = crl::guard(parent, callback);
Window::Show(navigation).showBox(
Box<GroupInfoBox>(
navigation,
GroupInfoBox::Type::Megagroup,
channel->name() + " Chat",
guarded),
Ui::LayerOption::KeepOther);
});
}
box->peerListSetAboveWidget(std::move(above));
auto below = object_ptr<Ui::VerticalLayout>(box);
if (chat && canEdit) {
below->add(SetupUnlink(below, channel, callback));
Settings::AddButton(
below,
(channel->isBroadcast()
? tr::lng_manage_discussion_group_unlink
: tr::lng_manage_linked_channel_unlink)(),
st::infoUnlinkChatButton,
{ &st::settingsIconMinus, Settings::kIconRed }
)->addClickHandler([=] { callback(nullptr); });
Settings::AddSkip(below);
}
below->add(
SetupFooter(below, channel),
st::linkedChatAboutPadding);
Settings::AddDividerText(
below,
(channel->isBroadcast()
? tr::lng_manage_discussion_group_posted
: tr::lng_manage_linked_channel_posted)());
box->peerListSetBelowWidget(std::move(below));
box->setTitle(channel->isBroadcast()
@@ -339,7 +327,7 @@ object_ptr<Ui::BoxContent> EditLinkedChatBox(
std::move(chats),
std::move(callback),
std::move(showHistoryCallback));
return Box<PeerListBox>(std::move(controller), init);
return Box<ListBox>(std::move(controller), init);
}
} // namespace

View File

@@ -17,14 +17,14 @@ namespace Window {
class SessionNavigation;
} // namespace Window
object_ptr<Ui::BoxContent> EditLinkedChatBox(
[[nodiscard]] object_ptr<Ui::BoxContent> EditLinkedChatBox(
not_null<Window::SessionNavigation*> navigation,
not_null<ChannelData*> channel,
not_null<ChannelData*> chat,
bool canEdit,
Fn<void(ChannelData*)> callback);
object_ptr<Ui::BoxContent> EditLinkedChatBox(
[[nodiscard]] object_ptr<Ui::BoxContent> EditLinkedChatBox(
not_null<Window::SessionNavigation*> navigation,
not_null<ChannelData*> channel,
std::vector<not_null<PeerData*>> &&chats,

View File

@@ -1913,7 +1913,7 @@ void Controller::deleteChannel() {
const auto session = &_peer->session();
_navigation->parentController()->hideLayer();
Core::App().closeChatFromWindows(_peer);
Core::App().closeChatFromWindows(channel);
if (chat) {
session->api().deleteConversation(chat, false);
}

View File

@@ -348,8 +348,9 @@ void SendFilesBox::enqueueNextPrepare() {
_list.filesToProcess.pop_front();
const auto weak = Ui::MakeWeak(this);
_preparing = true;
crl::async([weak, file = std::move(file)]() mutable {
Storage::PrepareDetails(file, st::sendMediaPreviewSize);
const auto sideLimit = PhotoSideLimit(); // Get on main thread.
crl::async([weak, sideLimit, file = std::move(file)]() mutable {
Storage::PrepareDetails(file, st::sendMediaPreviewSize, sideLimit);
crl::on_main([weak, file = std::move(file)]() mutable {
if (weak) {
weak->addPreparedAsyncFile(std::move(file));

View File

@@ -252,15 +252,6 @@ void Application::run() {
return;
}
if (KSandbox::isInside()) {
const auto path = settings().downloadPath();
if (!path.isEmpty()
&& path != FileDialog::Tmp()
&& !base::CanReadDirectory(path)) {
settings().setDownloadPath(QString());
}
}
_translator = std::make_unique<Lang::Translator>();
QCoreApplication::instance()->installTranslator(_translator.get());
@@ -606,8 +597,7 @@ bool Application::canReadDefaultDownloadPath(bool always) const {
}
bool Application::canSaveFileWithoutAskingForPath() const {
return !Core::App().settings().askDownloadPath()
&& canReadDefaultDownloadPath();
return !Core::App().settings().askDownloadPath();
}
MTP::Config &Application::fallbackProductionConfig() const {

View File

@@ -22,61 +22,6 @@ namespace {
std::map<int, const char*> BetaLogs() {
return {
{
4000003,
"- Animated emoji for messages.\n"
"- Premium: Privacy settings for voice messages.\n"
"- Premium: Gifting Telegram Premium "
"to any user from their profile page.\n"
},
{
4000004,
"- Allow sending animated emoji to Saved Messages "
"even without Telegram Premium.\n"
"- Premium: Suggest animated emoji by regular emoji "
"(can be disabled in Settings).\n"
"- Premium: Show all suggested premium stickers "
"in a special section of the stickers panel.\n"
"- Premium: Allow hiding premium stickers special section "
"of the stickers panel.\n"
"- Fix a memory leak in RTMP livestreams.\n"
"- Fix some bot webview bugs on macOS.\n"
"- Fix forwarding of voice messages.\n"
},
{
4001002,
"- New reaction selector above the right click menu.\n"
"- Premium: Set any custom emoji reactions in private chats.\n"
"- Premium: Set any custom emoji as your profile status.\n"
"- Insert or copy custom emoji from pack preview.\n"
},
{
4002001,
"- Improve scaling / cropping for photos / video files.\n"
"- Improve touch support in channel comments.\n"
"- Nice animation for spoilers.\n"
},
{
4002002,
"- Fix crash in spoiler revealing in media captions.\n"
"- Fix spoiler revealing in media viewer captions.\n"
"- Fix crash in folder editing on Linux.\n"
},
{
4004002,
"- Send photos and video files hidden by a spoiler effect.\n"
@@ -93,6 +38,32 @@ std::map<int, const char*> BetaLogs() {
"- Fix a crash in own profile photo updating.\n"
"- Bug fixes and other minor improvements.\n"
},
{
4005004,
"- Allow wide range of interface scale options.\n"
"- Show opened chat name in the window title.\n"
"- Bug fixes and other minor improvements.\n"
"- Fix updating on macOS older than 10.14.\n"
},
{
4005006,
"- Try enabling non-fractional scale "
"High DPI support on Windows and Linux.\n"
"- Experimental setting for fractional scale "
"High DPI support on Windows and Linux.\n"
"- Fix navigation to bottom problems in groups you didn't join.\n"
"- Fix a crash in chat export settings changes.\n"
"- Fix a crash in sending some of JPEG images.\n"
"- Fix CJK fonts on Windows.\n"
}
};
};

View File

@@ -93,7 +93,8 @@ QString HiddenUrlClickHandler::copyToClipboardContextItemText() const {
}
QString HiddenUrlClickHandler::dragText() const {
return HiddenUrlClickHandler::copyToClipboardText();
const auto result = HiddenUrlClickHandler::copyToClipboardText();
return result.startsWith(u"internal:"_q) ? QString() : result;
}
void HiddenUrlClickHandler::Open(QString url, QVariant context) {

View File

@@ -48,7 +48,32 @@ PreLaunchWindow::PreLaunchWindow(QString title) {
int paddingVertical = (_size / 2);
int paddingHorizontal = _size;
int borderRadius = (_size / 5);
setStyleSheet(u"QPushButton { padding: %1px %2px; background-color: #ffffff; border-radius: %3px; }\nQPushButton#confirm:hover, QPushButton#cancel:hover { background-color: #e3f1fa; color: #2f9fea; }\nQPushButton#confirm { color: #2f9fea; }\nQPushButton#cancel { color: #aeaeae; }\nQLineEdit { border: 1px solid #e0e0e0; padding: 5px; }\nQLineEdit:focus { border: 2px solid #37a1de; padding: 4px; }"_q.arg(paddingVertical).arg(paddingHorizontal).arg(borderRadius));
setStyleSheet(uR"(
QPushButton {
padding: %1px %2px;
background-color: #ffffff;
border-radius: %3px;
}
QPushButton#confirm:hover,
QPushButton#cancel:hover {
background-color: #e3f1fa;
color: #2f9fea;
}
QPushButton#confirm {
color: #2f9fea;
}
QPushButton#cancel {
color: #aeaeae;
}
QLineEdit {
border: 1px solid #e0e0e0;
padding: 5px;
}
QLineEdit:focus {
border: 2px solid #37a1de;
padding: 4px;
}
)"_q.arg(paddingVertical).arg(paddingHorizontal).arg(borderRadius));
if (!PreLaunchWindowInstance) {
PreLaunchWindowInstance = this;
}
@@ -57,7 +82,7 @@ PreLaunchWindow::PreLaunchWindow(QString title) {
void PreLaunchWindow::activate() {
setWindowState(windowState() & ~Qt::WindowMinimized);
setVisible(true);
psActivateProcess();
Platform::ActivateThisProcess();
raise();
activateWindow();
}

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
#include "storage/localstorage.h"
#include "storage/storage_account.h"
#include "base/platform/base_platform_info.h"
#include "base/platform/base_platform_file_utilities.h"
#include "platform/platform_file_utilities.h"
@@ -75,7 +76,7 @@ QString filedialogDefaultName(
QString base;
if (fileTime) {
const auto date = base::unixtime::parse(fileTime);
base = prefix + QLocale().toString(date, "_yyyy-MM-dd_HH-mm-ss");
base = prefix + date.toString("_yyyy-MM-dd_HH-mm-ss");
} else {
struct tm tm;
time_t t = time(NULL);
@@ -171,31 +172,14 @@ QString DefaultDownloadPathFolder(not_null<Main::Session*> session) {
}
QString DefaultDownloadPath(not_null<Main::Session*> session) {
const auto realDefaultPath = QStandardPaths::writableLocation(
if (!Core::App().canReadDefaultDownloadPath()) {
return session->local().tempDirectory();
}
return QStandardPaths::writableLocation(
QStandardPaths::DownloadLocation)
+ '/'
+ DefaultDownloadPathFolder(session)
+ '/';
if (!Core::App().canReadDefaultDownloadPath()) {
QStringList files;
QByteArray remoteContent;
const auto success = Platform::FileDialog::Get(
nullptr,
files,
remoteContent,
tr::lng_download_path_choose(tr::now),
QString(),
FileDialog::internal::Type::ReadFolder,
realDefaultPath);
if (success && !files.isEmpty() && !files[0].isEmpty()) {
const auto result = files[0].endsWith('/') ? files[0] : (files[0] + '/');
Core::App().settings().setDownloadPath(result);
Core::App().saveSettings();
return result;
}
return QString();
}
return realDefaultPath;
}
namespace internal {

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/platform_launcher.h"
#include "platform/platform_specific.h"
#include "base/options.h"
#include "base/platform/base_platform_info.h"
#include "base/platform/base_platform_file_utilities.h"
#include "ui/main_queue_processor.h"
@@ -272,8 +273,18 @@ bool CheckPortableVersionFolder() {
return true;
}
base::options::toggle OptionFractionalScalingEnabled({
.id = kOptionFractionalScalingEnabled,
.name = "Enable precise High DPI scaling",
.description = "Follow system interface scale settings exactly.",
.scope = base::options::windows | base::options::linux,
.restartRequired = true,
});
} // namespace
const char kOptionFractionalScalingEnabled[] = "fractional-scaling-enabled";
std::unique_ptr<Launcher> Launcher::Create(int argc, char *argv[]) {
return std::make_unique<Platform::Launcher>(argc, argv);
}
@@ -294,9 +305,6 @@ void Launcher::init() {
initQtMessageLogging();
QApplication::setApplicationName(u"TelegramDesktop"_q);
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling, true);
QApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::Floor);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// fallback session management is useless for tdesktop since it doesn't have
@@ -311,6 +319,17 @@ void Launcher::init() {
initHook();
}
void Launcher::initHighDpi() {
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
if (OptionFractionalScalingEnabled.value()) {
QApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
} else {
QApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor);
}
}
int Launcher::exec() {
init();
@@ -324,6 +343,9 @@ int Launcher::exec() {
Logs::start(this);
base::options::init(cWorkingDir() + "tdata/experimental_options.json");
// Must be called after options are inited.
initHighDpi();
if (Logs::DebugEnabled()) {
const auto openalLogPath = QDir::toNativeSeparators(
cWorkingDir() + u"DebugLogs/last_openal_log.txt"_q);

View File

@@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Core {
extern const char kOptionFractionalScalingEnabled[];
class Launcher {
public:
Launcher(int argc, char *argv[]);
@@ -52,6 +54,7 @@ private:
void init();
virtual void initHook() {
}
virtual void initHighDpi();
virtual bool launchUpdater(UpdaterLaunch action) = 0;

View File

@@ -50,6 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_premium.h"
#include "mainwidget.h"
#include "main/main_account.h"
#include "main/main_domain.h"
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "inline_bots/bot_attach_web_view.h"
@@ -798,12 +799,13 @@ bool ResolveLoginCode(
const Match &match,
const QVariant &context) {
const auto loginCode = match->captured(2);
if (loginCode.isEmpty()) {
const auto &domain = Core::App().domain();
if (loginCode.isEmpty() || (!controller && !domain.started())) {
return false;
};
(controller
? controller->session().account()
: Core::App().activeAccount()).handleLoginCode(loginCode);
: domain.active()).handleLoginCode(loginCode);
if (controller) {
controller->window().activate();
} else if (const auto window = Core::App().activeWindow()) {
@@ -869,7 +871,7 @@ const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
ResolvePrivatePost
},
{
u"^settings(/language|/devices|/folders|/privacy|/themes|/change_number|/auto_delete|/information)?$"_q,
u"^settings(/language|/devices|/folders|/privacy|/themes|/change_number|/auto_delete|/information|/edit_profile)?$"_q,
ResolveSettings
},
{

View File

@@ -25,19 +25,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/invoke_queued.h"
#include "base/qthelp_url.h"
#include "base/qthelp_regex.h"
#include "base/qt/qt_common_adapters.h"
#include "ui/ui_utility.h"
#include "ui/effects/animations.h"
#include <QtCore/QLockFile>
#include <QtGui/QSessionManager>
#include <QtGui/QScreen>
#include <QtGui/qpa/qplatformscreen.h>
namespace Core {
namespace {
constexpr auto kEmptyPidForCommandResponse = 0ULL;
QChar _toHex(ushort v) {
v = v & 0x000F;
return QChar::fromLatin1((v >= 10) ? ('a' + (v - 10)) : ('0' + v));
@@ -134,7 +132,7 @@ int Sandbox::start() {
[=] { socketDisconnected(); });
connect(
&_localSocket,
base::QLocalSocket_error,
&QLocalSocket::errorOccurred,
[=](QLocalSocket::LocalSocketError error) { socketError(error); });
connect(
&_localSocket,
@@ -210,38 +208,52 @@ void Sandbox::launchApplication() {
}
void Sandbox::setupScreenScale() {
const auto dpi = Sandbox::primaryScreen()->logicalDotsPerInch();
LOG(("Primary screen DPI: %1").arg(dpi));
if (dpi <= 108) {
cSetScreenScale(100); // 100%: 96 DPI (0-108)
} else if (dpi <= 132) {
cSetScreenScale(125); // 125%: 120 DPI (108-132)
} else if (dpi <= 168) {
cSetScreenScale(150); // 150%: 144 DPI (132-168)
} else if (dpi <= 216) {
cSetScreenScale(200); // 200%: 192 DPI (168-216)
} else if (dpi <= 264) {
cSetScreenScale(250); // 250%: 240 DPI (216-264)
} else {
cSetScreenScale(300); // 300%: 288 DPI (264-inf)
}
const auto ratio = devicePixelRatio();
if (ratio > 1.) {
if (!Platform::IsMac() || (ratio != 2.)) {
LOG(("Found non-trivial Device Pixel Ratio: %1").arg(ratio));
LOG(("Environmental variables: QT_DEVICE_PIXEL_RATIO='%1'").arg(qEnvironmentVariable("QT_DEVICE_PIXEL_RATIO")));
LOG(("Environmental variables: QT_SCALE_FACTOR='%1'").arg(qEnvironmentVariable("QT_SCALE_FACTOR")));
LOG(("Environmental variables: QT_AUTO_SCREEN_SCALE_FACTOR='%1'").arg(qEnvironmentVariable("QT_AUTO_SCREEN_SCALE_FACTOR")));
LOG(("Environmental variables: QT_SCREEN_SCALE_FACTORS='%1'").arg(qEnvironmentVariable("QT_SCREEN_SCALE_FACTORS")));
}
style::SetDevicePixelRatio(std::ceil(ratio));
if (Platform::IsMac() && ratio == 2.) {
cSetScreenScale(110); // 110% for Retina screens by default.
} else {
cSetScreenScale(style::kScaleDefault);
LOG(("Global devicePixelRatio: %1").arg(ratio));
const auto logEnv = [](const char *name) {
const auto value = qEnvironmentVariable(name);
if (!value.isEmpty()) {
LOG(("%1: %2").arg(name).arg(value));
}
};
logEnv("QT_DEVICE_PIXEL_RATIO");
logEnv("QT_AUTO_SCREEN_SCALE_FACTOR");
logEnv("QT_ENABLE_HIGHDPI_SCALING");
logEnv("QT_SCALE_FACTOR");
logEnv("QT_SCREEN_SCALE_FACTORS");
logEnv("QT_SCALE_FACTOR_ROUNDING_POLICY");
logEnv("QT_DPI_ADJUSTMENT_POLICY");
logEnv("QT_USE_PHYSICAL_DPI");
logEnv("QT_FONT_DPI");
// Like Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor.
// Round up for .75 and higher. This favors "small UI" over "large UI".
const auto roundedRatio = ((ratio - qFloor(ratio)) < 0.75)
? qFloor(ratio)
: qCeil(ratio);
const auto useRatio = std::clamp(roundedRatio, 1, 3);
style::SetDevicePixelRatio(useRatio);
const auto screen = Sandbox::primaryScreen();
const auto dpi = screen->logicalDotsPerInch();
const auto basePair = screen->handle()->logicalBaseDpi();
const auto base = (basePair.first + basePair.second) * 0.5;
const auto screenScaleExact = dpi / base;
const auto screenScale = int(base::SafeRound(screenScaleExact * 4)) * 25;
LOG(("Primary screen DPI: %1, Base: %2.").arg(dpi).arg(base));
LOG(("Computed screen scale: %1").arg(screenScale));
if (Platform::IsMac()) {
// 110% for Retina screens by default.
cSetScreenScale((useRatio == 2) ? 110 : 100);
} else {
const auto clamped = std::clamp(
screenScale * useRatio,
50 * useRatio,
300);
cSetScreenScale(int(base::SafeRound(clamped * 1. / useRatio)));
}
LOG(("DevicePixelRatio: %1").arg(useRatio));
LOG(("ScreenScale: %1").arg(cScreenScale()));
}
Sandbox::~Sandbox() = default;
@@ -298,17 +310,21 @@ void Sandbox::socketReading() {
return;
}
_localSocketReadData.append(_localSocket.readAll());
if (QRegularExpression("RES:(\\d+);").match(_localSocketReadData).hasMatch()) {
uint64 pid = base::StringViewMid(
_localSocketReadData,
4,
_localSocketReadData.length() - 5).toULongLong();
if (pid != kEmptyPidForCommandResponse) {
psActivateProcess(pid);
}
LOG(("Show command response received, pid = %1, activating and quitting...").arg(pid));
return Quit();
const auto m = QRegularExpression(u"RES:(\\d+)_(\\d+);"_q).match(
_localSocketReadData);
if (!m.hasMatch()) {
return;
}
const auto processId = m.capturedView(1).toULongLong();
const auto windowId = m.capturedView(2).toULongLong();
if (windowId) {
Platform::ActivateOtherProcess(processId, windowId);
}
LOG(("Show command response received, processId = %1, windowId = %2, "
"activating and quitting..."
).arg(processId
).arg(windowId));
return Quit();
}
void Sandbox::socketError(QLocalSocket::LocalSocketError e) {
@@ -426,8 +442,9 @@ void Sandbox::readClients() {
for (int32 to = cmds.indexOf(QChar(';'), from); to >= from; to = (from < l) ? cmds.indexOf(QChar(';'), from) : -1) {
auto cmd = base::StringViewMid(cmds, from, to - from);
if (cmd.startsWith(u"CMD:"_q)) {
execExternal(cmds.mid(from + 4, to - from - 4));
const auto response = u"RES:%1;"_q.arg(QApplication::applicationPid()).toLatin1();
const auto processId = QApplication::applicationPid();
const auto windowId = execExternal(cmds.mid(from + 4, to - from - 4));
const auto response = u"RES:%1_%2;"_q.arg(processId).arg(windowId).toLatin1();
i->first->write(response.data(), response.size());
} else if (cmd.startsWith(u"SEND:"_q)) {
if (cSendPaths().isEmpty()) {
@@ -437,14 +454,12 @@ void Sandbox::readClients() {
qputenv("XDG_ACTIVATION_TOKEN", _escapeFrom7bit(cmds.mid(from + 21, to - from - 21)).toUtf8());
} else if (cmd.startsWith(u"OPEN:"_q)) {
startUrl = _escapeFrom7bit(cmds.mid(from + 5, to - from - 5)).mid(0, 8192);
auto activateRequired = StartUrlRequiresActivate(startUrl);
if (activateRequired) {
execExternal("show");
}
const auto responsePid = activateRequired
? QApplication::applicationPid()
: kEmptyPidForCommandResponse;
const auto response = u"RES:%1;"_q.arg(responsePid).toLatin1();
const auto activationRequired = StartUrlRequiresActivate(startUrl);
const auto processId = QApplication::applicationPid();
const auto windowId = activationRequired
? execExternal("show")
: 0;
const auto response = u"RES:%1_%2;"_q.arg(processId).arg(windowId).toLatin1();
i->first->write(response.data(), response.size());
} else {
LOG(("Sandbox Error: unknown command %1 passed in local socket").arg(cmd.toString()));
@@ -643,17 +658,21 @@ void Sandbox::closeApplication() {
_updateChecker = nullptr;
}
void Sandbox::execExternal(const QString &cmd) {
uint64 Sandbox::execExternal(const QString &cmd) {
DEBUG_LOG(("Sandbox Info: executing external command '%1'").arg(cmd));
if (cmd == "show") {
if (Core::IsAppLaunched() && Core::App().primaryWindow()) {
Core::App().primaryWindow()->activate();
} else if (PreLaunchWindow::instance()) {
PreLaunchWindow::instance()->activate();
const auto window = Core::App().primaryWindow();
window->activate();
return Platform::ActivationWindowId(window->widget());
} else if (const auto window = PreLaunchWindow::instance()) {
window->activate();
return Platform::ActivationWindowId(window);
}
} else if (cmd == "quit") {
Quit();
}
return 0;
}
} // namespace Core

View File

@@ -95,7 +95,9 @@ private:
void singleInstanceChecked();
void launchApplication();
void setupScreenScale();
void execExternal(const QString &cmd);
// Return window id for activation.
uint64 execExternal(const QString &cmd);
// Single instance application
void socketConnected();

View File

@@ -370,6 +370,9 @@ void Manager::fillDefaults() {
set(u"ctrl+3"_q, Command::ChatPinned3);
set(u"ctrl+4"_q, Command::ChatPinned4);
set(u"ctrl+5"_q, Command::ChatPinned5);
set(u"ctrl+6"_q, Command::ChatPinned6);
set(u"ctrl+7"_q, Command::ChatPinned7);
set(u"ctrl+8"_q, Command::ChatPinned8);
auto &&folders = ranges::views::zip(
kShowFolder,

View File

@@ -34,6 +34,9 @@ enum class Command {
ChatPinned3,
ChatPinned4,
ChatPinned5,
ChatPinned6,
ChatPinned7,
ChatPinned8,
ShowAllChats,
ShowFolder1,

View File

@@ -13,7 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer.h"
#include "base/bytes.h"
#include "base/unixtime.h"
#include "base/qt/qt_common_adapters.h"
#include "storage/localstorage.h"
#include "core/application.h"
#include "core/changelogs.h"
@@ -664,7 +663,7 @@ void HttpChecker::start() {
_reply->connect(_reply, &QNetworkReply::finished, [=] {
gotResponse();
});
_reply->connect(_reply, base::QNetworkReply_error, [=](auto e) {
_reply->connect(_reply, &QNetworkReply::errorOccurred, [=](auto e) {
gotFailure(e);
});
}
@@ -703,7 +702,7 @@ void HttpChecker::clearSentRequest() {
return;
}
reply->disconnect(reply, &QNetworkReply::finished, nullptr, nullptr);
reply->disconnect(reply, base::QNetworkReply_error, nullptr, nullptr);
reply->disconnect(reply, &QNetworkReply::errorOccurred, nullptr, nullptr);
reply->abort();
reply->deleteLater();
_manager = nullptr;
@@ -857,7 +856,7 @@ void HttpLoaderActor::sendRequest() {
&HttpLoaderActor::partFinished);
connect(
_reply.get(),
base::QNetworkReply_error,
&QNetworkReply::errorOccurred,
this,
&HttpLoaderActor::partFailed);
connect(

View File

@@ -7,11 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "logs.h"
#include "base/basic_types.h"
#include "base/flags.h"
#include "base/algorithm.h"
#include "base/assertion.h"
#include "base/bytes.h"
#include <crl/crl_time.h>
@@ -182,52 +180,3 @@ static int32 FullArcLength = 360 * 16;
static int32 QuarterArcLength = (FullArcLength / 4);
static int32 MinArcLength = (FullArcLength / 360);
static int32 AlmostFullArcLength = (FullArcLength - MinArcLength);
// This pointer is used for global non-POD variables that are allocated
// on demand by createIfNull(lambda) and are never automatically freed.
template <typename T>
class NeverFreedPointer {
public:
NeverFreedPointer() = default;
NeverFreedPointer(const NeverFreedPointer<T> &other) = delete;
NeverFreedPointer &operator=(const NeverFreedPointer<T> &other) = delete;
template <typename... Args>
void createIfNull(Args&&... args) {
if (isNull()) {
reset(new T(std::forward<Args>(args)...));
}
};
T *data() const {
return _p;
}
T *release() {
return base::take(_p);
}
void reset(T *p = nullptr) {
delete _p;
_p = p;
}
bool isNull() const {
return data() == nullptr;
}
void clear() {
reset();
}
T *operator->() const {
return data();
}
T &operator*() const {
Assert(!isNull());
return *data();
}
explicit operator bool() const {
return !isNull();
}
private:
T *_p;
};

View File

@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
constexpr auto AppName = "Telegram Desktop"_cs;
constexpr auto AppFile = "Telegram"_cs;
constexpr auto AppVersion = 4004003;
constexpr auto AppVersionStr = "4.4.3";
constexpr auto AppVersion = 4005006;
constexpr auto AppVersionStr = "4.5.6";
constexpr auto AppBetaVersion = true;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -7,11 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_abstract_structure.h"
#include "base/never_freed_pointer.h"
namespace Data {
namespace {
using DataStructures = OrderedSet<AbstractStructure**>;
NeverFreedPointer<DataStructures> structures;
base::NeverFreedPointer<DataStructures> structures;
} // namespace

View File

@@ -279,11 +279,11 @@ void LoadCloudFile(
if (const auto onstack = progress) {
onstack();
}
}, [=, &file](bool started) {
}, [=, &file](FileLoader::Error error) {
finish(file);
file.flags |= CloudFile::Flag::Failed;
if (const auto onstack = fail) {
onstack(started);
onstack(error.started);
}
}, [=, &file] {
finish(file);

View File

@@ -988,8 +988,9 @@ void DocumentData::handleLoaderUpdates() {
_loader->updates(
) | rpl::start_with_next_error_done([=] {
_owner->documentLoadProgress(this);
}, [=](bool started) {
if (started && _loader) {
}, [=](FileLoader::Error error) {
using FailureReason = FileLoader::FailureReason;
if (error.started && _loader) {
const auto origin = _loader->fileOrigin();
const auto failedFileName = _loader->fileName();
const auto retry = [=] {
@@ -1000,23 +1001,21 @@ void DocumentData::handleLoaderUpdates() {
tr::lng_download_finish_failed(),
crl::guard(&session(), retry)
}));
} else {
// Sometimes we have LOCATION_INVALID error in documents / stickers.
// Sometimes FILE_REFERENCE_EXPIRED could not be handled.
//
//const auto openSettings = [=] {
// Core::App().settings().etDownloadPathBookmark(QByteArray());
// Core::App().settings().setDownloadPath(QString());
// Ui::show(Box<DownloadPathBox>());
//};
//Ui::show(Box<Ui::ConfirmBox>(
// tr::lng_download_path_failed(tr::now),
// tr::lng_download_path_settings(tr::now),
// crl::guard(&session(), openSettings)));
} else if (error.failureReason == FailureReason::FileWriteFailure) {
if (!Core::App().settings().downloadPath().isEmpty()) {
Core::App().settings().setDownloadPathBookmark(QByteArray());
Core::App().settings().setDownloadPath(QString());
Core::App().saveSettingsDelayed();
InvokeQueued(qApp, [] {
Ui::show(
Ui::MakeInformBox(
tr::lng_download_path_failed(tr::now)));
});
}
}
finishLoad();
status = FileDownloadFailed;
_owner->documentLoadFail(this, started);
_owner->documentLoadFail(this, error.started);
}, [=] {
finishLoad();
_owner->documentLoadDone(this);
@@ -1198,26 +1197,29 @@ bool DocumentData::isStickerSetInstalled() const {
Image *DocumentData::getReplyPreview(
Data::FileOrigin origin,
not_null<PeerData*> context) {
not_null<PeerData*> context,
bool spoiler) {
if (!hasThumbnail()) {
return nullptr;
} else if (!_replyPreview) {
_replyPreview = std::make_unique<Data::ReplyPreview>(this);
}
return _replyPreview->image(origin, context);
return _replyPreview->image(origin, context, spoiler);
}
Image *DocumentData::getReplyPreview(not_null<HistoryItem*> item) {
return getReplyPreview(item->fullId(), item->history()->peer);
const auto media = item->media();
const auto spoiler = media && media->hasSpoiler();
return getReplyPreview(item->fullId(), item->history()->peer, spoiler);
}
bool DocumentData::replyPreviewLoaded() const {
bool DocumentData::replyPreviewLoaded(bool spoiler) const {
if (!hasThumbnail()) {
return true;
} else if (!_replyPreview) {
return false;
}
return _replyPreview->loaded();
return _replyPreview->loaded(spoiler);
}
StickerData *DocumentData::sticker() const {

View File

@@ -142,9 +142,10 @@ public:
[[nodiscard]] Image *getReplyPreview(
Data::FileOrigin origin,
not_null<PeerData*> context);
not_null<PeerData*> context,
bool spoiler);
[[nodiscard]] Image *getReplyPreview(not_null<HistoryItem*> item);
[[nodiscard]] bool replyPreviewLoaded() const;
[[nodiscard]] bool replyPreviewLoaded(bool spoiler) const;
[[nodiscard]] StickerData *sticker() const;
[[nodiscard]] Data::FileOrigin stickerSetOrigin() const;

View File

@@ -618,7 +618,7 @@ Image *MediaPhoto::replyPreview() const {
}
bool MediaPhoto::replyPreviewLoaded() const {
return _photo->replyPreviewLoaded();
return _photo->replyPreviewLoaded(_spoiler);
}
TextWithEntities MediaPhoto::notificationText() const {
@@ -854,7 +854,7 @@ Image *MediaFile::replyPreview() const {
}
bool MediaFile::replyPreviewLoaded() const {
return _document->replyPreviewLoaded();
return _document->replyPreviewLoaded(_spoiler);
}
ItemPreview MediaFile::toPreview(ToPreviewOptions options) const {
@@ -1323,7 +1323,7 @@ TextForMimeData MediaLocation::clipboardText() const {
if (!descriptionResult.text.isEmpty()) {
result.append(std::move(descriptionResult));
}
result.append(LocationClickHandler(_point).dragText());
result.append(LocationClickHandler(_point).url());
return result;
}
@@ -1479,10 +1479,11 @@ Image *MediaWebPage::replyPreview() const {
}
bool MediaWebPage::replyPreviewLoaded() const {
const auto spoiler = false;
if (const auto document = MediaWebPage::document()) {
return document->replyPreviewLoaded();
return document->replyPreviewLoaded(spoiler);
} else if (const auto photo = MediaWebPage::photo()) {
return photo->replyPreviewLoaded();
return photo->replyPreviewLoaded(spoiler);
}
return true;
}
@@ -1552,10 +1553,11 @@ Image *MediaGame::replyPreview() const {
}
bool MediaGame::replyPreviewLoaded() const {
const auto spoiler = false;
if (const auto document = _game->document) {
return document->replyPreviewLoaded();
return document->replyPreviewLoaded(spoiler);
} else if (const auto photo = _game->photo) {
return photo->replyPreviewLoaded();
return photo->replyPreviewLoaded(spoiler);
}
return true;
}
@@ -1675,8 +1677,9 @@ Image *MediaInvoice::replyPreview() const {
}
bool MediaInvoice::replyPreviewLoaded() const {
const auto spoiler = false;
if (const auto photo = _invoice.photo) {
return photo->replyPreviewLoaded();
return photo->replyPreviewLoaded(spoiler);
}
return true;
}

View File

@@ -514,7 +514,8 @@ bool ChannelHasActiveCall(not_null<ChannelData*> channel) {
rpl::producer<QImage> PeerUserpicImageValue(
not_null<PeerData*> peer,
int size) {
int size,
std::optional<int> radius) {
return [=](auto consumer) {
auto result = rpl::lifetime();
struct State {
@@ -541,7 +542,10 @@ rpl::producer<QImage> PeerUserpicImageValue(
}
state->key = key;
state->empty = false;
consumer.put_next(peer->generateUserpicImage(state->view, size));
consumer.put_next(peer->generateUserpicImage(
state->view,
size,
radius));
};
peer->session().changes().peerFlagsValue(
peer,

View File

@@ -133,7 +133,8 @@ inline auto PeerFullFlagValue(
[[nodiscard]] rpl::producer<QImage> PeerUserpicImageValue(
not_null<PeerData*> peer,
int size);
int size,
std::optional<int> radius = {});
[[nodiscard]] const AllowedReactions &PeerAllowedReactions(
not_null<PeerData*> peer);

View File

@@ -23,7 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
constexpr auto kPhotoSideLimit = 1280;
constexpr auto kPhotoSideLimit = 2560;
using Data::PhotoMedia;
using Data::PhotoSize;
@@ -209,22 +209,25 @@ bool PhotoData::uploading() const {
Image *PhotoData::getReplyPreview(
Data::FileOrigin origin,
not_null<PeerData*> context) {
not_null<PeerData*> context,
bool spoiler) {
if (!_replyPreview) {
_replyPreview = std::make_unique<Data::ReplyPreview>(this);
}
return _replyPreview->image(origin, context);
return _replyPreview->image(origin, context, spoiler);
}
Image *PhotoData::getReplyPreview(not_null<HistoryItem*> item) {
return getReplyPreview(item->fullId(), item->history()->peer);
const auto media = item->media();
const auto spoiler = media && media->hasSpoiler();
return getReplyPreview(item->fullId(), item->history()->peer, spoiler);
}
bool PhotoData::replyPreviewLoaded() const {
bool PhotoData::replyPreviewLoaded(bool spoiler) const {
if (!_replyPreview) {
return false;
}
return _replyPreview->loaded();
return _replyPreview->loaded(spoiler);
}
void PhotoData::setRemoteLocation(

View File

@@ -66,9 +66,10 @@ public:
[[nodiscard]] Image *getReplyPreview(
Data::FileOrigin origin,
not_null<PeerData*> context);
not_null<PeerData*> context,
bool spoiler);
[[nodiscard]] Image *getReplyPreview(not_null<HistoryItem*> item);
[[nodiscard]] bool replyPreviewLoaded() const;
[[nodiscard]] bool replyPreviewLoaded(bool spoiler) const;
void setRemoteLocation(
int32 dc,

View File

@@ -101,6 +101,14 @@ void PhotoMedia::set(
QImage image,
QByteArray bytes) {
const auto index = PhotoSizeIndex(size);
const auto limit = PhotoData::SideLimit();
if (image.width() > limit || image.height() > limit) {
image = image.scaled(
limit,
limit,
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
}
_images[index] = PhotoImage{
.data = std::make_unique<Image>(std::move(image)),
.bytes = std::move(bytes),

View File

@@ -27,7 +27,11 @@ ReplyPreview::ReplyPreview(not_null<PhotoData*> photo)
ReplyPreview::~ReplyPreview() = default;
void ReplyPreview::prepare(not_null<Image*> image, Images::Options options) {
void ReplyPreview::prepare(
not_null<Image*> image,
Images::Options options,
bool spoiler) {
using namespace Images;
if (image->isNull()) {
return;
}
@@ -41,24 +45,34 @@ void ReplyPreview::prepare(not_null<Image*> image, Images::Options options) {
: QSize(
st::msgReplyBarSize.height(),
h * st::msgReplyBarSize.height() / w);
thumbSize *= cIntRetinaFactor();
options |= Images::Option::TransparentBackground;
thumbSize *= style::DevicePixelRatio();
options |= Option::TransparentBackground;
auto outerSize = st::msgReplyBarSize.height();
auto bitmap = image->pixNoCache(
thumbSize,
{ .options = options, .outer = { outerSize, outerSize } });
_image = std::make_unique<Image>(bitmap.toImage());
_good = ((options & Images::Option::Blur) == 0);
auto original = spoiler
? image->original().scaled(
{ 40, 40 },
Qt::KeepAspectRatio,
Qt::SmoothTransformation)
: image->original();
auto prepared = Prepare(std::move(original), thumbSize, {
.options = options | (spoiler ? Option::Blur : Option()),
.outer = { outerSize, outerSize },
});
(spoiler ? _spoilered : _regular) = std::make_unique<Image>(
std::move(prepared));
_good = spoiler || ((options & Option::Blur) == 0);
}
Image *ReplyPreview::image(
Data::FileOrigin origin,
not_null<PeerData*> context) {
if (_checked) {
return _image.get();
}
if (_document) {
if (!_image || (!_good && _document->hasThumbnail())) {
not_null<PeerData*> context,
bool spoiler) {
auto &image = spoiler ? _spoilered : _regular;
auto &checked = spoiler ? _checkedSpoilered : _checkedRegular;
if (checked) {
return image.get();
} else if (_document) {
if (!image || (!_good && _document->hasThumbnail())) {
if (!_documentMedia) {
_documentMedia = _document->createMediaView();
_documentMedia->thumbnailWanted(origin);
@@ -67,51 +81,66 @@ Image *ReplyPreview::image(
const auto option = _document->isVideoMessage()
? Images::Option::RoundCircle
: Images::Option::None;
if (thumbnail) {
if (spoiler) {
if (const auto image = _documentMedia->thumbnailInline()) {
prepare(image, option, true);
} else if (thumbnail) {
prepare(thumbnail, option, true);
}
} else if (thumbnail) {
prepare(thumbnail, option);
} else if (!_image) {
} else if (!image) {
if (const auto image = _documentMedia->thumbnailInline()) {
prepare(image, option | Images::Option::Blur);
}
}
if (_good || !_document->hasThumbnail()) {
_checked = true;
checked = true;
_documentMedia = nullptr;
}
}
} else {
Assert(_photo != nullptr);
if (!_image || !_good) {
if (!image || !_good) {
const auto inlineThumbnailBytes = _photo->inlineThumbnailBytes();
if (!_photoMedia) {
_photoMedia = _photo->createMediaView();
}
using Size = PhotoSize;
const auto loadThumbnail = inlineThumbnailBytes.isEmpty()
|| _photoMedia->autoLoadThumbnailAllowed(context);
|| (!spoiler
&& _photoMedia->autoLoadThumbnailAllowed(context));
if (loadThumbnail) {
_photoMedia->wanted(PhotoSize::Small, origin);
_photoMedia->wanted(Size::Small, origin);
}
if (const auto small = _photoMedia->image(PhotoSize::Small)) {
prepare(small, Images::Option(0));
} else if (const auto large = _photoMedia->image(
PhotoSize::Large)) {
prepare(large, Images::Option(0));
} else if (!_image) {
if (spoiler) {
if (const auto blurred = _photoMedia->thumbnailInline()) {
prepare(blurred, {}, true);
} else if (const auto small = _photoMedia->image(Size::Small)) {
prepare(small, {}, true);
} else if (const auto large = _photoMedia->image(Size::Large)) {
prepare(large, {}, true);
}
} else if (const auto small = _photoMedia->image(Size::Small)) {
prepare(small, {});
} else if (const auto large = _photoMedia->image(Size::Large)) {
prepare(large, {});
} else if (!image) {
if (const auto blurred = _photoMedia->thumbnailInline()) {
prepare(blurred, Images::Option::Blur);
}
}
if (_good) {
_checked = true;
checked = true;
_photoMedia = nullptr;
}
}
}
return _image.get();
return image.get();
}
bool ReplyPreview::loaded() const {
return _checked;
bool ReplyPreview::loaded(bool spoiler) const {
return spoiler ? _checkedSpoilered : _checkedRegular;
}
} // namespace Data

View File

@@ -25,19 +25,25 @@ public:
[[nodiscard]] Image *image(
Data::FileOrigin origin,
not_null<PeerData*> context);
[[nodiscard]] bool loaded() const;
not_null<PeerData*> context,
bool spoiler);
[[nodiscard]] bool loaded(bool spoiler) const;
private:
void prepare(not_null<Image*> image, Images::Options options);
void prepare(
not_null<Image*> image,
Images::Options options,
bool spoiler = false);
std::unique_ptr<Image> _image;
std::unique_ptr<Image> _regular;
std::unique_ptr<Image> _spoilered;
PhotoData *_photo = nullptr;
DocumentData *_document = nullptr;
std::shared_ptr<PhotoMedia> _photoMedia;
std::shared_ptr<DocumentMedia> _documentMedia;
bool _good = false;
bool _checked = false;
bool _checkedRegular = false;
bool _checkedSpoilered = false;
};

View File

@@ -144,6 +144,8 @@ WebPageType ParseWebPageType(
return WebPageType::Video;
} else if (type == u"photo"_q) {
return WebPageType::Photo;
} else if (type == u"document"_q) {
return WebPageType::Document;
} else if (type == u"profile"_q) {
return WebPageType::Profile;
} else if (type == u"telegram_background"_q) {

View File

@@ -26,6 +26,7 @@ enum class WebPageType {
Photo,
Video,
Document,
User,
Bot,

View File

@@ -926,7 +926,8 @@ void Stickers::specialSetReceived(
LOG(("API Error: "
"received recent attached stickers hash %1 "
"while counted hash is %2"
).arg(hash, counted));
).arg(hash
).arg(counted));
}
session().local().writeRecentMasks();
} break;

View File

@@ -2851,7 +2851,6 @@ void InnerWidget::searchInChat(Key key, PeerData *from) {
_searchInChat = key;
_searchFromPeer = from;
if (_searchInChat) {
_controller->closeFolder();
onHashtagFilterUpdate(QStringView());
_cancelSearchInChat->show();
} else {
@@ -3739,6 +3738,9 @@ void InnerWidget::setupShortcuts() {
Command::ChatPinned3,
Command::ChatPinned4,
Command::ChatPinned5,
Command::ChatPinned6,
Command::ChatPinned7,
Command::ChatPinned8,
};
auto &&pinned = ranges::views::zip(
kPinned,
@@ -3791,14 +3793,14 @@ void InnerWidget::setupShortcuts() {
});
request->check(Command::ReadChat) && request->handle([=] {
const auto history = _selected ? _selected->history() : nullptr;
if (history) {
if (history->chatListBadgesState().unread) {
session().data().histories().readInbox(history);
}
return true;
const auto thread = _selected ? _selected->thread() : nullptr;
if (!thread) {
return false;
}
return (history != nullptr);
if (Window::IsUnreadThread(thread)) {
Window::MarkAsReadThread(thread);
}
return true;
});
request->check(Command::ShowContacts) && request->handle([=] {

View File

@@ -94,16 +94,18 @@ constexpr auto kNoneLayer = 0;
constexpr auto kAngleStart = 90 * 16;
constexpr auto kAngleSpan = 180 * 16;
const auto penWidth = style::ConvertScaleExact(kPenWidth);
auto pen = QPen(st::premiumButtonFg);
pen.setJoinStyle(Qt::RoundJoin);
pen.setCapStyle(Qt::RoundCap);
pen.setWidthF(style::ConvertScaleExact(kPenWidth));
pen.setWidthF(penWidth);
q.setPen(pen);
q.setBrush(Qt::NoBrush);
q.drawArc(innerRect, kAngleStart, kAngleSpan);
q.setClipRect(innerRect - QMargins(innerRect.width() / 2, 0, 0, 0));
q.setClipRect(innerRect
- QMargins(innerRect.width() / 2, 0, -penWidth, -penWidth));
pen.setStyle(Qt::DotLine);
q.setPen(pen);
q.drawEllipse(innerRect);
@@ -252,6 +254,10 @@ Row::Row(Key key, int index, int top) : _id(key), _top(top), _index(index) {
}
}
Row::~Row() {
clearTopicJumpRipple();
}
void Row::recountHeight(float64 narrowRatio) {
if (const auto history = _id.history()) {
_height = history->isForum()
@@ -487,6 +493,11 @@ void Row::stopLastRipple() {
}
}
void Row::clearRipple() {
BasicRow::clearRipple();
clearTopicJumpRipple();
}
void Row::addTopicJumpRipple(
QPoint origin,
not_null<Ui::TopicJumpCache*> topicJumpCache,
@@ -503,10 +514,15 @@ void Row::addTopicJumpRipple(
}
void Row::clearTopicJumpRipple() {
if (_topicJumpRipple) {
clearRipple();
_topicJumpRipple = 0;
if (!_topicJumpRipple) {
return;
}
const auto history = this->history();
const auto view = history ? &history->lastItemDialogsView() : nullptr;
if (view) {
view->clearRipple();
}
_topicJumpRipple = 0;
}
bool Row::topicJumpRipple() const {

View File

@@ -53,11 +53,11 @@ public:
void addRipple(QPoint origin, QSize size, Fn<void()> updateCallback);
virtual void stopLastRipple();
virtual void clearRipple();
void addRippleWithMask(
QPoint origin,
QImage mask,
Fn<void()> updateCallback);
void clearRipple();
void paintRipple(
QPainter &p,
@@ -82,6 +82,7 @@ public:
explicit Row(std::nullptr_t) {
}
Row(Key key, int index, int top);
~Row();
[[nodiscard]] int top() const {
return _top;
@@ -105,6 +106,7 @@ public:
[[nodiscard]] bool lookupIsInTopicJump(int x, int y) const;
void stopLastRipple() override;
void clearRipple() override;
void addTopicJumpRipple(
QPoint origin,
not_null<Ui::TopicJumpCache*> topicJumpCache,

View File

@@ -188,7 +188,12 @@ Widget::Widget(
+ st::defaultDialogRow.photoSize
+ st::defaultDialogRow.padding.left())
, _searchControls(this)
, _mainMenuToggle(_searchControls, st::dialogsMenuToggle)
, _mainMenu({
.toggle = object_ptr<Ui::IconButton>(
_searchControls,
st::dialogsMenuToggle),
.under = object_ptr<Ui::AbstractButton>(_searchControls),
})
, _searchForNarrowFilters(_searchControls, st::dialogsSearchForNarrowFilters)
, _filter(_searchControls, st::dialogsFilter, tr::lng_dlg_filter())
, _chooseFromUser(
@@ -427,6 +432,14 @@ Widget::Widget(
updateControlsGeometry();
}, lifetime());
_childListShown.changes(
) | rpl::filter((rpl::mappers::_1 == 0.) || (rpl::mappers::_1 == 1.)
) | rpl::start_with_next([=](float64 shown) {
const auto color = (shown > 0.) ? &st::dialogsRippleBg : nullptr;
_mainMenu.toggle->setRippleColorOverride(color);
_searchForNarrowFilters->setRippleColorOverride(color);
}, lifetime());
setupDownloadBar();
}
}
@@ -662,13 +675,18 @@ void Widget::setupSupportMode() {
}
void Widget::setupMainMenuToggle() {
_mainMenuToggle->setClickedCallback([=] { showMainMenu(); });
_mainMenu.under->setClickedCallback([=] {
_mainMenu.toggle->clicked({}, Qt::LeftButton);
});
_mainMenu.under->stackUnder(_mainMenu.toggle);
_mainMenu.toggle->setClickedCallback([=] { showMainMenu(); });
rpl::single(rpl::empty) | rpl::then(
controller()->filtersMenuChanged()
) | rpl::start_with_next([=] {
const auto filtersHidden = !controller()->filtersWidth();
_mainMenuToggle->setVisible(filtersHidden);
_mainMenu.toggle->setVisible(filtersHidden);
_mainMenu.under->setVisible(filtersHidden);
_searchForNarrowFilters->setVisible(!filtersHidden);
updateControlsGeometry();
}, lifetime());
@@ -680,8 +698,8 @@ void Widget::setupMainMenuToggle() {
: !state.allMuted
? &st::dialogsMenuToggleUnread
: &st::dialogsMenuToggleUnreadMuted;
_mainMenuToggle->setIconOverride(icon, icon);
}, _mainMenuToggle->lifetime());
_mainMenu.toggle->setIconOverride(icon, icon);
}, _mainMenu.toggle->lifetime());
}
void Widget::setupShortcuts() {
@@ -2160,15 +2178,13 @@ bool Widget::setSearchInChat(Key chat, PeerData *from) {
}
}
_searchInMigrated = nullptr;
if (peer) {
if (peer && !forum) {
if (_layout != Layout::Main) {
return false;
} else if (const auto migrateTo = peer->migrateTo()) {
return setSearchInChat(peer->owner().history(migrateTo), from);
} else if (const auto migrateFrom = peer->migrateFrom()) {
if (!forum) {
_searchInMigrated = peer->owner().history(migrateFrom);
}
_searchInMigrated = peer->owner().history(migrateFrom);
}
}
if (searchInPeerUpdated) {
@@ -2180,6 +2196,9 @@ bool Widget::setSearchInChat(Key chat, PeerData *from) {
updateSearchFromVisibility();
clearSearchCache();
}
if (_searchInChat && _layout == Layout::Main) {
controller()->closeFolder();
}
_inner->searchInChat(_searchInChat, _searchFromAuthor);
if (_subsectionTopBar) {
_subsectionTopBar->searchEnableJumpToDate(
@@ -2368,7 +2387,7 @@ void Widget::updateControlsGeometry() {
auto filterLeft = (controller()->filtersWidth()
? st::dialogsFilterSkip
: (st::dialogsFilterPadding.x() + _mainMenuToggle->width()))
: (st::dialogsFilterPadding.x() + _mainMenu.toggle->width()))
+ st::dialogsFilterPadding.x();
auto filterRight = (session().domain().local().hasLocalPasscode()
? (st::dialogsFilterPadding.x() + _lockUnlock->width())
@@ -2388,9 +2407,16 @@ void Widget::updateControlsGeometry() {
_filter->setGeometryToLeft(filterLeft, filterTop, filterWidth, _filter->height());
auto mainMenuLeft = anim::interpolate(
st::dialogsFilterPadding.x(),
(_narrowWidth - _mainMenuToggle->width()) / 2,
(_narrowWidth - _mainMenu.toggle->width()) / 2,
narrowRatio);
_mainMenuToggle->moveToLeft(mainMenuLeft, st::dialogsFilterPadding.y());
_mainMenu.toggle->moveToLeft(mainMenuLeft, st::dialogsFilterPadding.y());
_mainMenu.under->setGeometry(
0,
0,
filterLeft,
_mainMenu.toggle->y()
+ _mainMenu.toggle->height()
+ st::dialogsFilterPadding.y());
const auto searchLeft = anim::interpolate(
-_searchForNarrowFilters->width(),
(_narrowWidth - _searchForNarrowFilters->width()) / 2,

View File

@@ -33,6 +33,7 @@ class ContactStatus;
} // namespace HistoryView
namespace Ui {
class AbstractButton;
class IconButton;
class PopupMenu;
class DropdownMenu;
@@ -222,7 +223,10 @@ private:
int _narrowWidth = 0;
object_ptr<Ui::RpWidget> _searchControls;
object_ptr<HistoryView::TopBarWidget> _subsectionTopBar = { nullptr } ;
object_ptr<Ui::IconButton> _mainMenuToggle;
struct {
object_ptr<Ui::IconButton> toggle;
object_ptr<Ui::AbstractButton> under;
} _mainMenu;
object_ptr<Ui::IconButton> _searchForNarrowFilters;
object_ptr<Ui::InputField> _filter;
object_ptr<Ui::FadeWrapScaled<Ui::IconButton>> _chooseFromUser;

View File

@@ -227,6 +227,12 @@ void MessageView::stopLastRipple() {
}
}
void MessageView::clearRipple() {
if (_topics) {
_topics->clearRipple();
}
}
int MessageView::countWidth() const {
auto result = 0;
if (!_senderCache.isEmpty()) {

View File

@@ -73,6 +73,7 @@ public:
not_null<TopicJumpCache*> topicJumpCache,
Fn<void()> updateCallback);
void stopLastRipple();
void clearRipple();
private:
struct LoadingContext;

View File

@@ -187,6 +187,10 @@ void TopicsView::stopLastRipple() {
}
}
void TopicsView::clearRipple() {
_ripple = nullptr;
}
void TopicsView::paintRipple(
QPainter &p,
int x,

View File

@@ -89,8 +89,8 @@ public:
int y,
int outerWidth,
const QColor *colorOverride) const;
void clearRipple();
void stopLastRipple();
void clearRipple();
[[nodiscard]] rpl::lifetime &lifetime() {
return _lifetime;

View File

@@ -54,8 +54,8 @@ photoEditorRotateButton: IconButton(defaultIconButton) {
width: photoEditorButtonBarHeight;
height: photoEditorButtonBarHeight;
icon: icon {{ "photo_editor/rotate", photoEditorButtonIconFg }};
iconOver: icon {{ "photo_editor/rotate", photoEditorButtonIconFgOver }};
icon: icon {{ "photo_editor/rotate-flip_horizontal", photoEditorButtonIconFg }};
iconOver: icon {{ "photo_editor/rotate-flip_horizontal", photoEditorButtonIconFgOver }};
rippleAreaPosition: point(4px, 4px);
rippleAreaSize: 40px;

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/confirm_box.h" // InformBox
#include "editor/editor_layer_widget.h"
#include "editor/photo_editor.h"
#include "storage/localimageloader.h"
#include "storage/storage_media_prepare.h"
#include "ui/chat/attach/attach_prepare.h"
#include "window/window_controller.h"
@@ -42,10 +43,11 @@ void OpenWithPreparedFile(
return;
}
const auto sideLimit = PhotoSideLimit();
auto callback = [=, done = std::move(doneCallback)](
const PhotoModifications &mods) {
image->modifications = mods;
Storage::UpdateImageDetails(*file, previewWidth);
Storage::UpdateImageDetails(*file, previewWidth, sideLimit);
{
using namespace Ui;
const auto size = file->preview.size();

View File

@@ -490,8 +490,6 @@ void ApiWrap::requestSplitRanges() {
_splits.push_back(MTP_messageRange(
MTP_int(1),
MTP_int(std::numeric_limits<int>::max())));
} else {
ranges::reverse(_splits);
}
_startProcess->splitIndex = useOnlyLastSplit()
? (_splits.size() - 1)

View File

@@ -845,18 +845,17 @@ not_null<HistoryItem*> History::addNewToBack(
addItemToBlock(item);
if (!unread && item->isRegular()) {
if (const auto sharedMediaTypes = item->sharedMediaTypes()) {
if (const auto types = item->sharedMediaTypes()) {
auto from = loadedAtTop() ? 0 : minMsgId();
auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId();
auto &storage = session().storage();
storage.add(Storage::SharedMediaAddExisting(
peer->id,
MsgId(0), // topicRootId
sharedMediaTypes,
types,
item->id,
{ from, till }));
const auto pinned = sharedMediaTypes.test(
Storage::SharedMediaType::Pinned);
const auto pinned = types.test(Storage::SharedMediaType::Pinned);
if (pinned) {
setHasPinnedMessages(true);
}
@@ -864,7 +863,7 @@ not_null<HistoryItem*> History::addNewToBack(
storage.add(Storage::SharedMediaAddExisting(
peer->id,
topic->rootId(),
sharedMediaTypes,
types,
item->id,
{ item->id, item->id}));
if (pinned) {

View File

@@ -1395,6 +1395,7 @@ void HistoryItem::applyEdition(HistoryMessageEdition &&edition) {
setReplyMarkup(base::take(edition.replyMarkup));
}
if (!isLocalUpdateMedia()) {
removeFromSharedMediaIndex();
refreshMedia(edition.mtpMedia);
}
if (!edition.useSameReactions) {
@@ -1405,6 +1406,9 @@ void HistoryItem::applyEdition(HistoryMessageEdition &&edition) {
setText(_media
? edition.textWithEntities
: EnsureNonEmpty(edition.textWithEntities));
if (!isLocalUpdateMedia()) {
indexAsNewItem();
}
if (!edition.useSameReplies) {
if (!edition.replies.isNull) {
if (checkRepliesPts(edition.replies)) {
@@ -1424,6 +1428,7 @@ void HistoryItem::applyEdition(const MTPDmessageService &message) {
if (message.vaction().type() == mtpc_messageActionHistoryClear) {
const auto wasGrouped = history()->owner().groups().isGrouped(this);
setReplyMarkup({});
removeFromSharedMediaIndex();
refreshMedia(nullptr);
setTextValue({});
changeViewsCount(-1);
@@ -1653,7 +1658,10 @@ void HistoryItem::destroyHistoryEntry() {
Storage::SharedMediaTypesMask HistoryItem::sharedMediaTypes() const {
auto result = Storage::SharedMediaTypesMask {};
if (const auto media = this->media()) {
const auto media = _savedLocalEditMediaData
? _savedLocalEditMediaData->media.get()
: _media.get();
if (media) {
result.set(media->sharedMediaTypes());
}
if (hasTextLinks()) {
@@ -1684,6 +1692,18 @@ void HistoryItem::indexAsNewItem() {
}
}
void HistoryItem::removeFromSharedMediaIndex() {
if (isRegular()) {
if (const auto types = sharedMediaTypes()) {
_history->session().storage().remove(
Storage::SharedMediaRemoveOne(
_history->peer->id,
types,
id));
}
}
}
void HistoryItem::incrementReplyToTopCounter() {
if (const auto reply = Get<HistoryMessageReply>()) {
changeReplyToTopCounter(reply, 1);
@@ -3676,18 +3696,22 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
const auto duration = (period == 5)
? u"5 seconds"_q
: Ui::FormatTTL(period);
if (const auto from = action.vauto_setting_from()) {
if (const auto from = action.vauto_setting_from(); from && period) {
if (const auto peer = _from->owner().peer(peerFromUser(*from))) {
if (!peer->isSelf() && period) {
result.text = tr::lng_action_ttl_global(
result.text = (peer->id == peer->session().userPeerId())
? tr::lng_action_ttl_global_me(
tr::now,
lt_duration,
{ .text = duration },
Ui::Text::WithEntities)
: tr::lng_action_ttl_global(
tr::now,
lt_from,
fromLinkText(), // Link 1.
Ui::Text::Link(peer->name(), 1), // Link 1.
lt_duration,
{ .text = duration },
Ui::Text::WithEntities);
return result;
}
return result;
}
}
if (isPost()) {

View File

@@ -346,6 +346,7 @@ public:
[[nodiscard]] Storage::SharedMediaTypesMask sharedMediaTypes() const;
void indexAsNewItem();
void removeFromSharedMediaIndex();
[[nodiscard]] QString notificationHeader() const;
[[nodiscard]] TextWithEntities notificationText() const;

View File

@@ -47,7 +47,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
const auto kPsaForwardedPrefix = "cloud_lng_forwarded_psa_";
constexpr auto kReplyBarAlpha = 230. / 255.;
} // namespace
@@ -283,9 +282,10 @@ bool HistoryMessageReply::updateData(
}
if (replyToMsg) {
const auto repaint = [=] { holder->customEmojiRepaint(); };
const auto context = Core::MarkedTextContext{
.session = &holder->history()->session(),
.customEmojiRepaint = [=] { holder->customEmojiRepaint(); },
.customEmojiRepaint = repaint,
};
replyToText.setMarkedText(
st::messageTextStyle,
@@ -312,9 +312,17 @@ bool HistoryMessageReply::updateData(
? replyToMsg->from()->id
: PeerId(0);
}
const auto media = replyToMsg->media();
if (!media || !media->hasReplyPreview() || !media->hasSpoiler()) {
spoiler = nullptr;
} else if (!spoiler) {
spoiler = std::make_unique<Ui::SpoilerAnimation>(repaint);
}
} else if (force) {
replyToMsgId = 0;
replyToColorKey = PeerId(0);
spoiler = nullptr;
}
if (force) {
holder->history()->owner().requestItemResize(holder);
@@ -456,21 +464,22 @@ void HistoryMessageReply::paint(
st::msgReplyBarSize.height(),
w + 2 * x);
const auto opacity = p.opacity();
p.setOpacity(opacity * kReplyBarAlpha);
p.setOpacity(opacity * kBarAlpha);
p.fillRect(rbar, bar);
p.setOpacity(opacity);
}
if (w > st::msgReplyBarSkip) {
if (replyToMsg) {
auto hasPreview = replyToMsg->media() ? replyToMsg->media()->hasReplyPreview() : false;
const auto media = replyToMsg->media();
auto hasPreview = media && media->hasReplyPreview();
if (hasPreview && w < st::msgReplyBarSkip + st::msgReplyBarSize.height()) {
hasPreview = false;
}
auto previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
if (hasPreview) {
if (const auto image = replyToMsg->media()->replyPreview()) {
if (const auto image = media->replyPreview()) {
auto to = style::rtlrect(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height(), w + 2 * x);
const auto preview = image->pixSingle(
image->size() / style::DevicePixelRatio(),
@@ -482,6 +491,16 @@ void HistoryMessageReply::paint(
.outer = to.size(),
});
p.drawPixmap(to.x(), to.y(), preview);
if (spoiler) {
holder->clearCustomEmojiRepaint();
Ui::FillSpoilerRect(
p,
to,
Ui::DefaultImageSpoiler().frame(
spoiler->index(
context.now,
context.paused)));
}
}
}
if (w > st::msgReplyBarSkip + previewSkip) {

View File

@@ -195,6 +195,8 @@ struct HistoryMessageReply
Expects(replyToVia == nullptr);
}
static constexpr auto kBarAlpha = 230. / 255.;
bool updateData(not_null<HistoryItem*> holder, bool force = false);
// Must be called before destructor.
@@ -246,6 +248,7 @@ struct HistoryMessageReply
WebPageId replyToWebPageId = 0;
ReplyToMessagePointer replyToMsg;
std::unique_ptr<HistoryMessageVia> replyToVia;
std::unique_ptr<Ui::SpoilerAnimation> spoiler;
ClickHandlerPtr replyToLnk;
mutable Ui::Text::String replyToName, replyToText;
mutable int replyToVersion = 0;

View File

@@ -24,6 +24,10 @@ public:
return QString();
}
QString url() const override {
return _text;
}
QString dragText() const override {
return _text;
}

View File

@@ -126,7 +126,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/player/media_player_instance.h"
#include "core/application.h"
#include "apiwrap.h"
#include "base/options.h"
#include "base/qthelp_regex.h"
#include "ui/boxes/report_box.h"
#include "ui/chat/pinned_bar.h"
@@ -171,13 +170,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QWindow>
#include <QtCore/QMimeData>
const char kOptionAutoScrollInactiveChat[] =
"auto-scroll-inactive-chat";
namespace {
constexpr auto kMessagesPerPageFirst = 10;
constexpr auto kMessagesPerPage = 10;
constexpr auto kMessagesPerPageFirst = 30;
constexpr auto kMessagesPerPage = 50;
constexpr auto kPreloadHeightsCount = 3; // when 3 screens to scroll left make a preload request
constexpr auto kScrollToVoiceAfterScrolledMs = 1000;
constexpr auto kSkipRepaintWhileScrollMs = 100;
@@ -194,13 +190,6 @@ constexpr auto kCommonModifiers = 0
| Qt::ControlModifier;
const auto kPsaAboutPrefix = "cloud_lng_about_psa_";
base::options::toggle AutoScrollInactiveChat({
.id = kOptionAutoScrollInactiveChat,
.name = "Enable auto-scroll of inactive chat",
.description = "Enable auto-scrolling chat for new messages, "
"even when the window is not in focus.",
});
[[nodiscard]] rpl::producer<PeerData*> ActivePeerValue(
not_null<Window::SessionController*> controller) {
return controller->activeChatValue(
@@ -1971,7 +1960,9 @@ void HistoryWidget::applyCloudDraft(History *history) {
}
bool HistoryWidget::insideJumpToEndInsteadOfToUnread() const {
if (session().supportMode()) {
Expects(_history != nullptr);
if (session().supportMode() || !_history->trackUnreadMessages()) {
return true;
} else if (!_historyInited) {
return false;
@@ -1979,6 +1970,14 @@ bool HistoryWidget::insideJumpToEndInsteadOfToUnread() const {
_history->calculateFirstUnreadMessage();
const auto unread = _history->firstUnreadMessage();
const auto visibleBottom = _scroll->scrollTop() + _scroll->height();
DEBUG_LOG(("JumpToEnd(%1, %2, %3): "
"unread: %4, top: %5, visibleBottom: %6."
).arg(_history->peer->name()
).arg(_history->inboxReadTillId().bare
).arg(Logs::b(_history->loadedAtBottom())
).arg(unread ? unread->data()->id.bare : 0
).arg(unread ? _list->itemTop(unread) : -1
).arg(visibleBottom));
return unread && _list->itemTop(unread) <= visibleBottom;
}
@@ -2004,6 +2003,11 @@ void HistoryWidget::showHistory(
if (showAtMsgId == ShowAtUnreadMsgId
&& insideJumpToEndInsteadOfToUnread()) {
DEBUG_LOG(("JumpToEnd(%1, %2, %3): "
"Jumping to end instead of unread."
).arg(_history->peer->name()
).arg(_history->inboxReadTillId().bare
).arg(Logs::b(_history->loadedAtBottom())));
showAtMsgId = ShowAtTheEndMsgId;
} else if (showAtMsgId == ShowForChooseMessagesMsgId) {
if (_chooseForReport) {
@@ -2023,6 +2027,11 @@ void HistoryWidget::showHistory(
}
const auto canShowNow = _history->isReadyFor(showAtMsgId);
if (!canShowNow) {
DEBUG_LOG(("JumpToEnd(%1, %2, %3): Showing delayed at %4."
).arg(_history->peer->name()
).arg(_history->inboxReadTillId().bare
).arg(Logs::b(_history->loadedAtBottom())
).arg(showAtMsgId.bare));
delayedShowAt(showAtMsgId);
} else {
_history->forgetScrollState();
@@ -2042,6 +2051,13 @@ void HistoryWidget::showHistory(
setMsgId(showAtMsgId);
if (_historyInited) {
DEBUG_LOG(("JumpToEnd(%1, %2, %3): "
"Showing instant at %4."
).arg(_history->peer->name()
).arg(_history->inboxReadTillId().bare
).arg(Logs::b(_history->loadedAtBottom())
).arg(showAtMsgId.bare));
const auto to = countInitialScrollTop();
const auto item = getItemFromHistoryOrMigrated(
_showAtMsgId);
@@ -2179,6 +2195,10 @@ void HistoryWidget::showHistory(
} else {
_chooseForReport = nullptr;
}
if (_showAtMsgId == ShowAtUnreadMsgId
&& !_history->trackUnreadMessages()) {
_showAtMsgId = ShowAtTheEndMsgId;
}
refreshTopBarActiveChat();
updateTopBarSelection();
@@ -2970,7 +2990,9 @@ void HistoryWidget::unreadCountUpdated() {
}
});
} else {
_cornerButtons.updateJumpDownVisibility(_history->isForum()
const auto hideCounter = _history->isForum()
|| !_history->trackUnreadMessages();
_cornerButtons.updateJumpDownVisibility(hideCounter
? 0
: _history->chatListBadgesState().unreadCounter);
}
@@ -3266,6 +3288,12 @@ void HistoryWidget::loadMessages() {
const auto minId = 0;
const auto historyHash = uint64(0);
DEBUG_LOG(("JumpToEnd(%1, %2, %3): Loading up before %4."
).arg(_history->peer->name()
).arg(_history->inboxReadTillId().bare
).arg(Logs::b(_history->loadedAtBottom())
).arg(offsetId.bare));
const auto history = from;
const auto type = Data::Histories::RequestType::History;
auto &histories = history->owner().histories();
@@ -3318,6 +3346,12 @@ void HistoryWidget::loadMessagesDown() {
const auto minId = 0;
const auto historyHash = uint64(0);
DEBUG_LOG(("JumpToEnd(%1, %2, %3): Loading down after %4."
).arg(_history->peer->name()
).arg(_history->inboxReadTillId().bare
).arg(Logs::b(_history->loadedAtBottom())
).arg(offsetId.bare));
const auto history = from;
const auto type = Data::Histories::RequestType::History;
auto &histories = history->owner().histories();
@@ -3350,6 +3384,12 @@ void HistoryWidget::delayedShowAt(MsgId showAtMsgId) {
clearAllLoadRequests();
_delayedShowAtMsgId = showAtMsgId;
DEBUG_LOG(("JumpToEnd(%1, %2, %3): Loading delayed around %4."
).arg(_history->peer->name()
).arg(_history->inboxReadTillId().bare
).arg(Logs::b(_history->loadedAtBottom())
).arg(showAtMsgId.bare));
auto from = _history;
auto offsetId = MsgId();
auto offset = 0;
@@ -4028,6 +4068,10 @@ void HistoryWidget::doneShow() {
void HistoryWidget::cornerButtonsShowAtPosition(
Data::MessagePosition position) {
if (position == Data::UnreadMessagePosition) {
DEBUG_LOG(("JumpToEnd(%1, %2, %3): Show at unread requested."
).arg(_history->peer->name()
).arg(_history->inboxReadTillId().bare
).arg(Logs::b(_history->loadedAtBottom())));
showHistory(_peer->id, ShowAtUnreadMsgId);
} else if (_peer && position.fullId.peer == _peer->id) {
showHistory(_peer->id, position.fullId.msg);
@@ -5413,9 +5457,6 @@ int HistoryWidget::countAutomaticScrollTop() {
Expects(_list != nullptr);
if (const auto unread = _history->firstUnreadMessage()) {
if (AutoScrollInactiveChat.value()) {
return ScrollMax;
}
const auto firstUnreadTop = _list->itemTop(unread);
const auto possibleUnreadBarTop = _scroll->scrollTopMax()
+ HistoryView::UnreadBar::height()
@@ -5866,7 +5907,10 @@ std::optional<bool> HistoryWidget::cornerButtonsDownShown() {
}
const auto haveUnreadBelowBottom = [&](History *history) {
if (!_list || !history || history->unreadCount() <= 0) {
if (!_list
|| !history
|| history->unreadCount() <= 0
|| !history->trackUnreadMessages()) {
return false;
}
const auto unread = history->firstUnreadMessage();
@@ -6224,7 +6268,7 @@ void HistoryWidget::checkPinnedBarState() {
_pinnedBar = std::make_unique<Ui::PinnedBar>(this, [=] {
return controller()->isGifPausedAtLeastFor(
Window::GifPauseReason::Any);
});
}, controller()->gifPauseLevelChanged());
auto pinnedRefreshed = Info::Profile::SharedMediaCountValue(
_peer,
MsgId(0), // topicRootId
@@ -7490,20 +7534,44 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) {
p.setInactive(
controller()->isGifPausedAtLeastFor(Window::GifPauseReason::Any));
p.fillRect(myrtlrect(0, backy, width(), backh), st::historyReplyBg);
const auto media = (!drawWebPagePreview && drawMsgText)
? drawMsgText->media()
: nullptr;
const auto hasPreview = media && media->hasReplyPreview();
const auto preview = hasPreview ? media->replyPreview() : nullptr;
const auto spoilered = preview && media->hasSpoiler();
if (!spoilered) {
_replySpoiler = nullptr;
} else if (!_replySpoiler) {
_replySpoiler = std::make_unique<Ui::SpoilerAnimation>([=] {
updateField();
});
}
if (_editMsgId || _replyToId || (!hasForward && _kbReplyTo)) {
const auto now = crl::now();
const auto paused = p.inactive();
auto replyLeft = st::historyReplySkip;
(_editMsgId ? st::historyEditIcon : st::historyReplyIcon).paint(p, st::historyReplyIconPosition + QPoint(0, backy), width());
if (!drawWebPagePreview) {
if (drawMsgText) {
if (drawMsgText->media() && drawMsgText->media()->hasReplyPreview()) {
if (const auto image = drawMsgText->media()->replyPreview()) {
if (hasPreview) {
if (preview) {
auto to = QRect(replyLeft, backy + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
p.drawPixmap(to.x(), to.y(), image->pixSingle(
image->size() / style::DevicePixelRatio(),
p.drawPixmap(to.x(), to.y(), preview->pixSingle(
preview->size() / style::DevicePixelRatio(),
{
.options = Images::Option::RoundSmall,
.outer = to.size(),
}));
if (_replySpoiler) {
Ui::FillSpoilerRect(
p,
to,
Ui::DefaultImageSpoiler().frame(
_replySpoiler->index(now, paused)));
}
}
replyLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x();
}
@@ -7521,8 +7589,8 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) {
.availableWidth = width() - replyLeft - _fieldBarCancel->width() - st::msgReplyPadding.right(),
.palette = &st::historyComposeAreaPalette,
.spoiler = Ui::Text::DefaultSpoilerCache(),
.now = crl::now(),
.paused = p.inactive(),
.now = now,
.paused = paused,
.elisionLines = 1,
});
} else {

View File

@@ -70,6 +70,7 @@ class RequestsBar;
struct PreparedList;
class SendFilesWay;
class SendAsButton;
class SpoilerAnimation;
enum class ReportReason;
class ChooseThemeController;
class ContinuousScroll;
@@ -102,8 +103,6 @@ class TTLButton;
class BotKeyboard;
class HistoryInner;
extern const char kOptionAutoScrollInactiveChat[];
class HistoryWidget final
: public Window::AbstractSectionWidget
, private HistoryView::CornerButtonsDelegate {
@@ -626,6 +625,7 @@ private:
HistoryItem *_replyEditMsg = nullptr;
Ui::Text::String _replyEditMsgText;
std::unique_ptr<Ui::SpoilerAnimation> _replySpoiler;
mutable base::Timer _updateEditTimeLeftDisplay;
object_ptr<Ui::IconButton> _fieldBarCancel;

View File

@@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat.h"
#include "data/data_channel.h"
#include "data/data_forum_topic.h"
#include "data/data_peer_values.h"
#include "data/stickers/data_stickers.h"
#include "data/stickers/data_custom_emoji.h"
#include "data/data_web_page.h"
@@ -987,12 +988,12 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) {
updateControlsGeometry(_wrap->size());
updateControlsVisibility();
updateFieldPlaceholder();
updateSendAsButton();
updateAttachBotsMenu();
//if (!_history) {
// return;
//}
const auto peer = _history->peer;
initSendAsButton(peer);
if (peer->isChat() && peer->asChat()->noParticipantInfo()) {
session().api().requestFullPeer(peer);
} else if (const auto channel = peer->asMegagroup()) {
@@ -1342,7 +1343,6 @@ void ComposeControls::init() {
initField();
initTabbedSelector();
initSendButton();
initSendAsButton();
initWriteRestriction();
initVoiceRecordBar();
initKeyHandler();
@@ -2058,17 +2058,24 @@ void ComposeControls::initSendButton() {
SendMenu::DefaultScheduleCallback(_wrap.get(), sendMenuType(), send));
}
void ComposeControls::initSendAsButton() {
session().sendAsPeers().updated(
) | rpl::filter([=](not_null<PeerData*> peer) {
return _history && (peer == _history->peer);
}) | rpl::start_with_next([=] {
void ComposeControls::initSendAsButton(not_null<PeerData*> peer) {
using namespace rpl::mappers;
// SendAsPeers::shouldChoose checks PeerData::canWrite(false).
rpl::combine(
rpl::single(peer) | rpl::then(
session().sendAsPeers().updated() | rpl::filter(_1 == peer)
),
Data::CanWriteValue(peer, false)
) | rpl::skip(1) | rpl::start_with_next([=] {
if (updateSendAsButton()) {
updateControlsVisibility();
updateControlsGeometry(_wrap->size());
orderControls();
}
}, _wrap->lifetime());
updateSendAsButton();
}
void ComposeControls::cancelInlineBot() {

View File

@@ -210,7 +210,7 @@ private:
void initField();
void initTabbedSelector();
void initSendButton();
void initSendAsButton();
void initSendAsButton(not_null<PeerData*> peer);
void initWebpageProcess();
void initForwardProcess();
void initWriteRestriction();

View File

@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_forum_topic.h"
#include "main/main_session.h"
#include "ui/chat/forward_options_box.h"
#include "ui/effects/spoiler_mess.h"
#include "ui/text/text_options.h"
#include "ui/text/text_utilities.h"
#include "ui/painter.h"
@@ -306,33 +307,35 @@ void ForwardPanel::paint(
return;
}
const_cast<ForwardPanel*>(this)->checkTexts();
const auto now = crl::now();
const auto paused = p.inactive();
const auto firstItem = _data.items.front();
const auto firstMedia = firstItem->media();
const auto hasPreview = (_data.items.size() < 2)
&& firstMedia
&& firstMedia->hasReplyPreview();
const auto preview = hasPreview ? firstMedia->replyPreview() : nullptr;
const auto spoiler = preview && firstMedia->hasSpoiler();
if (!spoiler) {
_spoiler = nullptr;
} else if (!_spoiler) {
_spoiler = std::make_unique<Ui::SpoilerAnimation>(_repaint);
}
if (preview) {
auto to = QRect(
x,
y + st::msgReplyPadding.top(),
st::msgReplyBarSize.height(),
st::msgReplyBarSize.height());
if (preview->width() == preview->height()) {
p.drawPixmap(to.x(), to.y(), preview->pix());
} else {
auto from = (preview->width() > preview->height())
? QRect(
(preview->width() - preview->height()) / 2,
0,
preview->height(),
preview->height())
: QRect(
0,
(preview->height() - preview->width()) / 2,
preview->width(),
preview->width());
p.drawPixmap(to, preview->pix(), from);
p.drawPixmap(to.x(), to.y(), preview->pixSingle(
preview->size() / style::DevicePixelRatio(),
{
.options = Images::Option::RoundSmall,
.outer = to.size(),
}));
if (_spoiler) {
Ui::FillSpoilerRect(p, to, Ui::DefaultImageSpoiler().frame(
_spoiler->index(now, paused)));
}
const auto skip = st::msgReplyBarSize.height()
+ st::msgReplyBarSkip
@@ -355,8 +358,8 @@ void ForwardPanel::paint(
.availableWidth = available,
.palette = &st::historyComposeAreaPalette,
.spoiler = Ui::Text::DefaultSpoilerCache(),
.now = crl::now(),
.paused = p.inactive(),
.now = now,
.paused = paused,
.elisionLines = 1,
});
}

View File

@@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class Painter;
class HistoryItem;
namespace Ui {
class SpoilerAnimation;
} // namespace Ui
namespace Data {
class Thread;
} // namespace Data
@@ -57,6 +61,7 @@ private:
rpl::event_stream<> _itemsUpdated;
Ui::Text::String _from, _text;
mutable std::unique_ptr<Ui::SpoilerAnimation> _spoiler;
int _nameVersion = 0;
};

View File

@@ -3749,6 +3749,8 @@ void ListWidget::viewReplaced(not_null<const Element*> was, Element *now) {
}
void ListWidget::itemRemoved(not_null<const HistoryItem*> item) {
saveScrollState();
if (_reactionsItem.current() == item) {
_reactionsItem = nullptr;
}

View File

@@ -38,8 +38,9 @@ namespace {
[[nodiscard]] Ui::MessageBarContent ContentWithPreview(
not_null<HistoryItem*> item,
Image *preview,
bool spoiler,
Fn<void()> repaint) {
auto result = ContentWithoutPreview(item, std::move(repaint));
auto result = ContentWithoutPreview(item, repaint);
if (!preview) {
static const auto kEmpty = [&] {
const auto size = st::historyReplyHeight * cIntRetinaFactor();
@@ -51,8 +52,10 @@ namespace {
return result;
}();
result.preview = kEmpty;
result.spoilerRepaint = nullptr;
} else {
result.preview = preview->original();
result.spoilerRepaint = spoiler ? repaint : nullptr;
}
return result;
}
@@ -90,7 +93,11 @@ namespace {
}) | rpl::then(
rpl::single(kFullLoaded)
) | rpl::map([=] {
return ContentWithPreview(item, media->replyPreview(), repaint);
return ContentWithPreview(
item,
media->replyPreview(),
media->hasSpoiler(),
repaint);
});
}) | rpl::flatten_latest();
}

View File

@@ -442,7 +442,7 @@ void RepliesWidget::setupRootView() {
_rootView = std::make_unique<Ui::PinnedBar>(this, [=] {
return controller()->isGifPausedAtLeastFor(
Window::GifPauseReason::Any);
});
}, controller()->gifPauseLevelChanged());
_rootView->setContent(rpl::combine(
RootViewContent(
_history,
@@ -1592,7 +1592,7 @@ void RepliesWidget::checkPinnedBarState() {
_pinnedBar = std::make_unique<Ui::PinnedBar>(this, [=] {
return controller()->isGifPausedAtLeastFor(
Window::GifPauseReason::Any);
});
}, controller()->gifPauseLevelChanged());
auto pinnedRefreshed = Info::Profile::SharedMediaCountValue(
_history->peer,
_rootId,

View File

@@ -71,8 +71,8 @@ bool DrawWebPageDataPreview(
}
const auto preview = photo
? photo->getReplyPreview(Data::FileOrigin(), context)
: document->getReplyPreview(Data::FileOrigin(), context);
? photo->getReplyPreview(Data::FileOrigin(), context, false)
: document->getReplyPreview(Data::FileOrigin(), context, false);
if (preview) {
const auto w = preview->width();
const auto h = preview->height();

View File

@@ -128,7 +128,7 @@ void ShowSetToast(
lt_link,
Ui::Text::Link(
tr::lng_profile_changed_photo_link(tr::now),
u"tg://settings/information"_q),
u"tg://settings/edit_profile"_q),
Ui::Text::WithEntities)
);
auto st = std::make_shared<style::Toast>(st::historyPremiumToast);

View File

@@ -166,6 +166,7 @@ QSize WebPage::countOptimalSize() {
} else if (!_data->document
&& _data->photo
&& _data->type != WebPageType::Photo
&& _data->type != WebPageType::Document
&& _data->type != WebPageType::Video) {
if (_data->type == WebPageType::Profile) {
_asArticle = true;
@@ -755,6 +756,7 @@ ClickHandlerPtr WebPage::replaceAttachLink(
|| _data->type == WebPageType::Video) {
return _openl;
} else if (_data->type == WebPageType::Photo
|| _data->type == WebPageType::Document
|| _data->siteName == u"Twitter"_q
|| _data->siteName == u"Facebook"_q) {
// leave photo link

View File

@@ -412,14 +412,14 @@ infoLabeledOneLine: FlatLabel(defaultFlatLabel) {
margin: margins(5px, 5px, 5px, 5px);
}
infoLabelSkip: 2px;
infoLabel: FlatLabel(infoLabeledOneLine) {
textFg: windowSubTextFg;
}
infoLabeled: FlatLabel(infoLabeledOneLine) {
minWidth: 180px;
maxHeight: 0px;
margin: margins(5px, 5px, 5px, 5px);
}
infoLabel: FlatLabel(infoLabeled) {
textFg: windowSubTextFg;
}
infoBlockHeaderLabel: FlatLabel(infoProfileStatus) {
textFg: windowBoldFg;

View File

@@ -497,7 +497,7 @@ void AttachWebView::request(const WebViewButton &button) {
data.vquery_id().v,
qs(data.vurl()),
button.text,
button.fromMenu);
button.fromMenu || button.url.isEmpty());
});
}).fail([=](const MTP::Error &error) {
_requestId = 0;
@@ -742,7 +742,7 @@ void AttachWebView::requestMenu(
_action->history->peer->input,
_bot->inputUser,
MTP_string(url),
MTPstring(), // url
MTPstring(), // start_param
MTP_dataJSON(MTP_bytes(Window::Theme::WebViewParams().json)),
MTP_string("tdesktop"),
MTP_int(_action->replyTo.bare),
@@ -800,7 +800,7 @@ void AttachWebView::show(
uint64 queryId,
const QString &url,
const QString &buttonText,
bool fromMenu) {
bool allowClipboardRead) {
Expects(_bot != nullptr && _action.has_value());
const auto close = crl::guard(this, [=] {
@@ -924,7 +924,7 @@ void AttachWebView::show(
.menuButtons = buttons,
.handleMenuButton = handleMenuButton,
.themeParams = [] { return Window::Theme::WebViewParams(); },
.allowClipboardRead = fromMenu,
.allowClipboardRead = allowClipboardRead,
});
*panel = _panel.get();
started(queryId);

View File

@@ -135,7 +135,7 @@ private:
uint64 queryId,
const QString &url,
const QString &buttonText = QString(),
bool fromMenu = false);
bool allowClipboardRead = false);
void confirmAddToMenu(
AttachWebViewBot bot,
Fn<void()> callback = nullptr);

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "inline_bots/inline_bot_layout_item.h"
#include "base/never_freed_pointer.h"
#include "data/data_photo.h"
#include "data/data_document.h"
#include "data/data_peer.h"
@@ -24,7 +25,7 @@ namespace InlineBots {
namespace Layout {
namespace {
NeverFreedPointer<DocumentItems> documentItemsMap;
base::NeverFreedPointer<DocumentItems> documentItemsMap;
} // namespace

View File

@@ -100,10 +100,13 @@ CodeWidget::CodeWidget(
_code->setDigitsCountMax(getData()->codeLength);
setTitleText(getData()->codeByFragmentUrl.isEmpty()
? rpl::single(Ui::FormatPhone(getData()->phone))
: tr::lng_intro_fragment_title());
updateDescText();
setTitleText(_isFragment.value(
) | rpl::map([=](bool isFragment) {
return !isFragment
? rpl::single(Ui::FormatPhone(getData()->phone))
: tr::lng_intro_fragment_title();
}) | rpl::flatten_latest());
account->setHandleLoginCode([=](const QString &code) {
_code->setText(code);
@@ -126,6 +129,7 @@ int CodeWidget::errorTop() const {
void CodeWidget::updateDescText() {
const auto byTelegram = getData()->codeByTelegram;
const auto isFragment = !getData()->codeByFragmentUrl.isEmpty();
_isFragment = isFragment;
setDescriptionText(
isFragment
? tr::lng_intro_fragment_about(
@@ -136,8 +140,7 @@ void CodeWidget::updateDescText() {
Ui::Text::RichLangValue)
: (byTelegram ? tr::lng_code_from_telegram : tr::lng_code_desc)(
Ui::Text::RichLangValue));
if (isFragment) {
} else if (getData()->codeByTelegram) {
if (getData()->codeByTelegram) {
_noTelegramCode->show();
_callTimer.cancel();
} else {
@@ -420,15 +423,19 @@ void CodeWidget::submitCode() {
}
rpl::producer<QString> CodeWidget::nextButtonText() const {
return getData()->codeByFragmentUrl.isEmpty()
? Step::nextButtonText()
: tr::lng_intro_fragment_button();
return _isFragment.value(
) | rpl::map([=](bool isFragment) {
return isFragment
? tr::lng_intro_fragment_button()
: Step::nextButtonText();
}) | rpl::flatten_latest();
}
const style::RoundButton *CodeWidget::nextButtonStyle() const {
return !getData()->codeByFragmentUrl.isEmpty()
? &st::introFragmentButton
: nullptr;
rpl::producer<const style::RoundButton*> CodeWidget::nextButtonStyle() const {
return _isFragment.value(
) | rpl::map([](bool isFragment) {
return isFragment ? &st::introFragmentButton : nullptr;
});
}
void CodeWidget::noTelegramCode() {

View File

@@ -56,7 +56,7 @@ public:
void cancelled() override;
void submit() override;
rpl::producer<QString> nextButtonText() const override;
const style::RoundButton *nextButtonStyle() const override;
rpl::producer<const style::RoundButton*> nextButtonStyle() const override;
void updateDescText();
@@ -96,6 +96,8 @@ private:
QString _sentCode;
mtpRequestId _sentRequest = 0;
rpl::variable<bool> _isFragment = false;
base::Timer _callTimer;
CallStatus _callStatus = CallStatus();
int _callTimeout;

View File

@@ -119,8 +119,8 @@ rpl::producer<QString> Step::nextButtonText() const {
return tr::lng_intro_next();
}
const style::RoundButton *Step::nextButtonStyle() const {
return nullptr;
rpl::producer<const style::RoundButton*> Step::nextButtonStyle() const {
return rpl::single((const style::RoundButton*)(nullptr));
}
void Step::goBack() {

View File

@@ -81,7 +81,8 @@ public:
virtual void submit() = 0;
[[nodiscard]] virtual rpl::producer<QString> nextButtonText() const;
[[nodiscard]] virtual const style::RoundButton *nextButtonStyle() const;
[[nodiscard]] virtual auto nextButtonStyle() const
-> rpl::producer<const style::RoundButton*>;
[[nodiscard]] int contentLeft() const;
[[nodiscard]] int contentTop() const;

View File

@@ -342,17 +342,21 @@ void Widget::historyMove(StackAction action, Animate animate) {
hideAndDestroy(std::exchange(_terms, { nullptr }));
}
{
const auto st = getStep()->nextButtonStyle();
const auto nextStyle = st ? st : &st::introNextButton;
if (_nextStyle != nextStyle) {
_nextStyle = nextStyle;
_next = nullptr;
_next.create(
this,
object_ptr<Ui::RoundButton>(this, nullptr, *nextStyle));
showControls();
updateControlsGeometry();
}
getStep()->nextButtonStyle(
) | rpl::start_with_next([=](const style::RoundButton *st) {
const auto nextStyle = st ? st : &st::introNextButton;
if (_nextStyle != nextStyle) {
_nextStyle = nextStyle;
const auto wasShown = _next->toggled();
_next.destroy();
_next.create(
this,
object_ptr<Ui::RoundButton>(this, nullptr, *nextStyle));
showControls();
updateControlsGeometry();
_next->toggle(wasShown, anim::type::instant);
}
}, _next->lifetime());
}
getStep()->finishInit();

View File

@@ -66,5 +66,9 @@ inline QString EmailConfirmationExpired() {
return u"This email confirmation has expired. Please setup two-step verification once again."_q;
}
inline QString AutostartEnableError() {
return u"Could not register for autostart."_q;
}
} // namespace Hard
} // namespace Lang

View File

@@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_intro.h"
#include "platform/platform_notifications_manager.h"
#include "base/platform/base_platform_info.h"
#include "base/options.h"
#include "base/variant.h"
#include "window/notifications_manager.h"
#include "window/themes/window_theme.h"
@@ -76,8 +77,18 @@ void FeedLangTestingKey(int key) {
}
}
base::options::toggle AutoScrollInactiveChat({
.id = kOptionAutoScrollInactiveChat,
.name = "Mark as read of inactive chat",
.description = "Mark new messages as read and scroll the chat "
"even when the window is not in focus.",
});
} // namespace
const char kOptionAutoScrollInactiveChat[] =
"auto-scroll-inactive-chat";
MainWindow::MainWindow(not_null<Window::Controller*> controller)
: Platform::MainWindow(controller) {
resize(st::windowDefaultWidth, st::windowDefaultHeight);
@@ -538,8 +549,8 @@ bool MainWindow::markingAsRead() const {
&& !_main->isHidden()
&& !_main->animatingShow()
&& !_layer
&& isActive()
&& !_main->session().updates().isIdle();
&& (AutoScrollInactiveChat.value()
|| (isActive() && !_main->session().updates().isIdle()));
}
void MainWindow::checkActivation() {

View File

@@ -42,6 +42,8 @@ class LayerStackWidget;
class MediaPreviewWidget;
extern const char kOptionAutoScrollInactiveChat[];
class MainWindow : public Platform::MainWindow {
public:
explicit MainWindow(not_null<Window::Controller*> controller);

View File

@@ -285,10 +285,10 @@ QImage PrepareBlurredBackground(QSize outer, QImage frame) {
const auto bsize = frame.size();
const auto copyw = std::min(
bsize.width(),
outer.width() * bsize.height() / outer.height());
std::max(outer.width() * bsize.height() / outer.height(), 1));
const auto copyh = std::min(
bsize.height(),
outer.height() * bsize.width() / outer.width());
std::max(outer.height() * bsize.width() / outer.width(), 1));
auto copy = (bsize == QSize(copyw, copyh))
? std::move(frame)
: frame.copy(

View File

@@ -254,6 +254,7 @@ struct OverlayWidget::PipWrap {
PipDelegate delegate;
Pip wrapped;
rpl::lifetime lifetime;
};
OverlayWidget::Streamed::Streamed(
@@ -1035,13 +1036,18 @@ void OverlayWidget::fillContextMenuActions(const MenuCallback &addAction) {
}, &st::mediaMenuIconProfile);
}();
[&] { // Report userpic.
if (!_peer || !_photo ) {
if (!_peer || !_photo) {
return;
}
using Type = SharedMediaType;
if (userPhotosKey()) {
if (_peer->isSelf() || _peer->isNotificationsUser()) {
return;
} else if (const auto user = _peer->asUser()) {
if (user->hasPersonalPhoto()
&& user->userpicPhotoId() == _photo->id) {
return;
}
}
} else if ((sharedMediaType().value_or(Type::File) == Type::ChatPhoto)
|| (_peer->userpicPhotoId() == _photo->id)) {
@@ -1523,10 +1529,7 @@ void OverlayWidget::hideControls(bool force) {
if (!_dropdown->isHidden()
|| (_streamed && _streamed->controls.hasMenu())
|| _menu
|| _mousePressed
|| (_fullScreenVideo
&& !videoIsGifOrUserpic()
&& _streamed->controls.geometry().contains(_lastMouseMovePos))) {
|| _mousePressed) {
return;
}
}
@@ -3144,18 +3147,22 @@ void OverlayWidget::refreshClipControllerGeometry() {
void OverlayWidget::playbackControlsPlay() {
playbackPauseResume();
activateControls();
}
void OverlayWidget::playbackControlsPause() {
playbackPauseResume();
activateControls();
}
void OverlayWidget::playbackControlsToFullScreen() {
playbackToggleFullScreen();
activateControls();
}
void OverlayWidget::playbackControlsFromFullScreen() {
playbackToggleFullScreen();
activateControls();
}
void OverlayWidget::playbackControlsToPictureInPicture() {
@@ -3274,7 +3281,7 @@ void OverlayWidget::playbackControlsSeekProgress(crl::time position) {
if (!_streamed->instance.player().paused()
&& !_streamed->instance.player().finished()) {
_streamed->pausedBySeek = true;
playbackControlsPause();
playbackPauseResume();
}
}
@@ -3284,6 +3291,7 @@ void OverlayWidget::playbackControlsSeekFinished(crl::time position) {
_streamingStartPaused = !_streamed->pausedBySeek
&& !_streamed->instance.player().finished();
restartAtSeekPosition(position);
activateControls();
}
void OverlayWidget::playbackControlsVolumeChanged(float64 volume) {
@@ -3301,6 +3309,7 @@ float64 OverlayWidget::playbackControlsCurrentVolume() {
void OverlayWidget::playbackControlsVolumeToggled() {
const auto volume = Core::App().settings().videoVolume();
playbackControlsVolumeChanged(volume ? 0. : _lastPositiveVolume);
activateControls();
}
void OverlayWidget::playbackControlsVolumeChangeFinished() {
@@ -3308,6 +3317,7 @@ void OverlayWidget::playbackControlsVolumeChangeFinished() {
if (volume > 0.) {
_lastPositiveVolume = volume;
}
activateControls();
}
void OverlayWidget::playbackControlsSpeedChanged(float64 speed) {
@@ -3334,14 +3344,14 @@ void OverlayWidget::switchToPip() {
Expects(_document != nullptr);
const auto document = _document;
const auto message = _message;
const auto messageId = _message ? _message->fullId() : FullMsgId();
const auto topicRootId = _topicRootId;
const auto closeAndContinue = [=] {
_showAsPip = false;
show(OpenRequest(
findWindow(false),
document,
message,
document->owner().message(messageId),
topicRootId,
true));
};
@@ -3352,6 +3362,16 @@ void OverlayWidget::switchToPip() {
_streamed->instance.shared(),
closeAndContinue,
[=] { _pip = nullptr; });
if (const auto raw = _message) {
raw->history()->owner().itemRemoved(
) | rpl::filter([=](not_null<const HistoryItem*> item) {
return (raw == item);
}) | rpl::start_with_next([=] {
_pip = nullptr;
}, _pip->lifetime);
}
if (isHidden()) {
clearBeforeHide();
clearAfterHide();
@@ -4529,7 +4549,7 @@ void OverlayWidget::handleMouseRelease(
updateOver(position);
if (const auto activated = ClickHandler::unpressed()) {
if (activated->dragText() == u"internal:show_saved_message"_q) {
if (activated->url() == u"internal:show_saved_message"_q) {
showSaveMsgFile();
return;
}
@@ -4934,6 +4954,11 @@ void OverlayWidget::updateHeader() {
&& (index == count - 1)
&& SyncUserFallbackPhotoViewer(_user)) {
_headerText = tr::lng_mediaview_profile_public_photo(tr::now);
} else if (_user
&& _user->hasPersonalPhoto()
&& _photo
&& (_photo->id == _user->userpicPhotoId())) {
_headerText = tr::lng_mediaview_profile_photo_by_you(tr::now);
} else {
_headerText = tr::lng_mediaview_n_of_amount(
tr::now,

View File

@@ -21,11 +21,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_list_widget.h" // HistoryView::SelectedItem.
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "mainwindow.h"
#include "storage/storage_account.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/widgets/popup_menu.h"
#include "window/window_session_controller.h"
#include "window/window_controller.h"
#include "styles/style_menu_icons.h"
#include "styles/style_widgets.h"
@@ -76,7 +78,12 @@ void AddAction(
: &st::menuIconDownload;
const auto showToast = documents.empty();
const auto weak = base::make_weak(controller);
const auto saveImages = [=](const QString &folderPath) {
const auto controller = weak.get();
if (!controller) {
return;
}
const auto session = &controller->session();
const auto downloadPath = folderPath.isEmpty()
? Core::App().settings().downloadPath()
@@ -141,6 +148,10 @@ void AddAction(
saveDocuments(folderPath);
callback();
};
const auto controller = weak.get();
if (!controller) {
return;
}
if (Core::App().settings().askDownloadPath()) {
const auto initialPath = [] {
const auto path = Core::App().settings().downloadPath();
@@ -159,7 +170,7 @@ void AddAction(
}
};
FileDialog::GetFolder(
nullptr,
controller->window().widget().get(),
tr::lng_download_path_choose(tr::now),
initialPath,
handleFolder);

View File

@@ -116,7 +116,7 @@ void ConfigLoader::enumerate() {
}
void ConfigLoader::refreshSpecialLoader() {
if (_proxyEnabled) {
if (_proxyEnabled || _instance->isKeysDestroyer()) {
_specialLoader.reset();
return;
}
@@ -136,6 +136,7 @@ void ConfigLoader::setPhone(const QString &phone) {
}
void ConfigLoader::createSpecialLoader() {
const auto testMode = _instance->isTestMode();
_triedSpecialEndpoints.clear();
_specialLoader = std::make_unique<SpecialConfigRequest>([=](
DcId dcId,
@@ -147,7 +148,7 @@ void ConfigLoader::createSpecialLoader() {
} else {
addSpecialEndpoint(dcId, ip, port, secret);
}
}, _instance->configValues().txtDomainString, _phone);
}, testMode, _instance->configValues().txtDomainString, _phone);
}
void ConfigLoader::addSpecialEndpoint(

View File

@@ -65,7 +65,7 @@ QByteArray DnsUserAgent() {
static const auto kResult = QByteArray(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/107.0.5304.110 Safari/537.36");
"Chrome/108.0.5359.98 Safari/537.36");
return kResult;
}

View File

@@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/details/mtproto_tcp_socket.h"
#include "base/invoke_queued.h"
#include "base/qt/qt_common_adapters.h"
namespace MTP::details {
@@ -47,7 +46,7 @@ TcpSocket::TcpSocket(
wrap([=] { _readyRead.fire({}); }));
connect(
&_socket,
base::QTcpSocket_error,
&QAbstractSocket::errorOccurred,
wrap([=](Error e) { handleError(e); }));
}

View File

@@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/bytes.h"
#include "base/invoke_queued.h"
#include "base/unixtime.h"
#include "base/qt/qt_common_adapters.h"
#include <QtCore/QtEndian>
#include <range/v3/algorithm/reverse.hpp>
@@ -482,7 +481,7 @@ TlsSocket::TlsSocket(
wrap([=] { plainReadyRead(); }));
connect(
&_socket,
base::QTcpSocket_error,
&QAbstractSocket::errorOccurred,
wrap([=](Error e) { handleError(e); }));
}

View File

@@ -542,7 +542,7 @@ void Instance::Private::syncHttpUnixtime() {
InvokeQueued(_instance, [=] {
_httpUnixtimeLoader = nullptr;
});
}, configValues().txtDomainString);
}, isTestMode(), configValues().txtDomainString);
}
void Instance::Private::restartedByTimeout(ShiftedDcId shiftedDcId) {

View File

@@ -188,6 +188,7 @@ SpecialConfigRequest::SpecialConfigRequest(
int port,
bytes::const_span secret)> callback,
Fn<void()> timeDoneCallback,
bool isTestMode,
const QString &domainString,
const QString &phone)
: _callback(std::move(callback))
@@ -198,16 +199,7 @@ SpecialConfigRequest::SpecialConfigRequest(
_manager.setProxy(QNetworkProxy::NoProxy);
auto domains = DnsDomains();
const auto domainsCount = domains.size();
std::random_device rd;
ranges::shuffle(domains, std::mt19937(rd()));
const auto takeDomain = [&] {
const auto result = domains.back();
domains.pop_back();
return result;
};
const auto shuffle = [&](int from, int till) {
Expects(till > from);
@@ -219,14 +211,9 @@ SpecialConfigRequest::SpecialConfigRequest(
_attempts = {};
_attempts.push_back({ Type::Google, "dns.google.com" });
_attempts.push_back({ Type::Google, takeDomain(), "dns" });
_attempts.push_back({ Type::Mozilla, "mozilla.cloudflare-dns.com" });
_attempts.push_back({ Type::RemoteConfig, "firebaseremoteconfig" });
while (!domains.empty()) {
_attempts.push_back({ Type::Google, takeDomain(), "dns" });
}
if (!_timeDoneCallback) {
_attempts.push_back({ Type::Realtime, "firebaseio.com" });
_attempts.push_back({ Type::FireStore, "firestore" });
for (const auto &domain : DnsDomains()) {
_attempts.push_back({ Type::FireStore, domain, "firestore" });
@@ -234,12 +221,15 @@ SpecialConfigRequest::SpecialConfigRequest(
}
shuffle(0, 2);
shuffle(2, 4);
if (!_timeDoneCallback) {
shuffle(
_attempts.size() - (2 + domainsCount),
_attempts.size() - domainsCount);
shuffle(_attempts.size() - domainsCount, _attempts.size());
shuffle(_attempts.size() - (int(DnsDomains().size()) + 1), _attempts.size());
}
if (isTestMode) {
_attempts.erase(ranges::remove_if(_attempts, [](
const Attempt &attempt) {
return (attempt.type != Type::Google)
&& (attempt.type != Type::Mozilla);
}), _attempts.end());
}
ranges::reverse(_attempts); // We go from last to first.
@@ -252,17 +242,25 @@ SpecialConfigRequest::SpecialConfigRequest(
const std::string &ip,
int port,
bytes::const_span secret)> callback,
bool isTestMode,
const QString &domainString,
const QString &phone)
: SpecialConfigRequest(std::move(callback), nullptr, domainString, phone) {
: SpecialConfigRequest(
std::move(callback),
nullptr,
isTestMode,
domainString,
phone) {
}
SpecialConfigRequest::SpecialConfigRequest(
Fn<void()> timeDoneCallback,
bool isTestMode,
const QString &domainString)
: SpecialConfigRequest(
nullptr,
std::move(timeDoneCallback),
isTestMode,
domainString,
QString()) {
}

View File

@@ -25,10 +25,12 @@ public:
const std::string &ip,
int port,
bytes::const_span secret)> callback,
bool isTestMode,
const QString &domainString,
const QString &phone);
SpecialConfigRequest(
Fn<void()> timeDoneCallback,
bool isTestMode,
const QString &domainString);
private:
@@ -52,6 +54,7 @@ private:
int port,
bytes::const_span secret)> callback,
Fn<void()> timeDoneCallback,
bool isTestMode,
const QString &domainString,
const QString &phone);

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