Merge tag 'v5.12.3' into dev

This commit is contained in:
AlexeyZavar 2025-03-13 12:55:33 +03:00
commit b0a4d15d64
27 changed files with 316 additions and 61 deletions

View file

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

View file

@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 5,12,1,0 FILEVERSION 5,12,3,0
PRODUCTVERSION 5,12,1,0 PRODUCTVERSION 5,12,3,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -62,10 +62,10 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Radolyn Labs" VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop" VALUE "FileDescription", "AyuGram Desktop"
VALUE "FileVersion", "5.12.1.0" VALUE "FileVersion", "5.12.3.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2025" VALUE "LegalCopyright", "Copyright (C) 2014-2025"
VALUE "ProductName", "AyuGram Desktop" VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "5.12.1.0" VALUE "ProductVersion", "5.12.3.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 5,12,1,0 FILEVERSION 5,12,3,0
PRODUCTVERSION 5,12,1,0 PRODUCTVERSION 5,12,3,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -53,10 +53,10 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Radolyn Labs" VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop Updater" VALUE "FileDescription", "AyuGram Desktop Updater"
VALUE "FileVersion", "5.12.1.0" VALUE "FileVersion", "5.12.3.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2025" VALUE "LegalCopyright", "Copyright (C) 2014-2025"
VALUE "ProductName", "AyuGram Desktop" VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "5.12.1.0" VALUE "ProductVersion", "5.12.3.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -363,15 +363,17 @@ object_ptr<Ui::RpWidget> AddReactionsSelector(
const auto customEmojiPaused = [controller = args.controller] { const auto customEmojiPaused = [controller = args.controller] {
return controller->isGifPausedAtLeastFor(PauseReason::Layer); return controller->isGifPausedAtLeastFor(PauseReason::Layer);
}; };
auto context = Core::TextContext({ auto simpleContext = Core::TextContext({
.session = session, .session = session,
.repaint = [=] { raw->update(); },
}); });
auto context = simpleContext;
context.customEmojiFactory = [=]( context.customEmojiFactory = [=](
QStringView data, QStringView data,
const Ui::Text::MarkedContext &context const Ui::Text::MarkedContext &context
) -> std::unique_ptr<Ui::Text::CustomEmoji> { ) -> std::unique_ptr<Ui::Text::CustomEmoji> {
const auto id = Data::ParseCustomEmojiData(data); const auto id = Data::ParseCustomEmojiData(data);
auto result = Ui::Text::MakeCustomEmoji(data, context); auto result = Ui::Text::MakeCustomEmoji(data, simpleContext);
if (state->unifiedFactoryOwner->lookupReactionId(id).custom()) { if (state->unifiedFactoryOwner->lookupReactionId(id).custom()) {
return std::make_unique<MaybeDisabledEmoji>( return std::make_unique<MaybeDisabledEmoji>(
std::move(result), std::move(result),

View file

@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
constexpr auto AppNameOld = "AyuGram for Windows"_cs; constexpr auto AppNameOld = "AyuGram for Windows"_cs;
constexpr auto AppName = "AyuGram Desktop"_cs; constexpr auto AppName = "AyuGram Desktop"_cs;
constexpr auto AppFile = "AyuGram"_cs; constexpr auto AppFile = "AyuGram"_cs;
constexpr auto AppVersion = 5012001; constexpr auto AppVersion = 5012003;
constexpr auto AppVersionStr = "5.12.1"; constexpr auto AppVersionStr = "5.12.3";
constexpr auto AppBetaVersion = false; constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View file

@ -322,6 +322,9 @@ QString CountriesInstance::flagEmojiByISO2(const QString &iso) const {
|| iso.back() < 'A' || iso.back() < 'A'
|| iso.back() > 'Z') { || iso.back() > 'Z') {
return QString(); return QString();
} else if (iso == u"FT"_q) {
return QString::fromUtf8(
"\xF0\x9F\x8F\xB4\xE2\x80\x8D\xE2\x98\xA0\xEF\xB8\x8F");
} }
auto result = QString(4, QChar(0xD83C)); auto result = QString(4, QChar(0xD83C));
result[1] = QChar(iso.front().unicode() - 'A' + 0xDDE6); result[1] = QChar(iso.front().unicode() - 'A' + 0xDDE6);

View file

@ -4905,33 +4905,14 @@ void InnerWidget::setupShortcuts() {
}); });
} }
const auto nearFolder = [=](bool isNext) {
const auto id = _controller->activeChatsFilterCurrent();
const auto list = &session().data().chatsFilters().list();
const auto index = int(ranges::find(
*list,
id,
&Data::ChatFilter::id
) - begin(*list));
if (index == list->size() && id != 0) {
return false;
}
const auto changed = index + (isNext ? 1 : -1);
if (changed >= int(list->size()) || changed < 0) {
return false;
}
_controller->setActiveChatsFilter((changed >= 0)
? (*list)[changed].id()
: 0);
return true;
};
request->check(Command::FolderNext) && request->handle([=] { request->check(Command::FolderNext) && request->handle([=] {
return nearFolder(true); using namespace Window;
return CheckAndJumpToNearChatsFilter(_controller, true, true);
}); });
request->check(Command::FolderPrevious) && request->handle([=] { request->check(Command::FolderPrevious) && request->handle([=] {
return nearFolder(false); using namespace Window;
return CheckAndJumpToNearChatsFilter(_controller, false, true);
}); });
request->check(Command::ReadChat) && request->handle([=] { request->check(Command::ReadChat) && request->handle([=] {

View file

@ -1334,6 +1334,9 @@ void Widget::toggleFiltersMenu(bool enabled) {
if (_layout == Layout::Child) { if (_layout == Layout::Child) {
enabled = false; enabled = false;
} }
if (const auto id = controller()->windowId(); id.forum() || id.folder()) {
enabled = false;
}
if (!enabled == !_chatFilters) { if (!enabled == !_chatFilters) {
return; return;
} else if (enabled) { } else if (enabled) {

View file

@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/layers/generic_box.h" #include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/boxes/calendar_box.h" #include "ui/boxes/calendar_box.h"
#include "ui/boxes/choose_time.h"
#include "platform/platform_specific.h" #include "platform/platform_specific.h"
#include "core/application.h" #include "core/application.h"
#include "core/file_utilities.h" #include "core/file_utilities.h"
@ -388,7 +389,7 @@ void SettingsWidget::addFormatAndLocationLabel(
void SettingsWidget::addLimitsLabel( void SettingsWidget::addLimitsLabel(
not_null<Ui::VerticalLayout*> container) { not_null<Ui::VerticalLayout*> container) {
auto fromLink = value() | rpl::map([](const Settings &data) { auto fromDateLink = value() | rpl::map([](const Settings &data) {
return data.singlePeerFrom; return data.singlePeerFrom;
}) | rpl::distinct_until_changed( }) | rpl::distinct_until_changed(
) | rpl::map([](TimeId from) { ) | rpl::map([](TimeId from) {
@ -399,7 +400,34 @@ void SettingsWidget::addLimitsLabel(
) | Ui::Text::ToLink(u"internal:edit_from"_q); ) | Ui::Text::ToLink(u"internal:edit_from"_q);
}) | rpl::flatten_latest(); }) | rpl::flatten_latest();
auto tillLink = value() | rpl::map([](const Settings &data) { const auto mapToTime = [](TimeId id, const QString &link) {
return rpl::single(id
? QLocale().toString(
base::unixtime::parse(id).time(),
QLocale::ShortFormat)
: QString()
) | Ui::Text::ToLink(link);
};
const auto concat = [](TextWithEntities date, TextWithEntities link) {
return link.text.isEmpty()
? date
: date.append(u", "_q).append(std::move(link));
};
auto fromTimeLink = value() | rpl::map([](const Settings &data) {
return data.singlePeerFrom;
}) | rpl::distinct_until_changed(
) | rpl::map([=](TimeId from) {
return mapToTime(from, u"internal:edit_from_time"_q);
}) | rpl::flatten_latest();
auto fromLink = rpl::combine(
std::move(fromDateLink),
std::move(fromTimeLink)
) | rpl::map(concat);
auto tillDateLink = value() | rpl::map([](const Settings &data) {
return data.singlePeerTill; return data.singlePeerTill;
}) | rpl::distinct_until_changed( }) | rpl::distinct_until_changed(
) | rpl::map([](TimeId till) { ) | rpl::map([](TimeId till) {
@ -410,6 +438,18 @@ void SettingsWidget::addLimitsLabel(
) | Ui::Text::ToLink(u"internal:edit_till"_q); ) | Ui::Text::ToLink(u"internal:edit_till"_q);
}) | rpl::flatten_latest(); }) | rpl::flatten_latest();
auto tillTimeLink = value() | rpl::map([](const Settings &data) {
return data.singlePeerTill;
}) | rpl::distinct_until_changed(
) | rpl::map([=](TimeId till) {
return mapToTime(till, u"internal:edit_till_time"_q);
}) | rpl::flatten_latest();
auto tillLink = rpl::combine(
std::move(tillDateLink),
std::move(tillTimeLink)
) | rpl::map(concat);
auto datesText = tr::lng_export_limits( auto datesText = tr::lng_export_limits(
lt_from, lt_from,
std::move(fromLink), std::move(fromLink),
@ -424,8 +464,47 @@ void SettingsWidget::addLimitsLabel(
object_ptr<Ui::FlatLabel>( object_ptr<Ui::FlatLabel>(
container, container,
std::move(datesText), std::move(datesText),
st::exportLocationLabel), st::boxLabel),
st::exportLimitsPadding); st::exportLimitsPadding);
const auto removeTime = [](TimeId dateTime) {
return base::unixtime::serialize(
QDateTime(
base::unixtime::parse(dateTime).date(),
QTime()));
};
const auto editTimeLimit = [=](Fn<TimeId()> now, Fn<void(TimeId)> done) {
_showBoxCallback(Box([=](not_null<Ui::GenericBox*> box) {
auto result = Ui::ChooseTimeWidget(
box->verticalLayout(),
[&] {
const auto time = base::unixtime::parse(now()).time();
return time.hour() * 3600
+ time.minute() * 60
+ time.second();
}(),
true);
const auto widget = box->addRow(std::move(result.widget));
const auto toSave = widget->lifetime().make_state<TimeId>(0);
std::move(
result.secondsValue
) | rpl::start_with_next([=](TimeId t) {
*toSave = t;
}, box->lifetime());
box->addButton(tr::lng_settings_save(), [=] {
done(*toSave);
box->closeBox();
});
box->addButton(tr::lng_cancel(), [=] {
box->closeBox();
});
box->setTitle(tr::lng_settings_ttl_after_custom());
}));
};
constexpr auto kOffset = 600;
label->overrideLinkClickHandler([=](const QString &url) { label->overrideLinkClickHandler([=](const QString &url) {
if (url == u"internal:edit_from"_q) { if (url == u"internal:edit_from"_q) {
const auto done = [=](TimeId limit) { const auto done = [=](TimeId limit) {
@ -439,10 +518,38 @@ void SettingsWidget::addLimitsLabel(
readData().singlePeerTill, readData().singlePeerTill,
tr::lng_export_from_beginning(), tr::lng_export_from_beginning(),
done); done);
} else if (url == u"internal:edit_from_time"_q) {
const auto now = [=] {
auto result = TimeId(0);
changeData([&](Settings &settings) {
result = settings.singlePeerFrom;
});
return result;
};
const auto done = [=](TimeId time) {
changeData([&](Settings &settings) {
const auto result = time
+ removeTime(settings.singlePeerFrom);
if (result >= settings.singlePeerTill
&& settings.singlePeerTill) {
settings.singlePeerFrom = settings.singlePeerTill
- kOffset;
} else {
settings.singlePeerFrom = result;
}
});
};
editTimeLimit(now, done);
} else if (url == u"internal:edit_till"_q) { } else if (url == u"internal:edit_till"_q) {
const auto done = [=](TimeId limit) { const auto done = [=](TimeId limit) {
changeData([&](Settings &settings) { changeData([&](Settings &settings) {
settings.singlePeerTill = limit; if (limit <= settings.singlePeerFrom
&& settings.singlePeerFrom) {
settings.singlePeerTill = settings.singlePeerFrom
+ kOffset;
} else {
settings.singlePeerTill = limit;
}
}); });
}; };
editDateLimit( editDateLimit(
@ -451,6 +558,28 @@ void SettingsWidget::addLimitsLabel(
0, 0,
tr::lng_export_till_end(), tr::lng_export_till_end(),
done); done);
} else if (url == u"internal:edit_till_time"_q) {
const auto now = [=] {
auto result = TimeId(0);
changeData([&](Settings &settings) {
result = settings.singlePeerTill;
});
return result;
};
const auto done = [=](TimeId time) {
changeData([&](Settings &settings) {
const auto result = time
+ removeTime(settings.singlePeerTill);
if (result <= settings.singlePeerFrom
&& settings.singlePeerFrom) {
settings.singlePeerTill = settings.singlePeerFrom
+ kOffset;
} else {
settings.singlePeerTill = result;
}
});
};
editTimeLimit(now, done);
} else { } else {
Unexpected("Click handler URL in export limits edit."); Unexpected("Click handler URL in export limits edit.");
} }

View file

@ -1567,7 +1567,7 @@ void VoiceRecordBar::init() {
if (value == 0. && !show) { if (value == 0. && !show) {
_lock->hide(); _lock->hide();
} else if (value == 1. && show) { } else if (value == 1. && show) {
computeAndSetLockProgress(QCursor::pos()); _lock->requestPaintProgress(calcLockProgress(QCursor::pos()));
} }
if (_fullRecord && !show) { if (_fullRecord && !show) {
updateTTLGeometry(TTLAnimationType::RightLeft, 1.); updateTTLGeometry(TTLAnimationType::RightLeft, 1.);
@ -1872,6 +1872,52 @@ void VoiceRecordBar::startRecording() {
_inField = true; _inField = true;
struct FloatingState {
Ui::Animations::Basic animation;
float64 animationProgress = 0;
float64 cursorProgress = 0;
bool lockCapturedByInput = false;
float64 frameCounter = 0;
rpl::lifetime lifetime;
};
const auto stateOwned
= _recordingLifetime.make_state<std::unique_ptr<FloatingState>>(
std::make_unique<FloatingState>());
const auto state = stateOwned->get();
_lock->locks() | rpl::start_with_next([=] {
stateOwned->reset();
}, state->lifetime);
constexpr auto kAnimationThreshold = 0.35;
const auto calcStateRatio = [=](float64 counter) {
return (1 - std::cos(std::fmod(counter, 2 * M_PI))) * 0.5;
};
state->animation.init([=](crl::time now) {
if (state->cursorProgress > kAnimationThreshold) {
state->lockCapturedByInput = true;
}
if (state->lockCapturedByInput) {
if (state->cursorProgress < 0.01) {
state->lockCapturedByInput = false;
state->frameCounter = 0;
} else {
_lock->requestPaintProgress(state->cursorProgress);
return;
}
}
const auto progress = anim::interpolateF(
state->cursorProgress,
kAnimationThreshold,
calcStateRatio(state->frameCounter));
state->frameCounter += 0.01;
_lock->requestPaintProgress(progress);
});
state->animation.start();
if (hasDuration()) {
stateOwned->reset();
}
_send->events( _send->events(
) | rpl::filter([=](not_null<QEvent*> e) { ) | rpl::filter([=](not_null<QEvent*> e) {
return (e->type() == QEvent::MouseMove return (e->type() == QEvent::MouseMove
@ -1892,7 +1938,10 @@ void VoiceRecordBar::startRecording() {
if (_showLockAnimation.animating() || !hasDuration()) { if (_showLockAnimation.animating() || !hasDuration()) {
return; return;
} }
computeAndSetLockProgress(mouse->globalPos()); const auto inputProgress = calcLockProgress(mouse->globalPos());
if (inputProgress > state->animationProgress) {
state->cursorProgress = inputProgress;
}
} else if (type == QEvent::MouseButtonRelease) { } else if (type == QEvent::MouseButtonRelease) {
checkTipRequired(); checkTipRequired();
stop(_inField.current()); stop(_inField.current());
@ -2328,10 +2377,14 @@ float64 VoiceRecordBar::showListenAnimationRatio() const {
} }
void VoiceRecordBar::computeAndSetLockProgress(QPoint globalPos) { void VoiceRecordBar::computeAndSetLockProgress(QPoint globalPos) {
_lock->requestPaintProgress(calcLockProgress(globalPos));
}
float64 VoiceRecordBar::calcLockProgress(QPoint globalPos) {
const auto localPos = mapFromGlobal(globalPos); const auto localPos = mapFromGlobal(globalPos);
const auto lower = _lock->height(); const auto lower = _lock->height();
const auto higher = 0; const auto higher = 0;
_lock->requestPaintProgress(Progress(localPos.y(), higher - lower)); return Progress(localPos.y(), higher - lower);
} }
bool VoiceRecordBar::peekTTLState() const { bool VoiceRecordBar::peekTTLState() const {

View file

@ -156,6 +156,7 @@ private:
[[nodiscard]] float64 activeAnimationRatio() const; [[nodiscard]] float64 activeAnimationRatio() const;
void computeAndSetLockProgress(QPoint globalPos); void computeAndSetLockProgress(QPoint globalPos);
[[nodiscard]] float64 calcLockProgress(QPoint globalPos);
[[nodiscard]] bool peekTTLState() const; [[nodiscard]] bool peekTTLState() const;
[[nodiscard]] bool takeTTLState() const; [[nodiscard]] bool takeTTLState() const;

View file

@ -748,6 +748,7 @@ AdminLog::OwnedItem AboutView::makeNewPeerInfo(not_null<UserData*> user) {
owned.get(), owned.get(),
GenerateNewPeerInfo(owned.get(), _item.get(), user, _commonGroups), GenerateNewPeerInfo(owned.get(), _item.get(), user, _commonGroups),
HistoryView::MediaGenericDescriptor{ HistoryView::MediaGenericDescriptor{
.maxWidth = st::newPeerWidth,
.service = true, .service = true,
.hideServiceText = true, .hideServiceText = true,
})); }));

View file

@ -308,6 +308,9 @@ void ContactStatus::Bar::showState(
_emojiStatusInfo->entity()->overrideLinkClickHandler([=] { _emojiStatusInfo->entity()->overrideLinkClickHandler([=] {
_emojiStatusClicks.fire({}); _emojiStatusClicks.fire({});
}); });
_emojiStatusInfo->entity()->setAnimationsPausedCallback([] {
return Ui::FlatLabel::WhichAnimationsPaused::CustomEmoji;
});
} }
_emojiStatusInfo->setVisible(has); _emojiStatusInfo->setVisible(has);
_add->setText((type == Type::Add) _add->setText((type == Type::Add)

View file

@ -304,7 +304,7 @@ QSize MediaGenericTextPart::countCurrentSize(int newWidth) {
const auto size = CountOptimalTextSize( const auto size = CountOptimalTextSize(
_text, _text,
st::msgMinWidth, st::msgMinWidth,
newWidth - skip); std::max(st::msgMinWidth, newWidth - skip));
return { return {
size.width() + skip, size.width() + skip,
_margins.top() + size.height() + _margins.bottom(), _margins.top() + size.height() + _margins.bottom(),

View file

@ -274,7 +274,10 @@ void InnerWidget::markUnpinned(std::vector<Entry>::iterator i) {
} }
++after; ++after;
} }
if (after == _entries.size()) { if (after == _entries.size() && !_allLoaded) {
// We don't know if the correct position is exactly in the end
// of the loaded part or later, so we hide it for now, let it
// be loaded later while scrolling.
_entries.erase(i); _entries.erase(i);
} else if (after > index + 1) { } else if (after > index + 1) {
std::rotate(i, i + 1, begin(_entries) + after); std::rotate(i, i + 1, begin(_entries) + after);

View file

@ -12,6 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/linux/base_linux_xdp_utilities.h" #include "base/platform/linux/base_linux_xdp_utilities.h"
#include "core/sandbox.h" #include "core/sandbox.h"
#include "core/application.h" #include "core/application.h"
#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
#include "core/core_settings.h"
#endif
#include "base/random.h" #include "base/random.h"
#include <QtCore/QAbstractEventDispatcher> #include <QtCore/QAbstractEventDispatcher>

View file

@ -423,7 +423,8 @@ void Manager::Private::init(XdgNotifications::NotificationsProxy proxy) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] { Core::Sandbox::Instance().customEnterFromEventLoop([&] {
for (const auto &[key, notifications] : _notifications) { for (const auto &[key, notifications] : _notifications) {
for (const auto &[msgId, notification] : notifications) { for (const auto &[msgId, notification] : notifications) {
if (id == v::get<uint>(notification->id)) { const auto &nid = notification->id;
if (v::is<uint>(nid) && v::get<uint>(nid) == id) {
if (actionName == "default") { if (actionName == "default") {
_manager->notificationActivated({ key, msgId }); _manager->notificationActivated({ key, msgId });
} else if (actionName == "mail-mark-read") { } else if (actionName == "mail-mark-read") {
@ -447,7 +448,8 @@ void Manager::Private::init(XdgNotifications::NotificationsProxy proxy) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] { Core::Sandbox::Instance().customEnterFromEventLoop([&] {
for (const auto &[key, notifications] : _notifications) { for (const auto &[key, notifications] : _notifications) {
for (const auto &[msgId, notification] : notifications) { for (const auto &[msgId, notification] : notifications) {
if (id == v::get<uint>(notification->id)) { const auto &nid = notification->id;
if (v::is<uint>(nid) && v::get<uint>(nid) == id) {
_manager->notificationReplied( _manager->notificationReplied(
{ key, msgId }, { key, msgId },
{ QString::fromStdString(text), {} }); { QString::fromStdString(text), {} });
@ -468,7 +470,8 @@ void Manager::Private::init(XdgNotifications::NotificationsProxy proxy) {
std::string token) { std::string token) {
for (const auto &[key, notifications] : _notifications) { for (const auto &[key, notifications] : _notifications) {
for (const auto &[msgId, notification] : notifications) { for (const auto &[msgId, notification] : notifications) {
if (id == v::get<uint>(notification->id)) { const auto &nid = notification->id;
if (v::is<uint>(nid) && v::get<uint>(nid) == id) {
GLib::setenv("XDG_ACTIVATION_TOKEN", token, true); GLib::setenv("XDG_ACTIVATION_TOKEN", token, true);
return; return;
} }
@ -501,7 +504,8 @@ void Manager::Private::init(XdgNotifications::NotificationsProxy proxy) {
* In all other cases we keep the notification reference so that we may clear the notification later from history, * In all other cases we keep the notification reference so that we may clear the notification later from history,
* if the message for that notification is read (e.g. chat is opened or read from another device). * if the message for that notification is read (e.g. chat is opened or read from another device).
*/ */
if (id == v::get<uint>(notification->id) && reason == 2) { const auto &nid = notification->id;
if (v::is<uint>(nid) && v::get<uint>(nid) == id && reason == 2) {
clearNotification({ key, msgId }); clearNotification({ key, msgId });
return; return;
} }

View file

@ -18,7 +18,8 @@ namespace Ui {
ChooseTimeResult ChooseTimeWidget( ChooseTimeResult ChooseTimeWidget(
not_null<RpWidget*> parent, not_null<RpWidget*> parent,
TimeId startSeconds) { TimeId startSeconds,
bool hiddenDaysInput) {
using TimeField = Ui::TimePartWithPlaceholder; using TimeField = Ui::TimePartWithPlaceholder;
const auto putNext = [](not_null<TimeField*> field, QChar ch) { const auto putNext = [](not_null<TimeField*> field, QChar ch) {
field->setCursorPosition(0); field->setCursorPosition(0);
@ -79,6 +80,10 @@ ChooseTimeResult ChooseTimeWidget(
const auto hour = Ui::MakeWeak(state->hour); const auto hour = Ui::MakeWeak(state->hour);
const auto minute = Ui::MakeWeak(state->minute); const auto minute = Ui::MakeWeak(state->minute);
if (hiddenDaysInput) {
day->setVisible(false);
}
day->setPhrase(tr::lng_days); day->setPhrase(tr::lng_days);
day->setMaxValue(31); day->setMaxValue(31);
day->setWheelStep(1); day->setWheelStep(1);
@ -105,13 +110,16 @@ ChooseTimeResult ChooseTimeWidget(
content->sizeValue( content->sizeValue(
) | rpl::start_with_next([=](const QSize &s) { ) | rpl::start_with_next([=](const QSize &s) {
const auto inputWidth = s.width() / 3; const auto inputWidth = s.width() / (hiddenDaysInput ? 2 : 3);
auto rect = QRect( auto rect = QRect(
0, 0,
(s.height() - day->height()) / 2, (s.height() - day->height()) / 2,
inputWidth, inputWidth,
day->height()); day->height());
for (const auto &input : { day, hour, minute }) { for (const auto &input : { day, hour, minute }) {
if (input->isHidden()) {
continue;
}
input->setGeometry(rect - st::muteBoxTimeFieldPadding); input->setGeometry(rect - st::muteBoxTimeFieldPadding);
rect.translate(inputWidth, 0); rect.translate(inputWidth, 0);
} }

View file

@ -20,6 +20,7 @@ struct ChooseTimeResult {
ChooseTimeResult ChooseTimeWidget( ChooseTimeResult ChooseTimeWidget(
not_null<RpWidget*> parent, not_null<RpWidget*> parent,
TimeId startSeconds); TimeId startSeconds,
bool hiddenDaysInput = false);
} // namespace Ui } // namespace Ui

View file

@ -1244,3 +1244,4 @@ newPeerUserpics: GroupCallUserpics {
align: align(left); align: align(left);
} }
newPeerUserpicsPadding: margins(0px, 3px, 0px, 0px); newPeerUserpicsPadding: margins(0px, 3px, 0px, 0px);
newPeerWidth: 320px;

View file

@ -72,8 +72,8 @@ void SendButton::setState(State state) {
st::universalDuration); st::universalDuration);
setPointerCursor(_state.type != Type::Slowmode); setPointerCursor(_state.type != Type::Slowmode);
updateSize(); updateSize();
update();
} }
update();
} }
void SendButton::finishAnimating() { void SendButton::finishAnimating() {

View file

@ -49,6 +49,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
#include "data/data_replies_list.h" #include "data/data_replies_list.h"
#include "data/data_peer_values.h" #include "data/data_peer_values.h"
#include "data/data_premium_limits.h"
#include "data/data_web_page.h" #include "data/data_web_page.h"
#include "passport/passport_form_controller.h" #include "passport/passport_form_controller.h"
#include "chat_helpers/tabbed_selector.h" #include "chat_helpers/tabbed_selector.h"
@ -3252,4 +3253,34 @@ SessionController::~SessionController() {
resetFakeUnreadWhileOpened(); resetFakeUnreadWhileOpened();
} }
bool CheckAndJumpToNearChatsFilter(
not_null<SessionController*> controller,
bool isNext,
bool jump) {
const auto id = controller->activeChatsFilterCurrent();
const auto session = &controller->session();
const auto list = &session->data().chatsFilters().list();
const auto index = int(ranges::find(
*list,
id,
&Data::ChatFilter::id
) - begin(*list));
if (index == list->size() && id != 0) {
return false;
}
const auto changed = index + (isNext ? 1 : -1);
if (changed >= int(list->size()) || changed < 0) {
return false;
}
if (changed > Data::PremiumLimits(session).dialogFiltersCurrent()) {
return false;
}
if (jump) {
controller->setActiveChatsFilter((changed >= 0)
? (*list)[changed].id()
: 0);
}
return true;
}
} // namespace Window } // namespace Window

View file

@ -757,4 +757,9 @@ void ActivateWindow(not_null<SessionController*> controller);
not_null<SessionController*> controller, not_null<SessionController*> controller,
GifPauseReason level); GifPauseReason level);
bool CheckAndJumpToNearChatsFilter(
not_null<SessionController*> controller,
bool isNext,
bool jump);
} // namespace Window } // namespace Window

View file

@ -1148,10 +1148,19 @@ depends:yasm/yasm
""") """)
stage('liblcms2', """ stage('liblcms2', """
mac:
git clone -b lcms2.16 https://github.com/mm2/Little-CMS.git liblcms2 git clone -b lcms2.16 https://github.com/mm2/Little-CMS.git liblcms2
cd liblcms2 cd liblcms2
win:
depends:python/Scripts/activate.bat
%THIRDPARTY_DIR%\\python\\Scripts\\activate.bat
meson setup --default-library=static --buildtype=debug -Db_vscrt=mtd out/Debug
meson compile -C out/Debug
release:
meson setup --default-library=static --buildtype=release -Db_vscrt=mt out/Release
meson compile -C out/Release
win:
deactivate
mac:
buildOneArch() { buildOneArch() {
arch=$1 arch=$1
folder=`pwd`/$2 folder=`pwd`/$2
@ -1689,6 +1698,7 @@ win:
SET OPENSSL_LIBS_DIR=%OPENSSL_DIR%\\out SET OPENSSL_LIBS_DIR=%OPENSSL_DIR%\\out
SET ZLIB_LIBS_DIR=%LIBS_DIR%\\zlib SET ZLIB_LIBS_DIR=%LIBS_DIR%\\zlib
SET WEBP_DIR=%LIBS_DIR%\\libwebp SET WEBP_DIR=%LIBS_DIR%\\libwebp
SET LCMS2_DIR=%LIBS_DIR%\\liblcms2
configure -prefix "%LIBS_DIR%\\Qt-%QT%" ^ configure -prefix "%LIBS_DIR%\\Qt-%QT%" ^
%CONFIGURATIONS% ^ %CONFIGURATIONS% ^
-force-debug-info ^ -force-debug-info ^
@ -1726,7 +1736,10 @@ win:
-D WebP_mux_INCLUDE_DIR="%WEBP_DIR%\\src" ^ -D WebP_mux_INCLUDE_DIR="%WEBP_DIR%\\src" ^
-D WebP_LIBRARY="%WEBP_DIR%\\out\\release-static\\$X8664\\lib\\webp.lib" ^ -D WebP_LIBRARY="%WEBP_DIR%\\out\\release-static\\$X8664\\lib\\webp.lib" ^
-D WebP_demux_LIBRARY="%WEBP_DIR%\\out\\release-static\\$X8664\\lib\\webpdemux.lib" ^ -D WebP_demux_LIBRARY="%WEBP_DIR%\\out\\release-static\\$X8664\\lib\\webpdemux.lib" ^
-D WebP_mux_LIBRARY="%WEBP_DIR%\\out\\release-static\\$X8664\\lib\\webpmux.lib" -D WebP_mux_LIBRARY="%WEBP_DIR%\\out\\release-static\\$X8664\\lib\\webpmux.lib" ^
-D LCMS2_FOUND=1 ^
-D LCMS2_INCLUDE_DIR="%LCMS2_DIR%\\include" ^
-D LCMS2_LIBRARIES="%LCMS2_DIR%\\out\Release\\src\\liblcms2.a"
cmake --build . --config Debug --parallel cmake --build . --config Debug --parallel
cmake --install . --config Debug cmake --install . --config Debug

View file

@ -1,7 +1,7 @@
AppVersion 5012001 AppVersion 5012003
AppVersionStrMajor 5.12 AppVersionStrMajor 5.12
AppVersionStrSmall 5.12.1 AppVersionStrSmall 5.12.3
AppVersionStr 5.12.1 AppVersionStr 5.12.3
BetaChannel 0 BetaChannel 0
AlphaVersion 0 AlphaVersion 0
AppVersionOriginal 5.12.1 AppVersionOriginal 5.12.3

View file

@ -1,3 +1,13 @@
5.12.3 (10.03.25)
- Fix a couple more crashes.
- Fix gift disappearing on unpin.
- Fix country emoji for Fragment numbers.
5.12.2 (09.03.25)
- Fix some crashes.
5.12.1 (08.03.25) 5.12.1 (08.03.25)
- Fix a crash in some chat switchings. - Fix a crash in some chat switchings.

2
cmake

@ -1 +1 @@
Subproject commit 8c146a3ec6f4475e5083eee84d126960495a7e1d Subproject commit 90e6d73100a9fd2dc4c30a270c3bbc1d35924f32