Compare commits

...

46 Commits

Author SHA1 Message Date
John Preston
08fa6a9815 Version 4.5.3.
- Attempt to fix incoming video in calls from mobile apps.
2023-01-06 21:57:04 +04:00
John Preston
a7cf4027ea Attempt to fix calls incoming video. 2023-01-06 21:55:08 +04:00
Ilya Fedin
646c7ecceb Update Qt version for Linux in cmake 2023-01-06 12:57:54 +04:00
Ilya Fedin
3cbbe3d3c2 Update Qt to 6.4.2 on Linux 2023-01-05 23:00:13 +04:00
John Preston
0af26dd353 Capture mouse in PipeWire screen capture. 2023-01-05 10:09:44 +04:00
John Preston
159e366122 Choose screens/windows in Wayland screencapture.
Fixes #25674.
2023-01-05 09:55:46 +04:00
John Preston
b9081c26ba Use tg://settings/edit_profile instead /information. 2023-01-05 09:55:38 +04:00
John Preston
9933c6ba59 Mark topics as read using Ctrl+R shortcut.
Fixes #25669.
2023-01-05 09:53:51 +04:00
John Preston
eb0642f569 Version 4.5.2.
- Fix unread reactions button in private chats.
- Fix tile background saving after an app update.
- Allow Ctrl+6,7,8 to activate extra pinned chats.
2023-01-03 11:11:02 +04:00
John Preston
1cce35a5a5 Fix multiline checkbox geometry counting. 2023-01-03 11:06:40 +04:00
John Preston
aeb71e089a Fix tile background saving after an app update.
Fixes #25666, I hope fixes #16468, I hope fixes #5944.
2023-01-03 10:43:55 +04:00
John Preston
b962efeca3 Allow ctrl+6/7/8 to activate extra pinned chats.
Fixes #25647.
2023-01-03 09:59:42 +04:00
John Preston
eb6c350e72 Fix unread reactions button in chats with users.
Regression was introduced in 6a7f030ee7.

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

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

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

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

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

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

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

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

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

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

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

View File

@@ -60,7 +60,7 @@ if (NOT DESKTOP_APP_USE_PACKAGED)
elseif (APPLE)
set(qt_version 6.3.2)
else()
set(qt_version 6.4.1)
set(qt_version 6.4.2)
endif()
endif()
include(cmake/external/qt/package.cmake)

2
LEGAL
View File

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

View File

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

View File

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

View File

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

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

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

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

@@ -869,7 +869,7 @@ const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
ResolvePrivatePost
},
{
u"^settings(/language|/devices|/folders|/privacy|/themes|/change_number|/auto_delete|/information)?$"_q,
u"^settings(/language|/devices|/folders|/privacy|/themes|/change_number|/auto_delete|/information|/edit_profile)?$"_q,
ResolveSettings
},
{

View File

@@ -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 = 4004003;
constexpr auto AppVersionStr = "4.4.3";
constexpr auto AppBetaVersion = true;
constexpr auto AppVersion = 4005003;
constexpr auto AppVersionStr = "4.5.3";
constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

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,
@@ -3791,14 +3794,14 @@ void InnerWidget::setupShortcuts() {
});
request->check(Command::ReadChat) && request->handle([=] {
const auto history = _selected ? _selected->history() : nullptr;
if (history) {
if (history->chatListBadgesState().unread) {
session().data().histories().readInbox(history);
}
return true;
const auto thread = _selected ? _selected->thread() : nullptr;
if (!thread) {
return false;
}
return (history != nullptr);
if (Window::IsUnreadThread(thread)) {
Window::MarkAsReadThread(thread);
}
return true;
});
request->check(Command::ShowContacts) && request->handle([=] {

View File

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

@@ -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() {
@@ -620,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

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

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

View File

@@ -81,8 +81,7 @@ AlbumThumbnail::AlbumThumbnail(
const auto filepath = file.path;
if (filepath.isEmpty()) {
_name = "image.png";
_status = FormatImageSizeText(_fullPreview.size()
/ _fullPreview.devicePixelRatio());
_status = FormatImageSizeText(file.originalDimensions);
} else {
auto fileinfo = QFileInfo(filepath);
_name = fileinfo.fileName();

View File

@@ -103,7 +103,6 @@ void ItemSingleFilePreview::preparePreview(not_null<DocumentData*> document) {
data.name = Text::FormatSongName(filename, songTitle, songPerformer)
.string();
data.statusText = FormatSizeText(document->size);
} else {
data.name = document->filename();
}

View File

@@ -80,6 +80,7 @@ struct PreparedFile {
std::unique_ptr<Ui::PreparedFileInformation> information;
QImage preview;
QSize shownDimensions;
QSize originalDimensions;
Type type = Type::File;
bool spoiler = false;
};

View File

@@ -41,8 +41,7 @@ void SingleFilePreview::preparePreview(const PreparedFile &file) {
if (filepath.isEmpty()) {
auto filename = "image.png";
data.name = filename;
data.statusText = FormatImageSizeText(preview.size()
/ preview.devicePixelRatio());
data.statusText = FormatImageSizeText(file.originalDimensions);
data.fileIsImage = true;
} else {
auto fileinfo = QFileInfo(filepath);

View File

@@ -140,6 +140,7 @@ void MessageBar::tweenTo(MessageBarContent &&content) {
? RectPart::Bottom
: RectPart::None;
animation.imageFrom = grabImagePart();
animation.spoilerFrom = std::move(_spoiler);
animation.bodyOrTextFrom = grabBodyOrTextPart(animation.bodyAnimation);
const auto sameLength = SameFirstPartLength(
_content.title,
@@ -208,6 +209,12 @@ void MessageBar::updateFromContent(MessageBarContent &&content) {
Ui::DialogTextOptions(),
_content.context);
_image = prepareImage(_content.preview);
if (!_content.spoilerRepaint) {
_spoiler = nullptr;
} else if (!_spoiler) {
_spoiler = std::make_unique<SpoilerAnimation>(
_content.spoilerRepaint);
}
}
QRect MessageBar::imageRect() const {
@@ -258,10 +265,21 @@ auto MessageBar::makeGrabGuard() {
auto imageShown = _animation
? std::move(_animation->imageShown)
: Ui::Animations::Simple();
return gsl::finally([&, shown = std::move(imageShown)]() mutable {
auto spoiler = std::move(_spoiler);
auto fromSpoiler = _animation
? std::move(_animation->spoilerFrom)
: nullptr;
return gsl::finally([
&,
shown = std::move(imageShown),
spoiler = std::move(spoiler),
fromSpoiler = std::move(fromSpoiler)
]() mutable {
if (_animation) {
_animation->imageShown = std::move(shown);
_animation->spoilerFrom = std::move(fromSpoiler);
}
_spoiler = std::move(spoiler);
});
}
@@ -358,12 +376,20 @@ void MessageBar::paint(Painter &p) {
: (_animation->movingTo == RectPart::Top)
? (shiftTo - shiftFull)
: (shiftTo + shiftFull);
const auto now = crl::now();
const auto paused = p.inactive();
paintLeftBar(p);
if (!_animation) {
if (!_image.isNull()) {
p.drawPixmap(image, _image);
paintImageWithSpoiler(
p,
image,
_image,
_spoiler.get(),
now,
paused);
}
} else if (!_animation->imageTo.isNull()
|| (!_animation->imageFrom.isNull()
@@ -381,14 +407,30 @@ void MessageBar::paint(Painter &p) {
}();
if (_animation->bodyMoved.animating()) {
p.setOpacity(1. - progress);
p.drawPixmap(
paintImageWithSpoiler(
p,
rect.translated(0, shiftFrom),
_animation->imageFrom);
_animation->imageFrom,
_animation->spoilerFrom.get(),
now,
paused);
p.setOpacity(progress);
p.drawPixmap(rect.translated(0, shiftTo), _animation->imageTo);
paintImageWithSpoiler(
p,
rect.translated(0, shiftTo),
_animation->imageTo,
_spoiler.get(),
now,
paused);
p.setOpacity(1.);
} else {
p.drawPixmap(rect, _image);
paintImageWithSpoiler(
p,
rect,
_image,
_spoiler.get(),
now,
paused);
}
}
if (!_animation || _animation->bodyAnimation == BodyAnimation::None) {
@@ -409,8 +451,8 @@ void MessageBar::paint(Painter &p) {
.availableWidth = body.width(),
.palette = &_st.textPalette,
.spoiler = Ui::Text::DefaultSpoilerCache(),
.now = crl::now(),
.paused = p.inactive(),
.now = now,
.paused = paused,
.elisionLines = 1,
});
}
@@ -510,6 +552,21 @@ void MessageBar::ensureGradientsCreated(int size) {
_topBarGradient = Images::PixmapFast(std::move(top));
}
void MessageBar::paintImageWithSpoiler(
QPainter &p,
QRect rect,
const QPixmap &image,
SpoilerAnimation *spoiler,
crl::time now,
bool paused) const {
p.drawPixmap(rect, image);
if (spoiler) {
const auto frame = DefaultImageSpoiler().frame(
spoiler->index(now, paused));
FillSpoilerRect(p, rect, frame);
}
}
void MessageBar::paintLeftBar(Painter &p) {
const auto state = countBarState();
const auto gradientSize = int(std::ceil(state.size * 2.5));

View File

@@ -18,6 +18,8 @@ struct MessageBar;
namespace Ui {
class SpoilerAnimation;
struct MessageBarContent {
int index = 0;
int count = 1;
@@ -25,6 +27,7 @@ struct MessageBarContent {
TextWithEntities text;
std::any context;
QImage preview;
Fn<void()> spoilerRepaint;
style::margins margins;
};
@@ -38,7 +41,7 @@ public:
void set(MessageBarContent &&content);
void set(rpl::producer<MessageBarContent> content);
[[nodiscard]] not_null<Ui::RpWidget*> widget() {
[[nodiscard]] not_null<RpWidget*> widget() {
return &_widget;
}
@@ -52,10 +55,10 @@ private:
None,
};
struct Animation {
Ui::Animations::Simple bodyMoved;
Ui::Animations::Simple imageShown;
Ui::Animations::Simple barScroll;
Ui::Animations::Simple barTop;
Animations::Simple bodyMoved;
Animations::Simple imageShown;
Animations::Simple barScroll;
Animations::Simple barTop;
QPixmap bodyOrTextFrom;
QPixmap bodyOrTextTo;
QPixmap titleSame;
@@ -63,6 +66,7 @@ private:
QPixmap titleTo;
QPixmap imageFrom;
QPixmap imageTo;
std::unique_ptr<SpoilerAnimation> spoilerFrom;
BodyAnimation bodyAnimation = BodyAnimation::None;
RectPart movingTo = RectPart::None;
};
@@ -98,19 +102,28 @@ private:
[[nodiscard]] BarState countBarState() const;
void ensureGradientsCreated(int size);
void paintImageWithSpoiler(
QPainter &p,
QRect rect,
const QPixmap &image,
SpoilerAnimation *spoiler,
crl::time now,
bool paused) const;
[[nodiscard]] static BodyAnimation DetectBodyAnimationType(
Animation *currentAnimation,
const MessageBarContent &currentContent,
const MessageBarContent &nextContent);
const style::MessageBar &_st;
Ui::RpWidget _widget;
RpWidget _widget;
Fn<bool()> _customEmojiPaused;
MessageBarContent _content;
rpl::lifetime _contentLifetime;
Ui::Text::String _title, _text;
Text::String _title, _text;
QPixmap _image, _topBarGradient, _bottomBarGradient;
std::unique_ptr<Animation> _animation;
std::unique_ptr<SpoilerAnimation> _spoiler;
bool _customEmojiRepaintScheduled = false;
};

View File

@@ -18,7 +18,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
PinnedBar::PinnedBar(not_null<QWidget*> parent, Fn<bool()> customEmojiPaused)
PinnedBar::PinnedBar(
not_null<QWidget*> parent,
Fn<bool()> customEmojiPaused,
rpl::producer<> customEmojiPausedChanges)
: _wrap(parent, object_ptr<RpWidget>(parent))
, _shadow(std::make_unique<PlainShadow>(_wrap.parentWidget()))
, _customEmojiPaused(std::move(customEmojiPaused)) {
@@ -30,6 +33,14 @@ PinnedBar::PinnedBar(not_null<QWidget*> parent, Fn<bool()> customEmojiPaused)
QPainter(_wrap.entity()).fillRect(clip, st::historyPinnedBg);
}, lifetime());
_wrap.setAttribute(Qt::WA_OpaquePaintEvent);
if (customEmojiPausedChanges) {
std::move(
customEmojiPausedChanges
) | rpl::start_with_next([=] {
_wrap.entity()->update();
}, lifetime());
}
}
PinnedBar::~PinnedBar() {

View File

@@ -22,7 +22,10 @@ class RpWidget;
class PinnedBar final {
public:
PinnedBar(not_null<QWidget*> parent, Fn<bool()> customEmojiPaused);
PinnedBar(
not_null<QWidget*> parent,
Fn<bool()> customEmojiPaused,
rpl::producer<> customEmojiPausedChanges);
~PinnedBar();
void show();

View File

@@ -51,8 +51,8 @@ constexpr auto kSaveWindowPositionTimeout = crl::time(1000);
base::options::toggle ShowChatNameInNewWindow({
.id = kOptionShowChatNameInNewWindow,
.name = "Show chat name in title of separated windows",
.description = "",
.name = "Chat name in window title",
.description = "Show chat name in the additional windows titles.",
});
} // namespace

View File

@@ -923,11 +923,21 @@ QImage ChatBackground::createCurrentImage() const {
}
bool ChatBackground::tile() const {
if (!started()) {
const auto &set = nightMode()
? _localStoredTileNightValue
: _localStoredTileDayValue;
if (set.has_value()) {
return *set;
}
}
return nightMode() ? _tileNightValue : _tileDayValue;
}
bool ChatBackground::tileDay() const {
if (Data::details::IsTestingThemeWallPaper(_paper) ||
if (!started() && _localStoredTileDayValue.has_value()) {
return *_localStoredTileDayValue;
} else if (Data::details::IsTestingThemeWallPaper(_paper) ||
Data::details::IsTestingDefaultWallPaper(_paper)) {
if (!nightMode()) {
return _tileForRevert;
@@ -937,7 +947,9 @@ bool ChatBackground::tileDay() const {
}
bool ChatBackground::tileNight() const {
if (Data::details::IsTestingThemeWallPaper(_paper) ||
if (!started() && _localStoredTileNightValue.has_value()) {
return *_localStoredTileNightValue;
} else if (Data::details::IsTestingThemeWallPaper(_paper) ||
Data::details::IsTestingDefaultWallPaper(_paper)) {
if (nightMode()) {
return _tileForRevert;

View File

@@ -161,31 +161,6 @@ void SetActionText(not_null<QAction*> action, rpl::producer<QString> &&text) {
}, *lifetime);
}
[[nodiscard]] bool IsUnreadThread(not_null<Data::Thread*> thread) {
return thread->chatListBadgesState().unread;
}
void MarkAsReadThread(not_null<Data::Thread*> thread) {
const auto readHistory = [&](not_null<History*> history) {
history->owner().histories().readInbox(history);
};
if (!IsUnreadThread(thread)) {
return;
} else if (const auto forum = thread->asForum()) {
forum->enumerateTopics([](
not_null<Data::ForumTopic*> topic) {
MarkAsReadThread(topic);
});
} else if (const auto history = thread->asHistory()) {
readHistory(history);
if (const auto migrated = history->migrateSibling()) {
readHistory(migrated);
}
} else if (const auto topic = thread->asTopic()) {
topic->readTillEnd();
}
}
void MarkAsReadChatList(not_null<Dialogs::MainList*> list) {
auto mark = std::vector<not_null<History*>>();
for (const auto &row : list->indexed()->all()) {
@@ -2391,4 +2366,29 @@ bool FillVideoChatMenu(
return has || manager;
}
bool IsUnreadThread(not_null<Data::Thread*> thread) {
return thread->chatListBadgesState().unread;
}
void MarkAsReadThread(not_null<Data::Thread*> thread) {
const auto readHistory = [&](not_null<History*> history) {
history->owner().histories().readInbox(history);
};
if (!IsUnreadThread(thread)) {
return;
} else if (const auto forum = thread->asForum()) {
forum->enumerateTopics([](
not_null<Data::ForumTopic*> topic) {
MarkAsReadThread(topic);
});
} else if (const auto history = thread->asHistory()) {
readHistory(history);
if (const auto migrated = history->migrateSibling()) {
readHistory(migrated);
}
} else if (const auto topic = thread->asTopic()) {
topic->readTillEnd();
}
}
} // namespace Window

View File

@@ -161,4 +161,7 @@ void UnpinAllMessages(
not_null<Window::SessionNavigation*> navigation,
not_null<Data::Thread*> thread);
[[nodiscard]] bool IsUnreadThread(not_null<Data::Thread*> thread);
void MarkAsReadThread(not_null<Data::Thread*> thread);
} // namespace Window

View File

@@ -31,6 +31,8 @@
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.social-networking</string>
<key>LSMinimumSystemVersion</key>

View File

@@ -1,8 +1,8 @@
{%- set GIT = "https://github.com" -%}
{%- set GIT_FREEDESKTOP = GIT ~ "/gitlab-freedesktop-mirrors" -%}
{%- set QT = "6_4_1" -%}
{%- set QT_TAG = "v6.4.1" -%}
{%- set QT_PREFIX = "/usr/local/desktop-app/Qt-6.4.1" -%}
{%- set QT = "6_4_2" -%}
{%- set QT_TAG = "v6.4.2" -%}
{%- set QT_PREFIX = "/usr/local/desktop-app/Qt-6.4.2" -%}
{%- set OPENSSL_VER = "1_1_1" -%}
{%- set OPENSSL_PREFIX = "/usr/local/desktop-app/openssl-1.1.1" -%}
{%- set CMAKE_VER = "3.24.3" -%}
@@ -56,7 +56,7 @@ ENV CXXFLAGS $CFLAGS
FROM builder AS patches
RUN git clone {{ GIT }}/desktop-app/patches.git \
&& cd patches \
&& git checkout 4ecd75fad8 \
&& git checkout 614ff3aebd \
&& rm -rf .git
FROM builder AS nasm
@@ -157,7 +157,7 @@ RUN git clone -b v1.0.9 --depth=1 {{ GIT }}/google/brotli.git \
&& rm -rf brotli
FROM builder AS highway
RUN git clone -b 1.0.1 --depth=1 {{ GIT }}/google/highway.git \
RUN git clone -b 1.0.2 --depth=1 {{ GIT }}/google/highway.git \
&& cd highway \
&& cmake -GNinja -B build . \
-DCMAKE_BUILD_TYPE=None \
@@ -208,7 +208,7 @@ RUN git clone -b 1.0.0 --depth=1 {{ GIT }}/videolan/dav1d.git \
&& rm -rf dav1d
FROM builder AS libde265
RUN git clone -b v1.0.8 --depth=1 {{ GIT }}/strukturag/libde265.git \
RUN git clone -b v1.0.9 --depth=1 {{ GIT }}/strukturag/libde265.git \
&& cd libde265 \
&& cmake -GNinja . \
-DCMAKE_BUILD_TYPE=None \
@@ -238,7 +238,7 @@ RUN git clone -b v1.11.0 --depth=1 {{ GIT }}/webmproject/libvpx.git \
FROM builder AS libavif
COPY --link --from=dav1d {{ LibrariesPath }}/dav1d-cache /
RUN git clone -b v0.10.1 --depth=1 {{ GIT }}/AOMediaCodec/libavif.git \
RUN git clone -b v0.11.1 --depth=1 {{ GIT }}/AOMediaCodec/libavif.git \
&& cd libavif \
&& cmake -GNinja -B build . \
-DCMAKE_BUILD_TYPE=None \
@@ -252,7 +252,7 @@ RUN git clone -b v0.10.1 --depth=1 {{ GIT }}/AOMediaCodec/libavif.git \
FROM builder AS libheif
COPY --link --from=libde265 {{ LibrariesPath }}/libde265-cache /
RUN git clone -b v1.13.0 --depth=1 {{ GIT }}/strukturag/libheif.git \
RUN git clone -b v1.14.0 --depth=1 {{ GIT }}/strukturag/libheif.git \
&& cd libheif \
&& cmake -GNinja -B build . \
-DCMAKE_BUILD_TYPE=None \
@@ -752,7 +752,7 @@ COPY --link --from=pipewire {{ LibrariesPath }}/pipewire-cache /
RUN git init tg_owt \
&& cd tg_owt \
&& git remote add origin {{ GIT }}/desktop-app/tg_owt.git \
&& git fetch --depth=1 origin 9b70d7679e86e6c216127d5a6a06ef5aa4f54793 \
&& git fetch --depth=1 origin 5098730b9eb6173f0b52068fe2555b7c1015123a \
&& git reset --hard FETCH_HEAD \
&& git submodule update --init --recursive --depth=1 \
&& rm -rf .git \

View File

@@ -12,8 +12,15 @@ def error(text):
print('[ERROR] ' + text)
finish(1)
def nativeToolsError():
error('Make sure to run from Native Tools Command Prompt.')
win = (sys.platform == 'win32')
mac = (sys.platform == 'darwin')
if win and not 'Platform' in os.environ:
nativeToolsError()
win32 = win and (os.environ['Platform'] == 'x86')
win64 = win and (os.environ['Platform'] == 'x64')
@@ -21,7 +28,7 @@ if win and not 'COMSPEC' in os.environ:
error('COMSPEC environment variable is not set.')
if win and not win32 and not win64:
error('Make sure to run from Native Tools Command Prompt.')
nativeToolsError()
os.chdir(scriptPath + '/../../../..')
@@ -397,7 +404,7 @@ if customRunCommand:
stage('patches', """
git clone https://github.com/desktop-app/patches.git
cd patches
git checkout 8edd80d889
git checkout 9e04e2eb9c
""")
stage('msys64', """
@@ -413,13 +420,13 @@ win:
del msys64.exe
bash -c "pacman-key --init; pacman-key --populate; pacman -Syu --noconfirm"
pacman -S --noconfirm mingw-w64-x86_64-perl mingw-w64-x86_64-nasm mingw-w64-x86_64-yasm mingw-w64-x86_64-ninja
pacman -Syu --noconfirm mingw-w64-x86_64-perl mingw-w64-x86_64-nasm mingw-w64-x86_64-yasm mingw-w64-x86_64-ninja
SET PATH=%PATH_BACKUP_%
""", 'ThirdParty')
stage('python', """
version: """ + (subprocess.run(['python', '-V'], capture_output=True, env=modifiedEnv).stdout.decode().strip().split()[-1] if win else '0') + """
version: """ + (subprocess.run(['python', '-V'], capture_output=True, text=True, env=modifiedEnv).stdout.strip().split()[-1] if win else '0') + """
win:
python -m venv python
python\\Scripts\\activate.bat
@@ -1464,4 +1471,10 @@ win:
# -Dprotobuf_WITH_ZLIB_DEFAULT=OFF
# cmake --build . $MAKE_THREADS_CNT
runStages()
if win:
currentCodePage = subprocess.run('chcp', capture_output=True, shell=True, text=True, env=modifiedEnv).stdout.strip().split()[-1]
subprocess.run('chcp 65001 > nul', shell=True, env=modifiedEnv)
runStages()
subprocess.run('chcp ' + currentCodePage + ' > nul', shell=True, env=modifiedEnv)
else:
runStages()

View File

@@ -1,7 +1,7 @@
AppVersion 4004003
AppVersionStrMajor 4.4
AppVersionStrSmall 4.4.3
AppVersionStr 4.4.3
BetaChannel 1
AppVersion 4005003
AppVersionStrMajor 4.5
AppVersionStrSmall 4.5.3
AppVersionStr 4.5.3
BetaChannel 0
AlphaVersion 0
AppVersionOriginal 4.4.3.beta
AppVersionOriginal 4.5.3

View File

@@ -1,3 +1,27 @@
4.5.3 (06.01.22)
- Attempt to fix incoming video in calls from mobile apps.
4.5.2 (03.01.22)
- 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.
4.5.1 (02.01.22)
- Fix crash in profile photo privacy edition.
- Allow sending photos larger than 1280px (in Experimental Settings).
4.5 (30.12.22)
- 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.
4.4.3 beta (29.12.22)
- Support for anonymous numbers from the Fragment.com platform.

2
cmake

Submodule cmake updated: 4d8c8a0f84...de92292f89

View File

@@ -84,6 +84,10 @@ layout:
/usr/lib/$CRAFT_ARCH_TRIPLET/webkit2gtk-4.1:
bind: $SNAP/usr/lib/$CRAFT_ARCH_TRIPLET/webkit2gtk-4.1
package-repositories:
- type: apt
ppa: kisak/kisak-mesa
parts:
telegram:
plugin: cmake
@@ -445,9 +449,9 @@ parts:
- libxkbcommon-x11-0
- zlib1g
override-pull: |
QT=6_4_1
QT=6_4_2
git clone -b v6.4.1 --depth=1 git://code.qt.io/qt/qt5.git .
git clone -b v6.4.2 --depth=1 git://code.qt.io/qt/qt5.git .
git submodule update --init --recursive --depth=1 qtbase qtdeclarative qtwayland qtimageformats qtsvg qt5compat qtshadertools
cd qtbase
@@ -513,7 +517,7 @@ parts:
webrtc:
source: https://github.com/desktop-app/tg_owt.git
source-depth: 1
source-commit: cc8edd5719556e1711f50da9a3f7a4c59bba574d
source-commit: 5098730b9eb6173f0b52068fe2555b7c1015123a
plugin: cmake
build-packages:
- yasm