Compare commits

..

71 Commits

Author SHA1 Message Date
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
John Preston
ef5e39f680 Beta version 4.4.3.
- Support for anonymous numbers from the Fragment.com platform.
- Fix a crash in own profile photo updating.
- Bug fixes and other minor improvements.
2022-12-29 15:03:34 +04:00
John Preston
27228480a8 Reorder privacy buttons like on Android. 2022-12-29 14:57:27 +04:00
John Preston
eebe1f4c11 Revert "Move Blocked users to Privacy in Settings."
This reverts commit 1acfe441e1.
2022-12-29 14:44:45 +04:00
Ilya Fedin
5d5e4cbdff Add information about required and supported peripheral to XDG metadata 2022-12-29 14:43:23 +04:00
Ilya Fedin
7e9920b5ea Set contributing URL in XDG metadata
Currently the button opens the official website instead
2022-12-29 14:43:23 +04:00
23rd
297fd0f0c8 Moved regexp for excluding digits to single place. 2022-12-29 13:36:28 +03:00
Ilya Fedin
0eec470387 Hide default download folder option if it's not available 2022-12-29 14:18:08 +04:00
John Preston
7a64725045 Improve fragment phone number description 2022-12-29 14:06:52 +04:00
John Preston
1acfe441e1 Move Blocked users to Privacy in Settings. 2022-12-29 14:06:52 +04:00
John Preston
80e932a083 Fix group call userpics on Retina. 2022-12-29 14:06:52 +04:00
bovirus
698d32db57 Update Inno Setup script
Add Copyright message with copyright autor styarting and ending yaer (current year) copyright
Add AppverName (app name and vesrion on top left in main window)
2022-12-29 13:41:37 +04:00
John Preston
2ee7cc784f Fix initial TTL after group creation. 2022-12-29 12:59:53 +04:00
John Preston
071411c8b9 Fix fragment button in Intro process. 2022-12-29 12:59:53 +04:00
23rd
43671e2b47 Fixed width of button in passport VerifyBox. 2022-12-29 11:48:53 +03:00
Ilya Fedin
1666683dbb Fix various line length in notifications_manager_linux 2022-12-29 12:30:17 +04:00
Ilya Fedin
c134861cd9 Have notification subtitle in title on Linux when body-markup is not supported
This makes it look way better
2022-12-29 12:30:17 +04:00
John Preston
a1a5ef9d39 Fix possible crash on force-logout from the server.. 2022-12-29 12:21:21 +04:00
John Preston
7e98e9ecf2 Fix uploading new group / account photo. 2022-12-29 12:16:49 +04:00
John Preston
cad6faa790 Fix connection re-init in case of CONNECTION_NOT_INITED. 2022-12-29 12:16:49 +04:00
John Preston
13ad39dfc2 Fix possible crash in logout. 2022-12-29 12:16:49 +04:00
23rd
aa8ca28f77 Added description to context menu for anonymous phone numbers. 2022-12-29 11:11:08 +03:00
23rd
d424a8b039 Added handler of urls for login. 2022-12-29 11:11:08 +03:00
23rd
9896855789 Added button for opening custom url to passport VerifyBox. 2022-12-29 11:11:08 +03:00
23rd
613d4932ca Added button for opening custom url to Ui::ConfirmPhoneBox. 2022-12-29 11:11:08 +03:00
23rd
44f79b8331 Added button for opening custom url to ChangePhone::EnterCode box. 2022-12-29 11:11:08 +03:00
23rd
aa1117a714 Added ability to sing up and sing in with anonymous numbers. 2022-12-29 11:11:08 +03:00
23rd
8748265b00 Added support of multiple phone pattern groups. 2022-12-29 11:11:08 +03:00
John Preston
be8aeb0d96 Fix week name display in chats list.
Fixes #25625.
2022-12-29 10:27:22 +04:00
John Preston
599cc35e57 Fix a crash in history clear. 2022-12-29 09:57:45 +04:00
John Preston
bd367da1bd Put #include after the #pragma once. 2022-12-29 09:52:38 +04:00
Ilya Fedin
62b50a41c8 Update libtgvoip 2022-12-29 09:32:07 +04:00
Ilya Fedin
a3caecbc07 Fix build with clang 2022-12-29 09:32:07 +04:00
John Preston
71354d1611 Fix two crashes in own profile photo edit. 2022-12-28 23:37:16 +04:00
132 changed files with 1415 additions and 428 deletions

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

@@ -849,6 +849,8 @@ PRIVATE
info/profile/info_profile_members.h
info/profile/info_profile_members_controllers.cpp
info/profile/info_profile_members_controllers.h
info/profile/info_profile_phone_menu.cpp
info/profile/info_profile_phone_menu.h
info/profile/info_profile_text.cpp
info/profile/info_profile_text.h
info/profile/info_profile_values.cpp

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -323,6 +323,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_intro_qr_step3" = "Scan this image to Log In";
"lng_intro_qr_skip" = "Or log in using your phone number";
"lng_intro_fragment_title" = "Enter code";
"lng_intro_fragment_about" = "Get the code for {phone_number} in the Anonymous Numbers section on Fragment.";
"lng_intro_fragment_button" = "Open Fragment";
"lng_phone_title" = "Your Phone Number";
"lng_phone_desc" = "Please confirm your country code and\nenter your mobile phone number.";
"lng_phone_to_qr" = "Quick log in using QR code";
@@ -751,6 +755,7 @@ 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»";
@@ -1192,6 +1197,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_info_tab_media" = "Media";
"lng_info_public_photo" = "public photo";
"lng_info_mobile_label" = "Mobile";
"lng_info_mobile_context_menu_fragment_about" = "This number is not tied to a SIM card and was acquired on {link}.";
"lng_info_mobile_context_menu_fragment_about_link" = "Fragment";
"lng_info_mobile_hidden" = "Hidden";
"lng_info_username_label" = "Username";
"lng_info_usernames_label" = "also";

View File

@@ -10,7 +10,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="4.4.2.0" />
Version="4.5.2.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,2,0
PRODUCTVERSION 4,4,2,0
FILEVERSION 4,5,2,0
PRODUCTVERSION 4,5,2,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.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2022"
VALUE "FileVersion", "4.5.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "4.4.2.0"
VALUE "ProductVersion", "4.5.2.0"
END
END
BLOCK "VarFileInfo"

View File

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

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "main/main_account.h"
#include "main/main_session.h"
#include "ui/boxes/confirm_box.h"
#include "ui/boxes/confirm_phone_box.h"
@@ -58,6 +59,10 @@ void ConfirmPhone::resolve(
}, [&](const MTPDauth_sentCodeTypeSetUpEmailRequired &) {
return bad("SetUpEmailRequired");
});
const auto fragmentUrl = data.vtype().match([](
const MTPDauth_sentCodeTypeFragmentSms &data) {
return qs(data.vurl());
}, [](const auto &) { return QString(); });
const auto phoneHash = qs(data.vphone_code_hash());
const auto timeout = [&]() -> std::optional<int> {
if (const auto nextType = data.vnext_type()) {
@@ -70,8 +75,15 @@ void ConfirmPhone::resolve(
auto box = Box<Ui::ConfirmPhoneBox>(
phone,
sentCodeLength,
fragmentUrl,
timeout);
const auto boxWeak = Ui::MakeWeak(box.data());
using LoginCode = rpl::event_stream<QString>;
const auto codeHandles = box->lifetime().make_state<LoginCode>();
controller->session().account().setHandleLoginCode([=](
const QString &code) {
codeHandles->fire_copy(code);
});
box->resendRequests(
) | rpl::start_with_next([=] {
_api.request(MTPauth_ResendCode(
@@ -83,7 +95,9 @@ void ConfirmPhone::resolve(
}
}).send();
}, box->lifetime());
box->checkRequests(
rpl::merge(
codeHandles->events(),
box->checkRequests()
) | rpl::start_with_next([=](const QString &code) {
if (_checkRequestId) {
return;
@@ -115,6 +129,10 @@ void ConfirmPhone::resolve(
boxWeak->showServerError(errorText);
}).handleFloodErrors().send();
}, box->lifetime());
box->boxClosing(
) | rpl::start_with_next([=] {
controller->session().account().setHandleLoginCode(nullptr);
}, box->lifetime());
controller->show(std::move(box), Ui::LayerOption::CloseOther);
});

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

@@ -467,6 +467,7 @@ void GroupInfoBox::prepare() {
&_navigation->parentController()->window(),
Ui::UserpicButton::Role::ChoosePhoto,
st::defaultUserpicButton);
_photo->showCustomOnChosen();
_title.create(
this,
st::defaultInputField,
@@ -640,10 +641,11 @@ void GroupInfoBox::createGroup(
MTP_int(_ttlPeriod)
)).done([=](const MTPUpdates &result) {
auto image = _photo->takeResultImage();
const auto period = _ttlPeriod;
const auto navigation = _navigation;
getDelegate()->hideLayer(); // Destroys 'this'.
ChatCreateDone(navigation, std::move(image), _ttlPeriod, result);
ChatCreateDone(navigation, std::move(image), period, result);
}).fail([=](const MTP::Error &error) {
const auto &type = error.type();
_creationRequestId = 0;

View File

@@ -293,6 +293,10 @@ membersAbout: FlatLabel(defaultFlatLabel) {
style: boxLabelStyle;
}
fragmentBoxButton: RoundButton(introFragmentButton) {
width: 256px;
}
passcodeHeaderFont: font(19px);
passcodeHeaderHeight: 80px;
passcodeInput: InputField(introPhone) {

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/change_phone_box.h"
#include "core/file_utilities.h"
#include "lang/lang_keys.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/sent_code_field.h"
@@ -19,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/confirm_box.h"
#include "boxes/phone_banned_box.h"
#include "countries/countries_instance.h" // Countries::ExtractPhoneCode.
#include "main/main_account.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_user.h"
@@ -67,6 +69,10 @@ void CreateErrorLabel(
}
}
[[nodiscard]] int ErrorSkip() {
return st::boxLittleSkip + st::changePhoneError.style.font->height;
}
} // namespace
namespace Settings {
@@ -109,6 +115,7 @@ public:
not_null<Window::SessionController*> controller,
const QString &phone,
const QString &hash,
const QString &openUrl,
int codeLength,
int callTimeout);
@@ -120,7 +127,7 @@ protected:
void prepare() override;
private:
void submit();
void submit(const QString &code);
void sendCall();
void updateCall();
void sendCodeFail(const MTP::Error &error);
@@ -128,18 +135,20 @@ private:
void hideError() {
showError(QString());
}
int countHeight();
[[nodiscard]] int countHeight() const;
const not_null<Window::SessionController*> _controller;
MTP::Sender _api;
QString _phone;
QString _hash;
QString _openUrl;
int _codeLength = 0;
int _callTimeout = 0;
object_ptr<Ui::SentCodeField> _code = { nullptr };
object_ptr<Ui::FadeWrap<Ui::FlatLabel>> _error = { nullptr };
object_ptr<Ui::FlatLabel> _callLabel = { nullptr };
object_ptr<Ui::RoundButton> _fragment = { nullptr };
mtpRequestId _requestId = 0;
Ui::SentCodeCall _call;
@@ -174,11 +183,9 @@ void ChangePhone::EnterPhone::prepare() {
this,
tr::lng_change_phone_new_description(tr::now),
st::changePhoneLabel);
const auto errorSkip = st::boxLittleSkip
+ st::changePhoneError.style.font->height;
description->moveToLeft(
st::boxPadding.left(),
_phone->y() + _phone->height() + errorSkip + st::boxLittleSkip);
_phone->y() + _phone->height() + ErrorSkip() + st::boxLittleSkip);
setDimensions(
st::boxWidth,
@@ -221,6 +228,7 @@ void ChangePhone::EnterPhone::sendPhoneDone(
return false;
};
auto codeLength = 0;
auto codeByFragmentUrl = QString();
const auto hasLength = data.vtype().match([&](
const MTPDauth_sentCodeTypeApp &typeData) {
LOG(("Error: should not be in-app code!"));
@@ -231,6 +239,7 @@ void ChangePhone::EnterPhone::sendPhoneDone(
return true;
}, [&](const MTPDauth_sentCodeTypeFragmentSms &typeData) {
codeLength = typeData.vlength().v;
codeByFragmentUrl = qs(typeData.vurl());
return true;
}, [&](const MTPDauth_sentCodeTypeCall &typeData) {
codeLength = typeData.vlength().v;
@@ -263,6 +272,7 @@ void ChangePhone::EnterPhone::sendPhoneDone(
_controller,
phoneNumber,
phoneCodeHash,
codeByFragmentUrl,
codeLength,
callTimeout),
Ui::LayerOption::KeepOther);
@@ -307,18 +317,21 @@ ChangePhone::EnterCode::EnterCode(
not_null<Window::SessionController*> controller,
const QString &phone,
const QString &hash,
const QString &openUrl,
int codeLength,
int callTimeout)
: _controller(controller)
, _api(&controller->session().mtp())
, _phone(phone)
, _hash(hash)
, _openUrl(openUrl)
, _codeLength(codeLength)
, _callTimeout(callTimeout)
, _call([this] { sendCall(); }, [this] { updateCall(); }) {
}
void ChangePhone::EnterCode::prepare() {
const auto width = st::boxWidth;
setTitle(tr::lng_change_phone_title());
const auto descriptionText = tr::lng_change_phone_code_description(
@@ -332,44 +345,69 @@ void ChangePhone::EnterCode::prepare() {
st::changePhoneLabel);
description->moveToLeft(st::boxPadding.left(), 0);
const auto submitInput = [=] { submit(_code->getDigitsOnly()); };
const auto phoneValue = QString();
_code.create(
this,
st::defaultInputField,
tr::lng_change_phone_code_title(),
phoneValue);
_code->setAutoSubmit(_codeLength, [=] { submit(); });
_code->setAutoSubmit(_codeLength, submitInput);
_code->setChangedCallback([=] { hideError(); });
_code->resize(st::boxWidth - 2 * st::boxPadding.left(), _code->height());
_code->resize(width - 2 * st::boxPadding.left(), _code->height());
_code->moveToLeft(st::boxPadding.left(), description->bottomNoMargins());
connect(_code, &Ui::InputField::submitted, [=] { submit(); });
connect(_code, &Ui::InputField::submitted, submitInput);
setDimensions(st::boxWidth, countHeight());
if (!_openUrl.isEmpty()) {
_fragment.create(
this,
tr::lng_intro_fragment_button(),
st::fragmentBoxButton);
_fragment->setClickedCallback([=] { File::OpenUrl(_openUrl); });
_fragment->setTextTransform(
Ui::RoundButton::TextTransform::NoTransform);
const auto codeBottom = _code->y() + _code->height();
_fragment->setFullWidth(_code->width());
_fragment->moveToLeft(
(width - _fragment->width()) / 2,
codeBottom + ErrorSkip() + st::boxLittleSkip);
}
_controller->session().account().setHandleLoginCode([=](QString code) {
submit(code);
});
boxClosing(
) | rpl::start_with_next([controller = _controller] {
controller->session().account().setHandleLoginCode(nullptr);
}, lifetime());
setDimensions(width, countHeight());
if (_callTimeout > 0) {
_call.setStatus({ Ui::SentCodeCall::State::Waiting, _callTimeout });
updateCall();
}
addButton(tr::lng_change_phone_new_submit(), [=] { submit(); });
addButton(tr::lng_change_phone_new_submit(), submitInput);
addButton(tr::lng_cancel(), [=] { closeBox(); });
}
int ChangePhone::EnterCode::countHeight() {
const auto errorSkip = st::boxLittleSkip
+ st::changePhoneError.style.font->height;
return _code->bottomNoMargins() + errorSkip + 3 * st::boxLittleSkip;
int ChangePhone::EnterCode::countHeight() const {
return _code->bottomNoMargins()
+ ErrorSkip()
+ 3 * st::boxLittleSkip
+ (_fragment ? _fragment->height() : 0);
}
void ChangePhone::EnterCode::submit() {
void ChangePhone::EnterCode::submit(const QString &code) {
if (_requestId) {
return;
}
hideError();
const auto session = &_controller->session();
const auto code = _code->getDigitsOnly();
const auto weak = Ui::MakeWeak(this);
_requestId = session->api().request(MTPaccount_ChangePhone(
MTP_string(_phone),

View File

@@ -25,7 +25,14 @@ DownloadPathBox::DownloadPathBox(
, _path(Core::App().settings().downloadPath())
, _pathBookmark(Core::App().settings().downloadPathBookmark())
, _group(std::make_shared<Ui::RadioenumGroup<Directory>>(typeFromPath(_path)))
, _default(this, _group, Directory::Downloads, tr::lng_download_path_default_radio(tr::now), st::defaultBoxCheckbox)
, _default(Core::App().canReadDefaultDownloadPath(true)
? object_ptr<Ui::Radioenum<Directory>>(
this,
_group,
Directory::Downloads,
tr::lng_download_path_default_radio(tr::now),
st::defaultBoxCheckbox)
: nullptr)
, _temp(this, _group, Directory::Temp, tr::lng_download_path_temp_radio(tr::now), st::defaultBoxCheckbox)
, _dir(this, _group, Directory::Custom, tr::lng_download_path_dir_radio(tr::now), st::defaultBoxCheckbox)
, _pathLink(this, QString(), st::boxLinkButton) {
@@ -50,7 +57,7 @@ void DownloadPathBox::updateControlsVisibility() {
auto custom = (_group->value() == Directory::Custom);
_pathLink->setVisible(custom);
auto newHeight = st::boxOptionListPadding.top() + _default->getMargins().top() + _default->heightNoMargins() + st::boxOptionListSkip + _temp->heightNoMargins() + st::boxOptionListSkip + _dir->heightNoMargins();
auto newHeight = st::boxOptionListPadding.top() + (_default ? _default->getMargins().top() + _default->heightNoMargins() : 0) + st::boxOptionListSkip + _temp->heightNoMargins() + st::boxOptionListSkip + _dir->heightNoMargins();
if (custom) {
newHeight += st::downloadPathSkip + _pathLink->height();
}
@@ -62,8 +69,10 @@ void DownloadPathBox::updateControlsVisibility() {
void DownloadPathBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
_default->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxOptionListPadding.top() + _default->getMargins().top());
_temp->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _default->bottomNoMargins() + st::boxOptionListSkip);
if (_default) {
_default->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxOptionListPadding.top() + _default->getMargins().top());
}
_temp->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), (_default ? _default->bottomNoMargins() : 0) + st::boxOptionListSkip);
_dir->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _temp->bottomNoMargins() + st::boxOptionListSkip);
auto inputx = st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultCheck.diameter + st::defaultBoxCheckbox.textPosition.x();
auto inputy = _dir->bottomNoMargins() + st::downloadPathSkip;

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

@@ -477,6 +477,7 @@ object_ptr<Ui::RpWidget> Controller::createPhotoEdit() {
st::defaultUserpicButton),
st::editPeerPhotoMargins);
_controls.photo = photoWrap->entity();
_controls.photo->showCustomOnChosen();
return photoWrap;
}

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

@@ -595,11 +595,9 @@ void Application::saveSettings() {
Local::writeSettings();
}
bool Application::canSaveFileWithoutAskingForPath() const {
if (Core::App().settings().askDownloadPath()) {
return false;
} else if (KSandbox::isInside()
&& Core::App().settings().downloadPath().isEmpty()) {
bool Application::canReadDefaultDownloadPath(bool always) const {
if (KSandbox::isInside()
&& (always || Core::App().settings().downloadPath().isEmpty())) {
const auto path = QStandardPaths::writableLocation(
QStandardPaths::DownloadLocation);
return base::CanReadDirectory(path);
@@ -607,6 +605,11 @@ bool Application::canSaveFileWithoutAskingForPath() const {
return true;
}
bool Application::canSaveFileWithoutAskingForPath() const {
return !Core::App().settings().askDownloadPath()
&& canReadDefaultDownloadPath();
}
MTP::Config &Application::fallbackProductionConfig() const {
if (!_fallbackProductionConfig) {
_fallbackProductionConfig = std::make_unique<MTP::Config>(

View File

@@ -178,6 +178,8 @@ public:
[[nodiscard]] Settings &settings();
void saveSettingsDelayed(crl::time delay = kDefaultSaveDelay);
void saveSettings();
[[nodiscard]] bool canReadDefaultDownloadPath(bool always = false) const;
[[nodiscard]] bool canSaveFileWithoutAskingForPath() const;
// Fallback config and proxy.

View File

@@ -84,6 +84,14 @@ std::map<int, const char*> BetaLogs() {
"- Set a public photo for those who are restricted to see "
"your profile photo in the Privacy Settings.\n"
"- Bug fixes and other minor improvements.\n"
},
{
4004003,
"- Support for anonymous numbers from the Fragment.com platform.\n"
"- Fix a crash in own profile photo updating.\n"
"- Bug fixes and other minor improvements.\n"
}
};

View File

@@ -24,8 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QStandardPaths>
#include <QtGui/QDesktopServices>
#include <ksandbox.h>
bool filedialogGetSaveFile(
QPointer<QWidget> parent,
QString &file,
@@ -173,15 +171,12 @@ QString DefaultDownloadPathFolder(not_null<Main::Session*> session) {
}
QString DefaultDownloadPath(not_null<Main::Session*> session) {
const auto standardLocation = QStandardPaths::writableLocation(
QStandardPaths::DownloadLocation);
const auto realDefaultPath = standardLocation
const auto realDefaultPath = QStandardPaths::writableLocation(
QStandardPaths::DownloadLocation)
+ '/'
+ DefaultDownloadPathFolder(session)
+ '/';
if (KSandbox::isInside()
&& Core::App().settings().downloadPath().isEmpty()
&& !base::CanReadDirectory(standardLocation)) {
if (!Core::App().canReadDefaultDownloadPath()) {
QStringList files;
QByteArray remoteContent;
const auto success = Platform::FileDialog::Get(

View File

@@ -49,6 +49,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_chat.h"
#include "settings/settings_premium.h"
#include "mainwidget.h"
#include "main/main_account.h"
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "inline_bots/bot_attach_web_view.h"
@@ -792,6 +793,25 @@ bool ResolvePremiumOffer(
return true;
}
bool ResolveLoginCode(
Window::SessionController *controller,
const Match &match,
const QVariant &context) {
const auto loginCode = match->captured(2);
if (loginCode.isEmpty()) {
return false;
};
(controller
? controller->session().account()
: Core::App().activeAccount()).handleLoginCode(loginCode);
if (controller) {
controller->window().activate();
} else if (const auto window = Core::App().activeWindow()) {
window->activate();
}
return true;
}
} // namespace
const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
@@ -864,6 +884,10 @@ const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
u"premium_offer/?(\\?.+)?(#|$)"_q,
ResolvePremiumOffer,
},
{
u"^login/?(\\?code=([0-9]+))(&|$)"_q,
ResolveLoginCode
},
{
u"^([^\\?]+)(\\?|#|$)"_q,
HandleUnknown

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

@@ -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 = 4004002;
constexpr auto AppVersionStr = "4.4.2";
constexpr auto AppBetaVersion = true;
constexpr auto AppVersion = 4005002;
constexpr auto AppVersionStr = "4.5.2";
constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -356,24 +356,55 @@ FormatResult CountriesInstance::format(FormatArgs args) {
const auto codeSize = int(bestCallingCodePtr->callingCode.size());
if (args.onlyGroups && args.incomplete) {
auto groups = args.skipCode
auto initialGroups = args.skipCode
? QVector<int>()
: QVector<int>{ codeSize };
auto groupSize = 0;
auto initialGroupsSize = 0;
if (bestCallingCodePtr->patterns.empty()) {
return FormatResult{ .groups = std::move(groups) };
return FormatResult{ .groups = std::move(initialGroups) };
}
for (const auto &c : bestCallingCodePtr->patterns.front()) {
if (c == ' ') {
groups.push_back(base::take(groupSize));
} else {
groupSize++;
auto bestGroups = initialGroups;
auto bestGroupsSize = initialGroupsSize;
auto bestPatternMaxMatches = -1;
for (const auto &pattern : bestCallingCodePtr->patterns) {
auto groups = initialGroups;
auto groupSize = initialGroupsSize;
auto lastSpacesCount = 0;
auto maxMatchedDigits = 0;
auto isNotBestPattern = false;
for (auto i = 0; i < pattern.size(); i++) {
const auto c = pattern.at(i);
if (c.isDigit()) {
const auto n = (i - lastSpacesCount) + codeSize;
if (n < phoneNumber.size()) {
if (phoneNumber.at(n) == c) {
maxMatchedDigits++;
} else {
isNotBestPattern = true;
}
} else {
isNotBestPattern = true;
}
}
if (c.isSpace()) {
groups.push_back(base::take(groupSize));
lastSpacesCount++;
} else {
groupSize++;
}
}
if (maxMatchedDigits > bestPatternMaxMatches) {
bestPatternMaxMatches = isNotBestPattern
? -1
: maxMatchedDigits;
bestGroups = std::move(groups);
bestGroupsSize = groupSize;
}
}
if (groupSize) {
groups.push_back(base::take(groupSize));
if (bestGroupsSize) {
bestGroups.push_back(base::take(bestGroupsSize));
}
return FormatResult{ .groups = std::move(groups) };
return FormatResult{ .groups = std::move(bestGroups) };
}
const auto formattedPart = phoneNumber.mid(codeSize);

View File

@@ -1198,26 +1198,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 {
@@ -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

@@ -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

@@ -3739,6 +3739,9 @@ void InnerWidget::setupShortcuts() {
Command::ChatPinned3,
Command::ChatPinned4,
Command::ChatPinned5,
Command::ChatPinned6,
Command::ChatPinned7,
Command::ChatPinned8,
};
auto &&pinned = ranges::views::zip(
kPinned,

View File

@@ -252,6 +252,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 +491,11 @@ void Row::stopLastRipple() {
}
}
void Row::clearRipple() {
BasicRow::clearRipple();
clearTopicJumpRipple();
}
void Row::addTopicJumpRipple(
QPoint origin,
not_null<Ui::TopicJumpCache*> topicJumpCache,
@@ -503,10 +512,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

@@ -2160,15 +2160,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) {

View File

@@ -89,12 +89,10 @@ void PaintRowDate(
const auto lastDate = lastTime.date();
const auto dt = [&] {
const auto wasSameDay = (lastDate == nowDate);
const auto wasRecently = qAbs(lastTime.secsTo(now)) < kRecentlyInSeconds;
if (wasSameDay || wasRecently) {
if ((lastDate == nowDate)
|| (qAbs(lastTime.secsTo(now)) < kRecentlyInSeconds)) {
return QLocale().toString(lastTime.time(), QLocale::ShortFormat);
} else if (lastDate.year() == nowDate.year()
&& lastDate.weekNumber() == nowDate.weekNumber()) {
} else if (qAbs(lastDate.daysTo(nowDate)) < 7) {
return langDayOfWeek(lastDate);
} else {
return QLocale().toString(lastDate, QLocale::ShortFormat);

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

@@ -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

@@ -1431,12 +1431,18 @@ void HistoryItem::applyEdition(const MTPDmessageService &message) {
if (wasGrouped) {
history()->owner().groups().unregisterMessage(this);
}
if (const auto reply = Get<HistoryMessageReply>()) {
reply->clearData(this);
}
clearDependencyMessage();
UpdateComponents(0);
createServiceFromMtp(message);
applyServiceDateEdition(message);
finishEditionToEmpty();
} else if (isService()) {
if (const auto reply = Get<HistoryMessageReply>()) {
reply->clearData(this);
}
clearDependencyMessage();
UpdateComponents(0);
createServiceFromMtp(message);

View File

@@ -283,9 +283,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 +313,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);
@@ -463,14 +472,15 @@ void HistoryMessageReply::paint(
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 +492,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

@@ -246,6 +246,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

@@ -176,8 +176,8 @@ const char kOptionAutoScrollInactiveChat[] =
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;
@@ -6224,7 +6224,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 +7490,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 +7545,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;
@@ -626,6 +627,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

@@ -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

@@ -141,7 +141,7 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallBarContentByCall(
userpic.peer->loadUserpic();
auto image = userpic.peer->generateUserpicImage(
userpic.view,
userpicSize);
userpicSize * style::DevicePixelRatio());
userpic.uniqueKey = userpic.peer->userpicUniqueKey(userpic.view);
state->current.users.push_back({
.userpic = std::move(image),

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

@@ -88,7 +88,7 @@ rpl::producer<Ui::RequestsBarContent> RequestsBarContentByPeer(
userpic.peer->loadUserpic();
auto image = userpic.peer->generateUserpicImage(
userpic.view,
userpicSize);
userpicSize * style::DevicePixelRatio());
userpic.uniqueKey = userpic.peer->userpicUniqueKey(userpic.view);
state->current.users.push_back({
.userpic = std::move(image),

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

@@ -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

@@ -45,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/info_controller.h"
#include "info/info_memento.h"
#include "info/profile/info_profile_icon.h"
#include "info/profile/info_profile_phone_menu.h"
#include "info/profile/info_profile_values.h"
#include "info/profile/info_profile_text.h"
#include "support/support_helper.h"
@@ -391,10 +392,17 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
user->session().supportHelper().infoTextValue(user));
}
addInfoOneLine(
tr::lng_info_mobile_label(),
PhoneOrHiddenValue(user),
tr::lng_profile_copy_phone(tr::now));
{
const auto phoneLabel = addInfoOneLine(
tr::lng_info_mobile_label(),
PhoneOrHiddenValue(user),
tr::lng_profile_copy_phone(tr::now)).text;
const auto hook = [=](Ui::FlatLabel::ContextMenuRequest request) {
phoneLabel->fillContextMenu(request);
AddPhoneMenu(request.menu, user);
};
phoneLabel->setContextMenuHook(hook);
}
auto label = user->isBot()
? tr::lng_info_about_label()
: tr::lng_info_bio_label();

View File

@@ -0,0 +1,155 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "info/profile/info_profile_phone_menu.h"
#include "data/data_user.h"
#include "lang/lang_keys.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/menu/menu_action.h"
#include "ui/widgets/popup_menu.h"
#include "styles/style_chat.h" // expandedMenuSeparator.
namespace Info {
namespace Profile {
namespace {
class TextItem final : public Ui::Menu::ItemBase {
public:
TextItem(
not_null<Ui::RpWidget*> parent,
const style::Menu &st,
rpl::producer<TextWithEntities> &&text);
not_null<QAction*> action() const override;
bool isEnabled() const override;
protected:
int contentHeight() const override;
private:
const base::unique_qptr<Ui::FlatLabel> _label;
const not_null<QAction*> _dummyAction;
};
[[nodiscard]] int CountMinWidthForHeight(
not_null<Ui::FlatLabel*> label,
int basicWidth,
int heightLimit) {
const auto height = [&](int width) {
label->resizeToWidth(width);
return label->height();
};
auto widthMin = basicWidth;
auto widthMax = label->naturalWidth();
if (height(widthMin) <= heightLimit || height(widthMax) > heightLimit) {
return basicWidth;
}
while (widthMin + 1 < widthMax) {
const auto middle = (widthMin + widthMax) / 2;
if (height(middle) > heightLimit) {
widthMin = middle;
} else {
widthMax = middle;
}
}
return widthMax;
}
TextItem::TextItem(
not_null<Ui::RpWidget*> parent,
const style::Menu &st,
rpl::producer<TextWithEntities> &&text)
: ItemBase(parent, st)
, _label(base::make_unique_q<Ui::FlatLabel>(
this,
std::move(text),
st::historyMessagesTTLLabel))
, _dummyAction(Ui::CreateChild<QAction>(parent.get())) {
// Try to fit the phrase in two lines.
const auto limit = st::historyMessagesTTLLabel.style.font->height * 2;
const auto min1 = st::historyMessagesTTLLabel.minWidth;
const auto min2 = CountMinWidthForHeight(_label.get(), min1, limit);
const auto added = st.itemPadding.left() + st.itemPadding.right();
setMinWidth(std::max(min1, min2) + added);
sizeValue(
) | rpl::start_with_next([=](const QSize &s) {
if (s.width() <= added) {
return;
}
_label->resizeToWidth(s.width() - added);
_label->moveToLeft(
st.itemPadding.left(),
(s.height() - _label->height()) / 2);
}, lifetime());
_label->resizeToWidth(parent->width() - added);
initResizeHook(parent->sizeValue());
}
not_null<QAction*> TextItem::action() const {
return _dummyAction;
}
bool TextItem::isEnabled() const {
return false;
}
int TextItem::contentHeight() const {
return _label->height();
}
} // namespace
void AddPhoneMenu(not_null<Ui::PopupMenu*> menu, not_null<UserData*> user) {
if (user->isSelf()) {
return;
}
using Strings = std::vector<QString>;
const auto prefixes = user->session().account().appConfig().get<Strings>(
u"fragment_prefixes"_q,
std::vector<QString>());
{
const auto proj = [&phone = user->phone()](const QString &p) {
return phone.startsWith(p);
};
if (ranges::none_of(prefixes, proj)) {
return;
}
}
const auto domains = user->session().account().appConfig().get<Strings>(
u"whitelisted_domains"_q,
std::vector<QString>());
const auto proj = [&, domain = u"fragment"_q](const QString &p) {
return p.contains(domain);
};
const auto it = ranges::find_if(domains, proj);
if (it == end(domains)) {
return;
}
menu->addSeparator(&st::expandedMenuSeparator);
const auto link = Ui::Text::Link(
tr::lng_info_mobile_context_menu_fragment_about_link(tr::now),
*it);
menu->addAction(base::make_unique_q<TextItem>(
menu->menu(),
st::reactionMenu.menu,
tr::lng_info_mobile_context_menu_fragment_about(
lt_link,
rpl::single(link),
Ui::Text::RichLangValue)));
}
} // namespace Profile
} // namespace Info

View File

@@ -0,0 +1,22 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class UserData;
namespace Ui {
class PopupMenu;
} // namespace Ui
namespace Info {
namespace Profile {
void AddPhoneMenu(not_null<Ui::PopupMenu*> menu, not_null<UserData*> user);
} // namespace Profile
} // namespace Info

View File

@@ -85,9 +85,17 @@ introCoverDuration: 200;
introNextButton: RoundButton(defaultActiveButton) {
width: 300px;
height: 42px;
radius: 6px;
textTop: 11px;
font: font(boxFontSize semibold);
}
introFragmentIcon: icon{{ "fragment", activeButtonFg }};
introFragmentIconOver: icon{{ "fragment", activeButtonFgOver }};
introFragmentButton: RoundButton(introNextButton) {
icon: introFragmentIcon;
iconOver: introFragmentIconOver;
iconPosition: point(-10px, 9px);
}
introStepFieldTop: 96px;
introPhoneTop: 6px;

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "intro/intro_signup.h"
#include "intro/intro_password_check.h"
#include "core/file_utilities.h"
#include "core/update_checker.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
@@ -99,8 +100,18 @@ CodeWidget::CodeWidget(
_code->setDigitsCountMax(getData()->codeLength);
setTitleText(rpl::single(Ui::FormatPhone(getData()->phone)));
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);
submitCode();
});
}
void CodeWidget::refreshLang() {
@@ -117,9 +128,18 @@ int CodeWidget::errorTop() const {
void CodeWidget::updateDescText() {
const auto byTelegram = getData()->codeByTelegram;
const auto isFragment = !getData()->codeByFragmentUrl.isEmpty();
_isFragment = isFragment;
setDescriptionText(
(byTelegram ? tr::lng_code_from_telegram : tr::lng_code_desc)(
Ui::Text::RichLangValue));
isFragment
? tr::lng_intro_fragment_about(
lt_phone_number,
rpl::single(TextWithEntities{
.text = Ui::FormatPhone(getData()->phone)
}),
Ui::Text::RichLangValue)
: (byTelegram ? tr::lng_code_from_telegram : tr::lng_code_desc)(
Ui::Text::RichLangValue));
if (getData()->codeByTelegram) {
_noTelegramCode->show();
_callTimer.cancel();
@@ -204,6 +224,7 @@ void CodeWidget::activate() {
void CodeWidget::finished() {
Step::finished();
account().setHandleLoginCode(nullptr);
_checkRequestTimer.cancel();
_callTimer.cancel();
apiClear();
@@ -300,7 +321,7 @@ void CodeWidget::codeSubmitFail(const MTP::Error &error) {
void CodeWidget::codeChanged() {
hideError();
submit();
submitCode();
}
void CodeWidget::sendCall() {
@@ -362,10 +383,18 @@ void CodeWidget::gotPassword(const MTPaccount_Password &result) {
}
void CodeWidget::submit() {
if (getData()->codeByFragmentUrl.isEmpty()) {
submitCode();
} else {
File::OpenUrl(getData()->codeByFragmentUrl);
}
}
void CodeWidget::submitCode() {
const auto text = QString(
_code->getLastText()
).remove(
QRegularExpression("[^\\d]")
TextUtilities::RegExpDigitsExclude()
).mid(0, getData()->codeLength);
if (_sentRequest
@@ -393,6 +422,22 @@ void CodeWidget::submit() {
}).handleFloodErrors().send();
}
rpl::producer<QString> CodeWidget::nextButtonText() const {
return _isFragment.value(
) | rpl::map([=](bool isFragment) {
return isFragment
? tr::lng_intro_fragment_button()
: Step::nextButtonText();
}) | rpl::flatten_latest();
}
rpl::producer<const style::RoundButton*> CodeWidget::nextButtonStyle() const {
return _isFragment.value(
) | rpl::map([](bool isFragment) {
return isFragment ? &st::introFragmentButton : nullptr;
});
}
void CodeWidget::noTelegramCode() {
if (_noTelegramCodeRequestId) {
return;

View File

@@ -55,6 +55,8 @@ public:
void finished() override;
void cancelled() override;
void submit() override;
rpl::producer<QString> nextButtonText() const override;
rpl::producer<const style::RoundButton*> nextButtonStyle() const override;
void updateDescText();
@@ -83,6 +85,8 @@ private:
void noTelegramCodeDone(const MTPauth_SentCode &result);
void noTelegramCodeFail(const MTP::Error &result);
void submitCode();
void stopCheck();
object_ptr<Ui::LinkButton> _noTelegramCode;
@@ -92,6 +96,8 @@ private:
QString _sentCode;
mtpRequestId _sentRequest = 0;
rpl::variable<bool> _isFragment = false;
base::Timer _callTimer;
CallStatus _callStatus = CallStatus();
int _callTimeout;

View File

@@ -34,6 +34,8 @@ SignupWidget::SignupWidget(
, _first(this, st::introName, tr::lng_signup_firstname())
, _last(this, st::introName, tr::lng_signup_lastname())
, _invertOrder(langFirstNameGoesSecond()) {
_photo->showCustomOnChosen();
Lang::Updated(
) | rpl::start_with_next([=] {
refreshLang();

View File

@@ -119,6 +119,10 @@ rpl::producer<QString> Step::nextButtonText() const {
return tr::lng_intro_next();
}
rpl::producer<const style::RoundButton*> Step::nextButtonStyle() const {
return rpl::single((const style::RoundButton*)(nullptr));
}
void Step::goBack() {
if (_goCallback) {
_goCallback(nullptr, StackAction::Back, Animate::Back);

View File

@@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rp_widget.h"
#include "ui/effects/animations.h"
namespace style {
struct RoundButton;
} // namespace style;
namespace Main {
class Account;
} // namespace Main;
@@ -77,6 +81,8 @@ public:
virtual void submit() = 0;
[[nodiscard]] virtual rpl::producer<QString> nextButtonText() const;
[[nodiscard]] virtual auto nextButtonStyle() const
-> rpl::producer<const style::RoundButton*>;
[[nodiscard]] int contentLeft() const;
[[nodiscard]] int contentTop() const;

View File

@@ -74,6 +74,7 @@ Widget::Widget(
: RpWidget(parent)
, _account(account)
, _data(details::Data{ .controller = controller })
, _nextStyle(&st::introNextButton)
, _back(this, object_ptr<Ui::IconButton>(this, st::introBackButton))
, _settings(
this,
@@ -83,7 +84,7 @@ Widget::Widget(
st::defaultBoxButton))
, _next(
this,
object_ptr<Ui::RoundButton>(this, nullptr, st::introNextButton))
object_ptr<Ui::RoundButton>(this, nullptr, *_nextStyle))
, _connecting(std::make_unique<Window::ConnectionState>(
this,
account,
@@ -127,10 +128,6 @@ Widget::Widget(
_back->entity()->setClickedCallback([=] { backRequested(); });
_back->hide(anim::type::instant);
_next->entity()->setClickedCallback([=] { getStep()->submit(); });
_next->entity()->setTextTransform(
Ui::RoundButton::TextTransform::NoTransform);
if (_changeLanguage) {
_changeLanguage->finishAnimating();
}
@@ -344,13 +341,35 @@ void Widget::historyMove(StackAction action, Animate animate) {
if (_terms) {
hideAndDestroy(std::exchange(_terms, { nullptr }));
}
{
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();
getStep()->prepareShowAnimated(wasStep);
if (wasStep->hasCover() != getStep()->hasCover()) {
_nextTopFrom = wasStep->contentTop() + st::introNextTop;
_controlsTopFrom = wasStep->hasCover() ? st::introCoverHeight : 0;
_coverShownAnimation.start([this] { updateControlsGeometry(); }, 0., 1., st::introCoverDuration, wasStep->hasCover() ? anim::linear : anim::easeOutCirc);
_coverShownAnimation.start(
[this] { updateControlsGeometry(); },
0.,
1.,
st::introCoverDuration,
wasStep->hasCover() ? anim::linear : anim::easeOutCirc);
}
_stepLifetime.destroy();
@@ -665,6 +684,10 @@ void Widget::showControls() {
}
void Widget::setupNextButton() {
_next->entity()->setClickedCallback([=] { getStep()->submit(); });
_next->entity()->setTextTransform(
Ui::RoundButton::TextTransform::NoTransform);
_next->entity()->setText(getStep()->nextButtonText(
) | rpl::filter([](const QString &text) {
return !text.isEmpty();
@@ -757,13 +780,18 @@ void Widget::resizeEvent(QResizeEvent *e) {
}
void Widget::updateControlsGeometry() {
auto shown = _coverShownAnimation.value(1.);
const auto skip = st::introSettingsSkip;
const auto shown = _coverShownAnimation.value(1.);
auto controlsTopTo = getStep()->hasCover() ? st::introCoverHeight : 0;
auto controlsTop = anim::interpolate(_controlsTopFrom, controlsTopTo, shown);
_settings->moveToRight(st::introSettingsSkip, controlsTop + st::introSettingsSkip);
const auto controlsTop = anim::interpolate(
_controlsTopFrom,
getStep()->hasCover() ? st::introCoverHeight : 0,
shown);
_settings->moveToRight(skip, controlsTop + skip);
if (_update) {
_update->moveToRight(st::introSettingsSkip + _settings->width() + st::introSettingsSkip, _settings->y());
_update->moveToRight(
skip + _settings->width() + skip,
_settings->y());
}
_back->moveToLeft(0, controlsTop);
@@ -779,13 +807,19 @@ void Widget::updateControlsGeometry() {
? QRect(0, 0, width(), realNextTop)
: QRect());
if (_changeLanguage) {
_changeLanguage->moveToLeft((width() - _changeLanguage->width()) / 2, _next->y() + _next->height() + _changeLanguage->height());
_changeLanguage->moveToLeft(
(width() - _changeLanguage->width()) / 2,
_next->y() + _next->height() + _changeLanguage->height());
}
if (_resetAccount) {
_resetAccount->moveToLeft((width() - _resetAccount->width()) / 2, height() - st::introResetBottom - _resetAccount->height());
_resetAccount->moveToLeft(
(width() - _resetAccount->width()) / 2,
height() - st::introResetBottom - _resetAccount->height());
}
if (_terms) {
_terms->moveToLeft((width() - _terms->width()) / 2, height() - st::introTermsBottom - _terms->height());
_terms->moveToLeft(
(width() - _terms->width()) / 2,
height() - st::introTermsBottom - _terms->height());
}
}

View File

@@ -186,6 +186,8 @@ private:
int _nextTopFrom = 0;
int _controlsTopFrom = 0;
const style::RoundButton *_nextStyle = nullptr;
object_ptr<Ui::FadeWrap<Ui::IconButton>> _back;
object_ptr<Ui::FadeWrap<Ui::RoundButton>> _update = { nullptr };
object_ptr<Ui::FadeWrap<Ui::RoundButton>> _settings;

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

@@ -602,6 +602,16 @@ void Account::destroyStaleAuthorizationKeys() {
}
}
void Account::setHandleLoginCode(Fn<void(QString)> callback) {
_handleLoginCode = std::move(callback);
}
void Account::handleLoginCode(const QString &code) const {
if (_handleLoginCode) {
_handleLoginCode(code);
}
}
void Account::resetAuthorizationKeys() {
Expects(_mtp != nullptr);

View File

@@ -110,6 +110,9 @@ public:
void suggestMainDcId(MTP::DcId mainDcId);
void destroyStaleAuthorizationKeys();
void setHandleLoginCode(Fn<void(QString)> callback);
void handleLoginCode(const QString &code) const;
[[nodiscard]] rpl::lifetime &lifetime() {
return _lifetime;
}
@@ -152,6 +155,8 @@ private:
std::unique_ptr<Session> _session;
rpl::variable<Session*> _sessionValue;
Fn<void(QString)> _handleLoginCode = nullptr;
UserId _sessionUserId = 0;
QByteArray _sessionUserSerialized;
int32 _sessionUserStreamVersion = 0;

View File

@@ -198,7 +198,6 @@ QByteArray Session::validTmpPassword() const {
// Can be called only right before ~Session.
void Session::finishLogout() {
updates().updateOnline();
unlockTerms();
data().clear();
data().clearLocalStorage();

View File

@@ -254,6 +254,7 @@ struct OverlayWidget::PipWrap {
PipDelegate delegate;
Pip wrapped;
rpl::lifetime lifetime;
};
OverlayWidget::Streamed::Streamed(
@@ -3334,14 +3335,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 +3353,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();

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

@@ -1537,6 +1537,7 @@ bool Instance::Private::onErrorDefault(
const auto session = getSession(qAbs(dcWithShift));
request->needsLayer = true;
session->setConnectionNotInited();
session->sendPrepared(request);
return true;
} else if (type == u"CONNECTION_LANG_CODE_INVALID"_q) {

View File

@@ -257,10 +257,14 @@ void Session::refreshOptions() {
}
void Session::reInitConnection() {
_dc->setConnectionInited(false);
setConnectionNotInited();
restart();
}
void Session::setConnectionNotInited() {
_dc->setConnectionInited(false);
}
void Session::stop() {
if (_killed) {
DEBUG_LOG(("Session Error: can't stop a killed session"));

View File

@@ -142,6 +142,7 @@ public:
void start();
void reInitConnection();
void setConnectionNotInited();
void restart();
void refreshOptions();

View File

@@ -1610,9 +1610,11 @@ Link::Link(
}),
parent->fullId());
} else if (_page->photo) {
if (_page->type == WebPageType::Profile || _page->type == WebPageType::Video) {
if (_page->type == WebPageType::Profile
|| _page->type == WebPageType::Video) {
_photol = createHandler(_page->url);
} else if (_page->type == WebPageType::Photo
|| _page->type == WebPageType::Document
|| _page->siteName == u"Twitter"_q
|| _page->siteName == u"Facebook"_q) {
_photol = std::make_shared<PhotoOpenClickHandler>(

View File

@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "passport/passport_panel_controller.h"
#include "main/main_account.h"
#include "main/main_session.h"
#include "lang/lang_keys.h"
#include "passport/passport_panel_edit_document.h"
#include "passport/passport_panel_edit_contact.h"
@@ -485,7 +487,9 @@ EditContactScheme GetContactScheme(Scope::Type type) {
return Ui::FormatPhone(value);
};
result.postprocess = [](QString value) {
return value.replace(QRegularExpression("[^\\d]"), QString());
return value.replace(
TextUtilities::RegExpDigitsExclude(),
QString());
};
return result;
} break;
@@ -1319,11 +1323,16 @@ void PanelController::processVerificationNeeded(
});
const auto box = [&] {
if (type == Value::Type::Phone) {
return show(VerifyPhoneBox(
const auto submit = [=](const QString &code) {
_form->verify(value, code);
};
const auto account = &_form->window()->session().account();
account->setHandleLoginCode(submit);
const auto box = show(VerifyPhoneBox(
text,
value->verification.codeLength,
[=](const QString &code) { _form->verify(value, code); },
value->verification.fragmentUrl,
submit,
value->verification.call ? rpl::single(
value->verification.call->getText()
) | rpl::then(rpl::duplicate(
@@ -1339,6 +1348,11 @@ void PanelController::processVerificationNeeded(
) | rpl::map([=](not_null<const Value*> field) {
return field->verification.error;
}) | rpl::distinct_until_changed()));
box->boxClosing(
) | rpl::start_with_next([=] {
account->setHandleLoginCode(nullptr);
}, box->lifetime());
return box;
} else if (type == Value::Type::Email) {
return show(VerifyEmailBox(
text,

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "passport/passport_panel_edit_contact.h"
#include "core/file_utilities.h"
#include "passport/passport_panel_controller.h"
#include "passport/ui/passport_details_row.h"
#include "ui/widgets/input_fields.h"
@@ -26,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "countries/countries_instance.h" // Countries::ExtractPhoneCode.
#include "main/main_session.h"
#include "lang/lang_keys.h"
#include "styles/style_boxes.h"
#include "styles/style_passport.h"
#include "styles/style_layers.h"
@@ -39,6 +41,7 @@ public:
rpl::producer<QString> title,
const QString &text,
int codeLength,
const QString &openUrl,
Fn<void(QString code)> submit,
Fn<void()> resend,
rpl::producer<QString> call,
@@ -54,6 +57,7 @@ private:
void setupControls(
const QString &text,
int codeLength,
const QString &openUrl,
Fn<void(QString code)> submit,
Fn<void()> resend,
rpl::producer<QString> call,
@@ -72,6 +76,7 @@ VerifyBox::VerifyBox(
rpl::producer<QString> title,
const QString &text,
int codeLength,
const QString &openUrl,
Fn<void(QString code)> submit,
Fn<void()> resend,
rpl::producer<QString> call,
@@ -81,6 +86,7 @@ VerifyBox::VerifyBox(
setupControls(
text,
codeLength,
openUrl,
submit,
resend,
std::move(call),
@@ -91,6 +97,7 @@ VerifyBox::VerifyBox(
void VerifyBox::setupControls(
const QString &text,
int codeLength,
const QString &openUrl,
Fn<void(QString code)> submit,
Fn<void()> resend,
rpl::producer<QString> call,
@@ -130,6 +137,21 @@ void VerifyBox::setupControls(
std::move(call),
st::boxDividerLabel),
small);
if (!openUrl.isEmpty()) {
const auto button = _content->add(
object_ptr<Ui::RoundButton>(
_content,
tr::lng_intro_fragment_button(),
st::fragmentBoxButton),
small);
_content->widthValue(
) | rpl::start_with_next([=](int w) {
button->setFullWidth(w - small.left() - small.right());
}, button->lifetime());
button->setClickedCallback([=] { ::File::OpenUrl(openUrl); });
button->setTextTransform(
Ui::RoundButton::TextTransform::NoTransform);
}
if (resend) {
auto link = TextWithEntities{ tr::lng_cloud_password_resend(tr::now) };
link.entities.push_back({
@@ -144,9 +166,7 @@ void VerifyBox::setupControls(
link
) | rpl::then(rpl::duplicate(
resent
) | rpl::map([](const QString &value) {
return TextWithEntities{ value };
})),
) | rpl::map(TextWithEntities::Simple)),
st::boxDividerLabel),
small);
std::move(
@@ -392,6 +412,7 @@ void PanelEditContact::save(const QString &value) {
object_ptr<Ui::BoxContent> VerifyPhoneBox(
const QString &phone,
int codeLength,
const QString &openUrl,
Fn<void(QString code)> submit,
rpl::producer<QString> call,
rpl::producer<QString> error) {
@@ -402,6 +423,7 @@ object_ptr<Ui::BoxContent> VerifyPhoneBox(
lt_phone,
Ui::FormatPhone(phone)),
codeLength,
openUrl,
submit,
nullptr,
std::move(call),
@@ -420,6 +442,7 @@ object_ptr<Ui::BoxContent> VerifyEmailBox(
tr::lng_passport_email_title(),
tr::lng_passport_confirm_email(tr::now, lt_email, email),
codeLength,
QString(),
submit,
resend,
rpl::single(QString()),

View File

@@ -78,6 +78,7 @@ private:
object_ptr<Ui::BoxContent> VerifyPhoneBox(
const QString &phone,
int codeLength,
const QString &openUrl,
Fn<void(QString code)> submit,
rpl::producer<QString> call,
rpl::producer<QString> error);

View File

@@ -80,7 +80,8 @@ std::unique_ptr<base::Platform::DBus::ServiceWatcher> CreateServiceWatcher() {
try {
return ranges::contains(
base::Platform::DBus::ListActivatableNames(connection),
Glib::ustring(std::string(kService)));
std::string(kService),
&Glib::ustring::raw);
} catch (...) {
// avoid service restart loop in sandboxed environments
return true;
@@ -129,9 +130,11 @@ void StartServiceAsync(Fn<void()> callback) {
};
const auto errorName =
Gio::DBus::ErrorUtils::get_remote_error(e);
Gio::DBus::ErrorUtils::get_remote_error(e).raw();
if (!ranges::contains(NotSupportedErrors, errorName)) {
if (!ranges::contains(
NotSupportedErrors,
errorName)) {
throw e;
}
}
@@ -166,7 +169,8 @@ bool GetServiceRegistered() {
try {
return ranges::contains(
DBus::ListActivatableNames(connection),
Glib::ustring(std::string(kService)));
std::string(kService),
&Glib::ustring::raw);
} catch (...) {
return false;
}
@@ -383,12 +387,12 @@ bool NotificationData::init(
const QString &msg,
Window::Notifications::Manager::DisplayOptions options) {
if (_application) {
_notification = Gio::Notification::create(title.toStdString());
_notification->set_body(
_notification = Gio::Notification::create(
subtitle.isEmpty()
? msg.toStdString()
: u"%1\n%2"_q.arg(subtitle, msg).toStdString());
? title.toStdString()
: subtitle.toStdString() + " (" + title.toStdString() + ')');
_notification->set_body(msg.toStdString());
_notification->set_icon(
Gio::ThemedIcon::create(base::IconName().toStdString()));
@@ -498,19 +502,22 @@ bool NotificationData::init(
});
};
_title = title.toStdString();
_imageKey = GetImageKey(CurrentServerInformationValue().specVersion);
if (capabilities.contains(u"body-markup"_q)) {
_title = title.toStdString();
_body = subtitle.isEmpty()
? msg.toHtmlEscaped().toStdString()
: u"<b>%1</b>\n%2"_q.arg(
subtitle.toHtmlEscaped(),
msg.toHtmlEscaped()).toStdString();
} else {
_body = subtitle.isEmpty()
? msg.toStdString()
: u"%1\n%2"_q.arg(subtitle, msg).toStdString();
_title = subtitle.isEmpty()
? title.toStdString()
: subtitle.toStdString() + " (" + title.toStdString() + ')';
_body = msg.toStdString();
}
if (capabilities.contains("actions")) {
@@ -529,12 +536,13 @@ bool NotificationData::init(
_actions.push_back(
tr::lng_notification_reply(tr::now).toStdString());
_notificationRepliedSignalId = _dbusConnection->signal_subscribe(
signalEmitted,
std::string(kService),
std::string(kInterface),
"NotificationReplied",
std::string(kObjectPath));
_notificationRepliedSignalId =
_dbusConnection->signal_subscribe(
signalEmitted,
std::string(kService),
std::string(kInterface),
"NotificationReplied",
std::string(kObjectPath));
} else {
// icon name according to https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
_actions.push_back("mail-reply-sender");
@@ -818,8 +826,6 @@ bool ByDefault() {
static const auto NeededCapabilities = {
// To show message content
u"body"_q,
// To make the sender name bold
u"body-markup"_q,
// To have buttons on notifications
u"actions"_q,
// To have quick reply
@@ -892,7 +898,8 @@ void Create(Window::Notifications::System *system) {
return;
}
GetServerInformation([=](const std::optional<ServerInformation> &result) {
GetServerInformation([=](
const std::optional<ServerInformation> &result) {
CurrentServerInformation = result;
oneReady();
});

View File

@@ -119,11 +119,13 @@ namespace {
constexpr auto kDesktopFile = ":/misc/org.telegram.desktop.desktop"_cs;
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
void PortalAutostart(bool start, bool silent) {
bool PortalAutostart(bool start, bool silent) {
if (cExeName().isEmpty()) {
return;
return false;
}
auto error = false;
try {
const auto connection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::SESSION);
@@ -182,14 +184,18 @@ void PortalAutostart(bool start, bool silent) {
const auto response = base::Platform::GlibVariantCast<
uint>(parameters.get_child(0));
if (response && !silent) {
LOG(("Portal Autostart Error: Request denied"));
if (response) {
if (!silent) {
LOG(("Portal Autostart Error: Request denied"));
}
error = true;
}
} catch (const std::exception &e) {
if (!silent) {
LOG(("Portal Autostart Error: %1").arg(
QString::fromStdString(e.what())));
}
error = true;
}
loop->quit();
@@ -227,7 +233,10 @@ void PortalAutostart(bool start, bool silent) {
LOG(("Portal Autostart Error: %1").arg(
QString::fromStdString(e.what())));
}
error = true;
}
return !error;
}
void LaunchGApplication() {
@@ -264,7 +273,8 @@ void LaunchGApplication() {
if (ranges::contains(
activatableNames,
"org.freedesktop.Notifications")) {
"org.freedesktop.Notifications",
&Glib::ustring::raw)) {
return true;
}
@@ -619,25 +629,31 @@ bool AutostartSupported() {
void AutostartToggle(bool enabled, Fn<void(bool)> done) {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
const auto guard = gsl::finally([&] {
if (done) {
done(enabled);
}
});
const auto success = [&] {
const auto silent = !done;
if (KSandbox::isFlatpak()) {
return PortalAutostart(enabled, silent);
}
const auto silent = !done;
if (KSandbox::isFlatpak()) {
PortalAutostart(enabled, silent);
} else {
const auto autostart = QStandardPaths::writableLocation(
QStandardPaths::GenericConfigLocation)
+ u"/autostart/"_q;
if (enabled) {
GenerateDesktopFile(autostart, { u"-autostart"_q }, true, silent);
} else {
QFile::remove(autostart + QGuiApplication::desktopFileName());
if (!enabled) {
return QFile::remove(
autostart + QGuiApplication::desktopFileName());
}
return GenerateDesktopFile(
autostart,
{ u"-autostart"_q },
true,
silent);
}();
if (done) {
done(enabled && success);
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
}

View File

@@ -142,51 +142,78 @@ void DeleteMyModules() {
RemoveDirectory(modules.c_str());
}
void ManageAppLink(bool create, bool silent, int path_csidl, const wchar_t *args, const wchar_t *description) {
bool ManageAppLink(
bool create,
bool silent,
const GUID &folderId,
const wchar_t *args,
const wchar_t *description) {
if (cExeName().isEmpty()) {
return;
return false;
}
WCHAR startupFolder[MAX_PATH];
HRESULT hr = SHGetFolderPath(0, path_csidl, 0, SHGFP_TYPE_CURRENT, startupFolder);
if (SUCCEEDED(hr)) {
QString lnk = QString::fromWCharArray(startupFolder) + '\\' + AppFile.utf16() + u".lnk"_q;
if (create) {
const auto shellLink = base::WinRT::TryCreateInstance<IShellLink>(
CLSID_ShellLink,
CLSCTX_INPROC_SERVER);
if (shellLink) {
QString exe = QDir::toNativeSeparators(cExeDir() + cExeName()), dir = QDir::toNativeSeparators(QDir(cWorkingDir()).absolutePath());
shellLink->SetArguments(args);
shellLink->SetPath(exe.toStdWString().c_str());
shellLink->SetWorkingDirectory(dir.toStdWString().c_str());
shellLink->SetDescription(description);
if (const auto propertyStore = shellLink.try_as<IPropertyStore>()) {
PROPVARIANT appIdPropVar;
hr = InitPropVariantFromString(AppUserModelId::getId(), &appIdPropVar);
if (SUCCEEDED(hr)) {
hr = propertyStore->SetValue(AppUserModelId::getKey(), appIdPropVar);
PropVariantClear(&appIdPropVar);
if (SUCCEEDED(hr)) {
hr = propertyStore->Commit();
}
}
}
if (const auto persistFile = shellLink.try_as<IPersistFile>()) {
hr = persistFile->Save(lnk.toStdWString().c_str(), TRUE);
} else {
if (!silent) LOG(("App Error: could not create interface IID_IPersistFile %1").arg(hr));
}
} else {
if (!silent) LOG(("App Error: could not create instance of IID_IShellLink %1").arg(hr));
}
} else {
QFile::remove(lnk);
PWSTR startupFolder;
HRESULT hr = SHGetKnownFolderPath(
folderId,
KF_FLAG_CREATE,
nullptr,
&startupFolder);
const auto guard = gsl::finally([&] {
CoTaskMemFree(startupFolder);
});
if (!SUCCEEDED(hr)) {
WCHAR buffer[64];
const auto size = base::array_size(buffer) - 1;
const auto length = StringFromGUID2(folderId, buffer, size);
if (length > 0 && length <= size) {
buffer[length] = 0;
if (!silent) LOG(("App Error: could not get %1 folder: %2").arg(buffer).arg(hr));
}
} else {
if (!silent) LOG(("App Error: could not get CSIDL %1 folder %2").arg(path_csidl).arg(hr));
return false;
}
const auto lnk = QString::fromWCharArray(startupFolder)
+ '\\'
+ AppFile.utf16()
+ u".lnk"_q;
if (!create) {
QFile::remove(lnk);
return true;
}
const auto shellLink = base::WinRT::TryCreateInstance<IShellLink>(
CLSID_ShellLink,
CLSCTX_INPROC_SERVER);
if (!shellLink) {
if (!silent) LOG(("App Error: could not create instance of IID_IShellLink %1").arg(hr));
return false;
}
QString exe = QDir::toNativeSeparators(cExeDir() + cExeName()), dir = QDir::toNativeSeparators(QDir(cWorkingDir()).absolutePath());
shellLink->SetArguments(args);
shellLink->SetPath(exe.toStdWString().c_str());
shellLink->SetWorkingDirectory(dir.toStdWString().c_str());
shellLink->SetDescription(description);
if (const auto propertyStore = shellLink.try_as<IPropertyStore>()) {
PROPVARIANT appIdPropVar;
hr = InitPropVariantFromString(AppUserModelId::getId(), &appIdPropVar);
if (SUCCEEDED(hr)) {
hr = propertyStore->SetValue(AppUserModelId::getKey(), appIdPropVar);
PropVariantClear(&appIdPropVar);
if (SUCCEEDED(hr)) {
hr = propertyStore->Commit();
}
}
}
const auto persistFile = shellLink.try_as<IPersistFile>();
if (!persistFile) {
if (!silent) LOG(("App Error: could not create interface IID_IPersistFile %1").arg(hr));
return false;
}
hr = persistFile->Save(lnk.toStdWString().c_str(), TRUE);
if (!SUCCEEDED(hr)) {
if (!silent) LOG(("App Error: could not save IPersistFile to path %1").arg(lnk));
return false;
}
return true;
}
} // namespace
@@ -415,9 +442,15 @@ void AutostartToggle(bool enabled, Fn<void(bool)> done) {
done ? Fn<void(bool)>(callback) : nullptr);
#else // OS_WIN_STORE
const auto silent = !done;
ManageAppLink(enabled, silent, CSIDL_STARTUP, L"-autostart", L"Telegram autorun link.\nYou can disable autorun in Telegram settings.");
const auto success = ManageAppLink(
enabled,
silent,
FOLDERID_Startup,
L"-autostart",
L"Telegram autorun link.\n"
"You can disable autorun in Telegram settings.");
if (done) {
done(enabled);
done(enabled && success);
}
#endif // OS_WIN_STORE
}
@@ -586,7 +619,13 @@ void NewVersionLaunched(int oldVersion) {
} // namespace Platform
void psSendToMenu(bool send, bool silent) {
ManageAppLink(send, silent, CSIDL_SENDTO, L"-sendpath", L"Telegram send to link.\nYou can disable send to menu item in Telegram settings.");
ManageAppLink(
send,
silent,
FOLDERID_SendTo,
L"-sendpath",
L"Telegram send to link.\n"
"You can disable send to menu item in Telegram settings.");
}
bool psLaunchMaps(const Data::LocationPoint &point) {

View File

@@ -500,10 +500,17 @@ void SetupSystemIntegrationContent(
) | rpl::filter([](bool checked) {
return (checked != cAutoStart());
}) | rpl::start_with_next([=](bool checked) {
const auto weak = base::make_weak(controller);
cSetAutoStart(checked);
Platform::AutostartToggle(checked, crl::guard(autostart, [=](
bool enabled) {
autostart->setChecked(enabled);
if (checked && !enabled && weak) {
weak->window().showToast(
Lang::Hard::AutostartEnableError());
}
Ui::PostponeCall(autostart, [=] {
autostart->setChecked(enabled);
});
if (enabled || !minimized->entity()->checked()) {
Local::writeSettings();
} else {

View File

@@ -1055,7 +1055,9 @@ void SetupDataStorage(
auto pathtext = Core::App().settings().downloadPathValue(
) | rpl::map([](const QString &text) {
if (text.isEmpty()) {
return tr::lng_download_path_default(tr::now);
return Core::App().canReadDefaultDownloadPath(true)
? tr::lng_download_path_default(tr::now)
: tr::lng_download_path_unset(tr::now);
} else if (text == FileDialog::Tmp()) {
return tr::lng_download_path_temp(tr::now);
}

View File

@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "window/window_controller.h"
#include "settings/settings_common.h"
#include "storage/localimageloader.h"
#include "styles/style_settings.h"
#include "styles/style_layers.h"
@@ -142,6 +143,7 @@ void SetupExperimental(
addToggle(Ui::GL::kOptionAllowLinuxNvidiaOpenGL);
addToggle(Ui::kOptionUseSmallMsgBubbleRadius);
addToggle(Media::Player::kOptionDisableAutoplayNext);
addToggle(kOptionSendLargePhotos);
addToggle(Settings::kOptionMonoSettingsIcons);
addToggle(Webview::kOptionWebviewDebugEnabled);
addToggle(kOptionAutoScrollInactiveChat);

View File

@@ -1163,9 +1163,10 @@ object_ptr<Ui::RpWidget> ProfilePhotoPrivacyController::setupBelowWidget(
_saveAdditional = [=] {
if (removeButton->isHidden()) {
const auto photoId = SyncUserFallbackPhotoViewer(self);
if (const auto photo = self->owner().photo(*photoId)) {
controller->session().api().peerPhoto().clear(photo);
if (const auto photoId = SyncUserFallbackPhotoViewer(self)) {
if (const auto photo = self->owner().photo(*photoId)) {
controller->session().api().peerPhoto().clear(photo);
}
}
} else if (!state->localOriginal.isNull()) {
controller->session().api().peerPhoto().uploadFallback(

View File

@@ -280,22 +280,27 @@ void SetupPrivacy(
{ &st::settingsIconOnline, kIconLightBlue },
Key::LastSeen,
[=] { return std::make_unique<LastSeenPrivacyController>(session); });
add(
tr::lng_settings_profile_photo_privacy(),
{ &st::settingsIconAccount, kIconRed },
Key::ProfilePhoto,
[] { return std::make_unique<ProfilePhotoPrivacyController>(); });
add(
tr::lng_settings_forwards_privacy(),
{ &st::settingsIconForward, kIconLightOrange },
Key::Forwards,
[=] { return std::make_unique<ForwardsPrivacyController>(
controller); });
add(
tr::lng_settings_profile_photo_privacy(),
{ &st::settingsIconAccount, kIconRed },
Key::ProfilePhoto,
[] { return std::make_unique<ProfilePhotoPrivacyController>(); });
add(
tr::lng_settings_calls(),
{ &st::settingsIconVideoCalls, kIconGreen },
Key::Calls,
[] { return std::make_unique<CallsPrivacyController>(); });
add(
tr::lng_settings_groups_invite(),
{ &st::settingsIconGroup, kIconDarkBlue },
Key::Invites,
[] { return std::make_unique<GroupsInvitePrivacyController>(); });
AddPremiumPrivacyButton(
controller,
container,
@@ -303,16 +308,11 @@ void SetupPrivacy(
{ &st::settingsPremiumIconVoice, kIconRed },
Key::Voices,
[=] { return std::make_unique<VoicesPrivacyController>(session); });
add(
tr::lng_settings_groups_invite(),
{ &st::settingsIconGroup, kIconDarkBlue },
Key::Invites,
[] { return std::make_unique<GroupsInvitePrivacyController>(); });
session->api().userPrivacy().reload(Api::UserPrivacy::Key::AddedByPhone);
AddSkip(container, st::settingsPrivacySecurityPadding);
AddDividerText(container, tr::lng_settings_group_privacy_about());
AddDivider(container);
}
void SetupArchiveAndMute(
@@ -865,10 +865,10 @@ void PrivacySecurity::setupContent(
return rpl::duplicate(updateOnTick);
};
SetupPrivacy(controller, content, trigger());
SetupSecurity(controller, content, trigger(), [=](Type type) {
_showOther.fire_copy(type);
});
SetupPrivacy(controller, content, trigger());
#if !defined OS_MAC_STORE && !defined OS_WIN_STORE
SetupSensitiveContent(controller, content, trigger());
#else // !OS_MAC_STORE && !OS_WIN_STORE

View File

@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h"
#include "core/file_utilities.h"
#include "core/mime_type.h"
#include "base/options.h"
#include "base/unixtime.h"
#include "base/random.h"
#include "editor/scene/scene_item_sticker.h"
@@ -49,6 +50,13 @@ constexpr auto kRecompressAfterBpp = 4;
using Ui::ValidateThumbDimensions;
base::options::toggle SendLargePhotos({
.id = kOptionSendLargePhotos,
.name = "Send large photos",
.description = "Increase the side limit on compressed images to 2560px.",
});
std::atomic<bool> SendLargePhotosAtomic/* = false*/;
struct PreparedFileThumbnail {
uint64 id = 0;
QString name;
@@ -191,8 +199,22 @@ struct PreparedFileThumbnail {
return result;
}
[[nodiscard]] int PhotoSideLimit(bool large) {
return large ? 2560 : 1280;
}
[[nodiscard]] int PhotoSideLimitAtomic() {
return PhotoSideLimit(SendLargePhotosAtomic.load());
}
} // namespace
const char kOptionSendLargePhotos[] = "send-large-photos";
int PhotoSideLimit() {
return PhotoSideLimit(SendLargePhotos.value());
}
SendMediaPrepare::SendMediaPrepare(
const QString &file,
const PeerId &peer,
@@ -518,7 +540,6 @@ void FileLoadResult::setThumbData(const QByteArray &thumbdata) {
}
}
FileLoadTask::FileLoadTask(
not_null<Main::Session*> session,
const QString &filepath,
@@ -543,6 +564,8 @@ FileLoadTask::FileLoadTask(
Expects(to.options.scheduled
|| !to.replaceMediaOf
|| IsServerMsgId(to.replaceMediaOf));
SendLargePhotosAtomic = SendLargePhotos.value();
}
FileLoadTask::FileLoadTask(
@@ -942,8 +965,9 @@ void FileLoadTask::process(Args &&args) {
}
auto medium = (w > 320 || h > 320) ? fullimage.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage;
const auto downscaled = (w > 1280 || h > 1280);
auto full = downscaled ? fullimage.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage;
const auto limit = PhotoSideLimitAtomic();
const auto downscaled = (w > limit || h > limit);
auto full = downscaled ? fullimage.scaled(limit, limit, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage;
if (downscaled) {
fullimagebytes = fullimageformat = QByteArray();
}

View File

@@ -24,6 +24,10 @@ constexpr auto kFileSizeLimit = 2'000 * int64(1024 * 1024);
// Load files up to 4'000 MB.
constexpr auto kFileSizePremiumLimit = 4'000 * int64(1024 * 1024);
extern const char kOptionSendLargePhotos[];
[[nodiscard]] int PhotoSideLimit();
enum class SendMediaType {
Photo,
Audio,

View File

@@ -47,13 +47,10 @@ bool ValidVideoForAlbum(const PreparedFileInformation::Video &video) {
return Ui::ValidateThumbDimensions(width, height);
}
QSize PrepareShownDimensions(const QImage &preview) {
constexpr auto kMaxWidth = 1280;
constexpr auto kMaxHeight = 1280;
QSize PrepareShownDimensions(const QImage &preview, int sideLimit) {
const auto result = preview.size();
return (result.width() > kMaxWidth || result.height() > kMaxHeight)
? result.scaled(kMaxWidth, kMaxHeight, Qt::KeepAspectRatio)
return (result.width() > sideLimit || result.height() > sideLimit)
? result.scaled(sideLimit, sideLimit, Qt::KeepAspectRatio)
: result;
}
@@ -63,10 +60,11 @@ void PrepareDetailsInParallel(PreparedList &result, int previewWidth) {
if (result.files.empty()) {
return;
}
const auto sideLimit = PhotoSideLimit(); // Get on main thread.
QSemaphore semaphore;
for (auto &file : result.files) {
crl::async([=, &semaphore, &file] {
PrepareDetails(file, previewWidth);
PrepareDetails(file, previewWidth, sideLimit);
semaphore.release();
});
}
@@ -272,7 +270,7 @@ std::optional<PreparedList> PreparedFileFromFilesDialog(
}
}
void PrepareDetails(PreparedFile &file, int previewWidth) {
void PrepareDetails(PreparedFile &file, int previewWidth, int sideLimit) {
if (!file.path.isEmpty()) {
file.information = FileLoadTask::ReadMediaInformation(
file.path,
@@ -293,7 +291,7 @@ void PrepareDetails(PreparedFile &file, int previewWidth) {
&file.information->media)) {
Assert(!image->data.isNull());
if (ValidPhotoForAlbum(*image, file.information->filemime)) {
UpdateImageDetails(file, previewWidth);
UpdateImageDetails(file, previewWidth, sideLimit);
file.type = PreparedFile::Type::Photo;
} else if (image->animated) {
file.type = PreparedFile::Type::None;
@@ -303,7 +301,10 @@ void PrepareDetails(PreparedFile &file, int previewWidth) {
if (ValidVideoForAlbum(*video)) {
auto blurred = Images::Blur(
Images::Opaque(base::duplicate(video->thumbnail)));
file.shownDimensions = PrepareShownDimensions(video->thumbnail);
file.originalDimensions = video->thumbnail.size();
file.shownDimensions = PrepareShownDimensions(
video->thumbnail,
sideLimit);
file.preview = std::move(blurred).scaledToWidth(
previewWidth * cIntRetinaFactor(),
Qt::SmoothTransformation);
@@ -316,7 +317,10 @@ void PrepareDetails(PreparedFile &file, int previewWidth) {
}
}
void UpdateImageDetails(PreparedFile &file, int previewWidth) {
void UpdateImageDetails(
PreparedFile &file,
int previewWidth,
int sideLimit) {
const auto image = std::get_if<Image>(&file.information->media);
if (!image) {
return;
@@ -326,7 +330,8 @@ void UpdateImageDetails(PreparedFile &file, int previewWidth) {
? Editor::ImageModified(image->data, image->modifications)
: image->data;
Assert(!preview.isNull());
file.shownDimensions = PrepareShownDimensions(preview);
file.originalDimensions = preview.size();
file.shownDimensions = PrepareShownDimensions(preview, sideLimit);
const auto toWidth = std::min(
previewWidth,
style::ConvertScale(preview.width())

View File

@@ -51,8 +51,11 @@ enum class MimeDataState {
QImage &&image,
QByteArray &&content,
int previewWidth);
void PrepareDetails(Ui::PreparedFile &file, int previewWidth);
void UpdateImageDetails(Ui::PreparedFile &file, int previewWidth);
void PrepareDetails(Ui::PreparedFile &file, int previewWidth, int sideLimit);
void UpdateImageDetails(
Ui::PreparedFile &file,
int previewWidth,
int sideLimit);
bool ApplyModifications(Ui::PreparedList &list);

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