Merge remote-tracking branch 'upstream-main/dev' into dev

# Conflicts:
#	Telegram/Resources/winrc/Telegram.rc
#	Telegram/Resources/winrc/Updater.rc
#	Telegram/SourceFiles/core/version.h
#	Telegram/lib_ui
#	snap/snapcraft.yaml
This commit is contained in:
ZavaruKitsu 2023-09-27 20:38:30 +03:00
commit ad29ab0f51
36 changed files with 233 additions and 198 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="4.10.0.0" /> Version="4.10.1.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 4,10,0,0 FILEVERSION 4,10,1,0
PRODUCTVERSION 4,10,0,0 PRODUCTVERSION 4,10,1,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", "4.10.0.0" VALUE "FileVersion", "4.10.1.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023" VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "AyuGram Desktop" VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "4.10.0.0" VALUE "ProductVersion", "4.10.1.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 4,10,0,0 FILEVERSION 4,10,1,0
PRODUCTVERSION 4,10,0,0 PRODUCTVERSION 4,10,1,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", "4.10.0.0" VALUE "FileVersion", "4.10.1.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023" VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "AyuGram Desktop" VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "4.10.0.0" VALUE "ProductVersion", "4.10.1.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -780,10 +780,9 @@ QString ApiWrap::exportDirectMessageLink(
QString ApiWrap::exportDirectStoryLink(not_null<Data::Story*> story) { QString ApiWrap::exportDirectStoryLink(not_null<Data::Story*> story) {
const auto storyId = story->fullId(); const auto storyId = story->fullId();
const auto user = story->peer()->asUser(); const auto peer = story->peer();
Assert(user != nullptr);
const auto fallback = [&] { const auto fallback = [&] {
const auto base = user->username(); const auto base = peer->userName();
const auto story = QString::number(storyId.story); const auto story = QString::number(storyId.story);
const auto query = base + "/s/" + story; const auto query = base + "/s/" + story;
return session().createInternalLinkFull(query); return session().createInternalLinkFull(query);
@ -793,7 +792,7 @@ QString ApiWrap::exportDirectStoryLink(not_null<Data::Story*> story) {
? i->second ? i->second
: fallback(); : fallback();
request(MTPstories_ExportStoryLink( request(MTPstories_ExportStoryLink(
story->peer()->input, peer->input,
MTP_int(story->id()) MTP_int(story->id())
)).done([=](const MTPExportedStoryLink &result) { )).done([=](const MTPExportedStoryLink &result) {
const auto link = qs(result.data().vlink()); const auto link = qs(result.data().vlink());

View file

@ -855,7 +855,9 @@ bool ResolveBoost(
match->captured(1), match->captured(1),
qthelp::UrlParamNameTransform::ToLower); qthelp::UrlParamNameTransform::ToLower);
const auto domainParam = params.value(u"domain"_q); const auto domainParam = params.value(u"domain"_q);
const auto channelParam = params.value(u"channel"_q); const auto channelParam = params.contains(u"c"_q)
? params.value(u"c"_q)
: params.value(u"channel"_q);
const auto myContext = context.value<ClickHandlerContext>(); const auto myContext = context.value<ClickHandlerContext>();
using Navigation = Window::SessionNavigation; using Navigation = Window::SessionNavigation;
@ -1086,6 +1088,12 @@ QString TryConvertUrlToLocal(QString url) {
if (params.indexOf("boost", 0, Qt::CaseInsensitive) >= 0 if (params.indexOf("boost", 0, Qt::CaseInsensitive) >= 0
&& params.toLower().split('&').contains(u"boost"_q)) { && params.toLower().split('&').contains(u"boost"_q)) {
return u"tg://boost?domain="_q + domain; return u"tg://boost?domain="_q + domain;
} else if (domain == u"boost"_q) {
if (const auto domainMatch = regex_match(u"^/([a-zA-Z0-9\\.\\_]+)(/?\\?|/?$)"_q, usernameMatch->captured(2))) {
return u"tg://boost?domain="_q + domainMatch->captured(1);
} else if (params.indexOf("c=", 0, Qt::CaseInsensitive) >= 0) {
return u"tg://boost?"_q + params;
}
} }
const auto base = u"tg://resolve?domain="_q + url_encode(usernameMatch->captured(1)); const auto base = u"tg://resolve?domain="_q + url_encode(usernameMatch->captured(1));
auto added = QString(); auto added = QString();

View file

@ -28,7 +28,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/qthelp_regex.h" #include "base/qthelp_regex.h"
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/platform/ui_platform_utility.h"
#include <QtCore/QLockFile> #include <QtCore/QLockFile>
#include <QtGui/QSessionManager> #include <QtGui/QSessionManager>
@ -593,18 +592,9 @@ void Sandbox::registerEnterFromEventLoop() {
} }
bool Sandbox::notifyOrInvoke(QObject *receiver, QEvent *e) { bool Sandbox::notifyOrInvoke(QObject *receiver, QEvent *e) {
const auto type = e->type(); if (e->type() == base::InvokeQueuedEvent::Type()) {
if (type == base::InvokeQueuedEvent::Type()) {
static_cast<base::InvokeQueuedEvent*>(e)->invoke(); static_cast<base::InvokeQueuedEvent*>(e)->invoke();
return true; return true;
} else if (receiver == this) {
if (type == QEvent::ApplicationDeactivate) {
if (Ui::Platform::SkipApplicationDeactivateEvent()) {
return true;
}
} else if (type == QEvent::ApplicationActivate) {
Ui::Platform::GotApplicationActivateEvent();
}
} }
return QApplication::notify(receiver, e); return QApplication::notify(receiver, e);
} }

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 = 4010000; constexpr auto AppVersion = 4010001;
constexpr auto AppVersionStr = "4.10"; constexpr auto AppVersionStr = "4.10.1";
constexpr auto AppBetaVersion = false; constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View file

@ -1217,8 +1217,11 @@ void Stories::toggleHidden(
bool hidden, bool hidden,
std::shared_ptr<Ui::Show> show) { std::shared_ptr<Ui::Show> show) {
const auto peer = _owner->peer(peerId); const auto peer = _owner->peer(peerId);
const auto justRemove = peer->isServiceUser() && hidden;
if (peer->hasStoriesHidden() != hidden) { if (peer->hasStoriesHidden() != hidden) {
peer->setStoriesHidden(hidden); if (!justRemove) {
peer->setStoriesHidden(hidden);
}
session().api().request(MTPstories_TogglePeerStoriesHidden( session().api().request(MTPstories_TogglePeerStoriesHidden(
peer->input, peer->input,
MTP_bool(hidden) MTP_bool(hidden)
@ -1239,6 +1242,11 @@ void Stories::toggleHidden(
} }
}); });
if (justRemove) {
apply(peer, nullptr);
return;
}
const auto i = _all.find(peerId); const auto i = _all.find(peerId);
if (i == end(_all)) { if (i == end(_all)) {
return; return;

View file

@ -370,8 +370,7 @@ bool Story::hasDirectLink() const {
if (!_privacyPublic || (!_pinned && expired())) { if (!_privacyPublic || (!_pinned && expired())) {
return false; return false;
} }
const auto user = _peer->asUser(); return !_peer->userName().isEmpty();
return user && !user->username().isEmpty();
} }
std::optional<QString> Story::errorTextForForward( std::optional<QString> Story::errorTextForForward(

View file

@ -330,8 +330,8 @@ State::State(not_null<Data::Stories*> data, Data::StorySourcesList list)
} }
Content State::next() { Content State::next() {
auto result = Content();
const auto &sources = _data->sources(_list); const auto &sources = _data->sources(_list);
auto result = Content{ .total = int(sources.size()) };
result.elements.reserve(sources.size()); result.elements.reserve(sources.size());
for (const auto &info : sources) { for (const auto &info : sources) {
const auto source = _data->source(info.id); const auto source = _data->source(info.id);
@ -390,8 +390,10 @@ rpl::producer<Content> LastForPeer(not_null<PeerData*> peer) {
) | rpl::map([=] { ) | rpl::map([=] {
auto ids = std::vector<StoryId>(); auto ids = std::vector<StoryId>();
auto readTill = StoryId(); auto readTill = StoryId();
auto total = 0;
if (const auto source = stories->source(peerId)) { if (const auto source = stories->source(peerId)) {
readTill = source->readTill; readTill = source->readTill;
total = int(source->ids.size());
ids = ranges::views::all(source->ids) ids = ranges::views::all(source->ids)
| ranges::views::reverse | ranges::views::reverse
| ranges::views::take(kShownLastCount) | ranges::views::take(kShownLastCount)
@ -420,7 +422,7 @@ rpl::producer<Content> LastForPeer(not_null<PeerData*> peer) {
} }
auto done = true; auto done = true;
auto resolving = false; auto resolving = false;
auto result = Content{}; auto result = Content{ .total = total };
for (const auto id : ids) { for (const auto id : ids) {
const auto storyId = FullStoryId{ peerId, id }; const auto storyId = FullStoryId{ peerId, id };
const auto maybe = stories->lookup(storyId); const auto maybe = stories->lookup(storyId);

View file

@ -49,6 +49,7 @@ struct Element {
struct Content { struct Content {
std::vector<Element> elements; std::vector<Element> elements;
int total = 0;
friend inline bool operator==( friend inline bool operator==(
const Content &a, const Content &a,

View file

@ -254,7 +254,9 @@ FrameGenerator::Frame FrameGenerator::Impl::renderNext(
QImage storage, QImage storage,
QSize size, QSize size,
Qt::AspectRatioMode mode) { Qt::AspectRatioMode mode) {
if (!_current.frame) { if (!_codec) {
return {};
} else if (!_current.frame) {
readNextFrame(); readNextFrame();
} }
std::swap(_current, _next); std::swap(_current, _next);
@ -266,6 +268,9 @@ FrameGenerator::Frame FrameGenerator::Impl::renderNext(
} }
void FrameGenerator::Impl::jumpToStart() { void FrameGenerator::Impl::jumpToStart() {
if (!_codec) {
return;
}
auto result = 0; auto result = 0;
if ((result = avformat_seek_file(_format.get(), _streamId, std::numeric_limits<int64_t>::min(), 0, std::numeric_limits<int64_t>::max(), 0)) < 0) { if ((result = avformat_seek_file(_format.get(), _streamId, std::numeric_limits<int64_t>::min(), 0, std::numeric_limits<int64_t>::max(), 0)) < 0) {
if ((result = av_seek_frame(_format.get(), _streamId, 0, AVSEEK_FLAG_BYTE)) < 0) { if ((result = av_seek_frame(_format.get(), _streamId, 0, AVSEEK_FLAG_BYTE)) < 0) {

View file

@ -1738,16 +1738,16 @@ std::unique_ptr<QMimeData> HistoryInner::prepareDrag() {
return nullptr; return nullptr;
} }
const auto mouseActionView = viewByItem(_mouseActionItem); const auto pressedView = viewByItem(_mouseActionItem);
bool uponSelected = false; bool uponSelected = false;
if (mouseActionView) { if (pressedView) {
if (!_selected.empty() && _selected.cbegin()->second == FullSelection) { if (!_selected.empty() && _selected.cbegin()->second == FullSelection) {
uponSelected = _dragStateItem uponSelected = _mouseActionItem
&& (_selected.find(_dragStateItem) != _selected.cend()); && (_selected.find(_mouseActionItem) != _selected.cend());
} else { } else {
StateRequest request; StateRequest request;
request.flags |= Ui::Text::StateRequest::Flag::LookupSymbol; request.flags |= Ui::Text::StateRequest::Flag::LookupSymbol;
auto dragState = mouseActionView->textState(_dragStartPosition, request); auto dragState = pressedView->textState(_dragStartPosition, request);
uponSelected = (dragState.cursor == CursorState::Text); uponSelected = (dragState.cursor == CursorState::Text);
if (uponSelected) { if (uponSelected) {
if (_selected.empty() if (_selected.empty()
@ -1788,19 +1788,15 @@ std::unique_ptr<QMimeData> HistoryInner::prepareDrag() {
} }
} }
return mimeData; return mimeData;
} else if (_dragStateItem) { } else if (pressedView) {
const auto view = viewByItem(_dragStateItem);
if (!view) {
return nullptr;
}
auto forwardIds = MessageIdsList(); auto forwardIds = MessageIdsList();
if (_mouseCursorState == CursorState::Date) { if (_mouseCursorState == CursorState::Date) {
forwardIds = session().data().itemOrItsGroup(_dragStateItem); forwardIds = session().data().itemOrItsGroup(_mouseActionItem);
} else if (view->isHiddenByGroup() && pressedHandler) { } else if (pressedView->isHiddenByGroup() && pressedHandler) {
forwardIds = MessageIdsList(1, _dragStateItem->fullId()); forwardIds = MessageIdsList(1, _mouseActionItem->fullId());
} else if (const auto media = view->media()) { } else if (const auto media = pressedView->media()) {
if (media->dragItemByHandler(pressedHandler)) { if (media->dragItemByHandler(pressedHandler)) {
forwardIds = MessageIdsList(1, _dragStateItem->fullId()); forwardIds = MessageIdsList(1, _mouseActionItem->fullId());
} }
} }
if (forwardIds.empty()) { if (forwardIds.empty()) {
@ -1809,7 +1805,7 @@ std::unique_ptr<QMimeData> HistoryInner::prepareDrag() {
session().data().setMimeForwardIds(std::move(forwardIds)); session().data().setMimeForwardIds(std::move(forwardIds));
auto result = std::make_unique<QMimeData>(); auto result = std::make_unique<QMimeData>();
result->setData(u"application/x-td-forward"_q, "1"); result->setData(u"application/x-td-forward"_q, "1");
if (const auto media = view->media()) { if (const auto media = pressedView->media()) {
if (const auto document = media->getDocument()) { if (const auto document = media->getDocument()) {
const auto filepath = document->filepath(true); const auto filepath = document->filepath(true);
if (!filepath.isEmpty()) { if (!filepath.isEmpty()) {
@ -2384,7 +2380,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
_widget->confirmDeleteSelected(); _widget->confirmDeleteSelected();
}, &st::menuIconDelete); }, &st::menuIconDelete);
} }
if (selectedState.count > 0) { if (selectedState.count > 0 && !hasCopyRestrictionForSelected()) {
Menu::AddDownloadFilesAction(_menu, controller, _selected, this); Menu::AddDownloadFilesAction(_menu, controller, _selected, this);
} }
_menu->addAction(tr::lng_context_clear_selection(tr::now), [=] { _menu->addAction(tr::lng_context_clear_selection(tr::now), [=] {
@ -2582,7 +2578,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
_widget->confirmDeleteSelected(); _widget->confirmDeleteSelected();
}, &st::menuIconDelete); }, &st::menuIconDelete);
} }
if (selectedState.count > 0) { if (selectedState.count > 0 && !hasCopyRestrictionForSelected()) {
Menu::AddDownloadFilesAction(_menu, controller, _selected, this); Menu::AddDownloadFilesAction(_menu, controller, _selected, this);
} }
_menu->addAction(tr::lng_context_clear_selection(tr::now), [=] { _menu->addAction(tr::lng_context_clear_selection(tr::now), [=] {
@ -2921,7 +2917,7 @@ void HistoryInner::keyPressEvent(QKeyEvent *e) {
&& !showCopyRestrictionForSelected()) { && !showCopyRestrictionForSelected()) {
TextUtilities::SetClipboardText(getSelectedText(), QClipboard::FindBuffer); TextUtilities::SetClipboardText(getSelectedText(), QClipboard::FindBuffer);
#endif // Q_OS_MAC #endif // Q_OS_MAC
} else if (e == QKeySequence::Delete) { } else if (e == QKeySequence::Delete || e->key() == Qt::Key_Backspace) {
auto selectedState = getSelectionState(); auto selectedState = getSelectionState();
if (selectedState.count > 0 if (selectedState.count > 0
&& selectedState.canDeleteCount == selectedState.count) { && selectedState.canDeleteCount == selectedState.count) {

View file

@ -839,7 +839,9 @@ void AddDownloadFilesAction(
not_null<Ui::PopupMenu*> menu, not_null<Ui::PopupMenu*> menu,
const ContextMenuRequest &request, const ContextMenuRequest &request,
not_null<ListWidget*> list) { not_null<ListWidget*> list) {
if (!request.overSelection || request.selectedItems.empty()) { if (!request.overSelection
|| request.selectedItems.empty()
|| list->hasCopyRestrictionForSelected()) {
return; return;
} }
Menu::AddDownloadFilesAction( Menu::AddDownloadFilesAction(

View file

@ -2451,7 +2451,7 @@ void ListWidget::keyPressEvent(QKeyEvent *e) {
&& !hasCopyRestrictionForSelected()) { && !hasCopyRestrictionForSelected()) {
TextUtilities::SetClipboardText(getSelectedText(), QClipboard::FindBuffer); TextUtilities::SetClipboardText(getSelectedText(), QClipboard::FindBuffer);
#endif // Q_OS_MAC #endif // Q_OS_MAC
} else if (e == QKeySequence::Delete) { } else if (e == QKeySequence::Delete || e->key() == Qt::Key_Backspace) {
_delegate->listDeleteRequest(); _delegate->listDeleteRequest();
} else if (!(e->modifiers() & ~Qt::ShiftModifier) } else if (!(e->modifiers() & ~Qt::ShiftModifier)
&& e->key() != Qt::Key_Shift) { && e->key() != Qt::Key_Shift) {

View file

@ -525,7 +525,7 @@ void TopBar::setStories(rpl::producer<Dialogs::Stories::Content> content) {
rpl::duplicate( rpl::duplicate(
last last
) | rpl::start_with_next([=](const Content &content) { ) | rpl::start_with_next([=](const Content &content) {
const auto count = int(content.elements.size()); const auto count = content.total;
if (_storiesCount != count) { if (_storiesCount != count) {
const auto was = (_storiesCount > 0); const auto was = (_storiesCount > 0);
_storiesCount = count; _storiesCount = count;

View file

@ -279,7 +279,7 @@ namespace {
bool DebugModeEnabled = false; bool DebugModeEnabled = false;
void MoveOldDataFiles(const QString &wasDir) { [[maybe_unused]] void MoveOldDataFiles(const QString &wasDir) {
if (wasDir.isEmpty()) { if (wasDir.isEmpty()) {
return; return;
} }

View file

@ -866,8 +866,7 @@ void Controller::show(
.list = story->recentViewers(), .list = story->recentViewers(),
.reactions = story->reactions(), .reactions = story->reactions(),
.total = story->views(), .total = story->views(),
.self = peer->isSelf(), .type = RecentViewsTypeFor(peer),
.channel = peer->isChannel(),
}, _reactions->likedValue()); }, _reactions->likedValue());
if (const auto nowLikeButton = _recentViews->likeButton()) { if (const auto nowLikeButton = _recentViews->likeButton()) {
if (wasLikeButton != nowLikeButton) { if (wasLikeButton != nowLikeButton) {
@ -875,7 +874,7 @@ void Controller::show(
} }
} }
if (peer->isSelf() || peer->isChannel()) { if (peer->isSelf() || peer->isChannel() || peer->isServiceUser()) {
_reactions->setReactionIconWidget(_recentViews->likeIconWidget()); _reactions->setReactionIconWidget(_recentViews->likeIconWidget());
} else if (const auto like = _replyArea->likeAnimationTarget()) { } else if (const auto like = _replyArea->likeAnimationTarget()) {
_reactions->setReactionIconWidget(like); _reactions->setReactionIconWidget(like);
@ -963,8 +962,7 @@ void Controller::subscribeToSession() {
.list = update.story->recentViewers(), .list = update.story->recentViewers(),
.reactions = update.story->reactions(), .reactions = update.story->reactions(),
.total = update.story->views(), .total = update.story->views(),
.self = update.story->peer()->isSelf(), .type = RecentViewsTypeFor(update.story->peer()),
.channel = update.story->peer()->isChannel(),
}); });
updateAreas(update.story); updateAreas(update.story);
} }

View file

@ -123,6 +123,16 @@ constexpr auto kLoadViewsPages = 2;
} // namespace } // namespace
RecentViewsType RecentViewsTypeFor(not_null<PeerData*> peer) {
return peer->isSelf()
? RecentViewsType::Self
: peer->isChannel()
? RecentViewsType::Channel
: peer->isServiceUser()
? RecentViewsType::Changelog
: RecentViewsType::Other;
}
RecentViews::RecentViews(not_null<Controller*> controller) RecentViews::RecentViews(not_null<Controller*> controller)
: _controller(controller) { : _controller(controller) {
} }
@ -155,7 +165,7 @@ void RecentViews::show(
|| (_data.reactions != data.reactions); || (_data.reactions != data.reactions);
const auto usersChanged = !_userpics || (_data.list != data.list); const auto usersChanged = !_userpics || (_data.list != data.list);
_data = data; _data = data;
if (!_data.self) { if (_data.type != RecentViewsType::Self) {
_text = {}; _text = {};
_clickHandlerLifetime.destroy(); _clickHandlerLifetime.destroy();
_userpicsLifetime.destroy(); _userpicsLifetime.destroy();
@ -177,13 +187,17 @@ void RecentViews::show(
refreshClickHandler(); refreshClickHandler();
} }
if (!_data.channel) { if (_data.type != RecentViewsType::Channel
&& _data.type != RecentViewsType::Changelog) {
_likeIcon = nullptr; _likeIcon = nullptr;
_likeWrap = nullptr; _likeWrap = nullptr;
_viewsWrap = nullptr; _viewsWrap = nullptr;
} else { } else {
_viewsCounter = Lang::FormatCountDecimal(std::max(_data.total, 1)); _viewsCounter = (_data.type == RecentViewsType::Channel)
_likesCounter = _data.reactions ? Lang::FormatCountDecimal(std::max(_data.total, 1))
: tr::lng_stories_cant_reply(tr::now);
_likesCounter = ((_data.type == RecentViewsType::Channel)
&& _data.reactions)
? Lang::FormatCountDecimal(_data.reactions) ? Lang::FormatCountDecimal(_data.reactions)
: QString(); : QString();
if (!_likeWrap || !_likeIcon || !_viewsWrap) { if (!_likeWrap || !_likeIcon || !_viewsWrap) {
@ -300,14 +314,19 @@ void RecentViews::setupViewsReactions() {
st::storiesViewsText); st::storiesViewsText);
views->show(); views->show();
views->setAttribute(Qt::WA_TransparentForMouseEvents); views->setAttribute(Qt::WA_TransparentForMouseEvents);
views->move(st::storiesViewsTextPosition);
views->widthValue( views->widthValue(
) | rpl::start_with_next([=](int width) { ) | rpl::start_with_next([=](int width) {
_viewsWrap->resize(views->x() + width, _likeIcon->height()); const auto left = (_data.type == RecentViewsType::Changelog)
? st::mediaviewCaptionPadding.left()
: st::storiesViewsTextPosition.x();
views->move(left, st::storiesViewsTextPosition.y());
_viewsWrap->resize(left + width, _likeIcon->height());
updateViewsReactionsGeometry(); updateViewsReactionsGeometry();
}, _viewsWrap->lifetime()); }, _viewsWrap->lifetime());
_viewsWrap->paintRequest() | rpl::start_with_next([=] { _viewsWrap->paintRequest() | rpl::filter([=] {
return (_data.type != RecentViewsType::Changelog);
}) | rpl::start_with_next([=] {
auto p = QPainter(_viewsWrap.get()); auto p = QPainter(_viewsWrap.get());
const auto &icon = st::storiesViewsIcon; const auto &icon = st::storiesViewsIcon;
const auto top = (_viewsWrap->height() - icon.height()) / 2; const auto top = (_viewsWrap->height() - icon.height()) / 2;
@ -342,9 +361,14 @@ void RecentViews::setupViewsReactions() {
} }
void RecentViews::updateViewsReactionsGeometry() { void RecentViews::updateViewsReactionsGeometry() {
_viewsWrap->move(_outer.topLeft() + st::storiesViewsPosition); const auto outerWidth = (_data.type == RecentViewsType::Changelog)
_likeWrap->move(_outer.topLeft() ? std::max(_outer.width(), st::storiesChangelogFooterWidthMin)
+ QPoint(_outer.width() - _likeWrap->width(), 0) : _outer.width();
const auto outerOrigin = _outer.topLeft()
+ QPoint((_outer.width() - outerWidth) / 2, 0);
_viewsWrap->move(outerOrigin + st::storiesViewsPosition);
_likeWrap->move(outerOrigin
+ QPoint(outerWidth - _likeWrap->width(), 0)
+ st::storiesLikesPosition); + st::storiesLikesPosition);
} }

View file

@ -33,12 +33,18 @@ namespace Media::Stories {
class Controller; class Controller;
enum class RecentViewsType {
Other,
Self,
Channel,
Changelog,
};
struct RecentViewsData { struct RecentViewsData {
std::vector<not_null<PeerData*>> list; std::vector<not_null<PeerData*>> list;
int reactions = 0; int reactions = 0;
int total = 0; int total = 0;
bool self = false; RecentViewsType type = RecentViewsType::Other;
bool channel = false;
friend inline auto operator<=>( friend inline auto operator<=>(
const RecentViewsData &, const RecentViewsData &,
@ -48,6 +54,8 @@ struct RecentViewsData {
const RecentViewsData &) = default; const RecentViewsData &) = default;
}; };
[[nodiscard]] RecentViewsType RecentViewsTypeFor(not_null<PeerData*> peer);
class RecentViews final { class RecentViews final {
public: public:
explicit RecentViews(not_null<Controller*> controller); explicit RecentViews(not_null<Controller*> controller);

View file

@ -675,8 +675,9 @@ void ReplyArea::show(
}), }),
}); });
_controls->clear(); _controls->clear();
const auto hidden = peer && (!peer->isUser() || peer->isSelf()); const auto hidden = peer
const auto cant = !peer || peer->isServiceUser(); && (!peer->isUser() || peer->isSelf() || peer->isServiceUser());
const auto cant = !peer;
if (!hidden && !cant) { if (!hidden && !cant) {
_controls->show(); _controls->show();
} else { } else {

View file

@ -1014,3 +1014,4 @@ storiesLikeCountStyle: TextStyle(defaultTextStyle) {
linkFont: font(32px semibold); linkFont: font(32px semibold);
linkFontOver: font(32px semibold underline); linkFontOver: font(32px semibold underline);
} }
storiesChangelogFooterWidthMin: 240px;

View file

@ -255,6 +255,18 @@ void OverlayWidget::RendererGL::deinit(
_fillProgram = std::nullopt; _fillProgram = std::nullopt;
_controlsProgram = std::nullopt; _controlsProgram = std::nullopt;
_contentBuffer = std::nullopt; _contentBuffer = std::nullopt;
_controlsFadeImage.destroy(f);
_radialImage.destroy(f);
_documentBubbleImage.destroy(f);
_themePreviewImage.destroy(f);
_saveMsgImage.destroy(f);
_footerImage.destroy(f);
_captionImage.destroy(f);
_groupThumbsImage.destroy(f);
_controlsImage.destroy(f);
for (auto &part : _storiesSiblingParts) {
part.destroy(f);
}
} }
void OverlayWidget::RendererGL::paint( void OverlayWidget::RendererGL::paint(

View file

@ -7,75 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "platform/linux/file_utilities_linux.h" #include "platform/linux/file_utilities_linux.h"
#include "base/platform/linux/base_linux_app_launch_context.h"
#include "platform/linux/linux_xdp_open_with_dialog.h" #include "platform/linux/linux_xdp_open_with_dialog.h"
#include <QtGui/QDesktopServices>
#include <gio/gio.hpp>
using namespace gi::repository;
namespace Platform { namespace Platform {
namespace File { namespace File {
void UnsafeOpenUrl(const QString &url) {
{
const auto result = Gio::AppInfo::launch_default_for_uri(
url.toStdString(),
base::Platform::AppLaunchContext());
if (!result) {
LOG(("App Error: %1").arg(result.error().what()));
} else if (*result) {
return;
}
}
QDesktopServices::openUrl(url);
}
void UnsafeOpenEmailLink(const QString &email) {
UnsafeOpenUrl(u"mailto:"_q + email);
}
bool UnsafeShowOpenWith(const QString &filepath) { bool UnsafeShowOpenWith(const QString &filepath) {
if (internal::ShowXDPOpenWithDialog(filepath)) { return internal::ShowXDPOpenWithDialog(filepath);
return true;
}
return false;
}
void UnsafeLaunch(const QString &filepath) {
if ([&] {
const auto filename = GLib::filename_to_uri(filepath.toStdString());
if (!filename) {
LOG(("App Error: %1").arg(filename.error().what()));
return false;
}
const auto result = Gio::AppInfo::launch_default_for_uri(
*filename,
base::Platform::AppLaunchContext());
if (!result) {
LOG(("App Error: %1").arg(result.error().what()));
return false;
}
return *result;
}()) {
return;
}
if (UnsafeShowOpenWith(filepath)) {
return;
}
QDesktopServices::openUrl(QUrl::fromLocalFile(filepath));
} }
} // namespace File } // namespace File

View file

@ -16,10 +16,22 @@ inline QString UrlToLocal(const QUrl &url) {
return ::File::internal::UrlToLocalDefault(url); return ::File::internal::UrlToLocalDefault(url);
} }
inline void UnsafeOpenUrl(const QString &url) {
return ::File::internal::UnsafeOpenUrlDefault(url);
}
inline void UnsafeOpenEmailLink(const QString &email) {
return ::File::internal::UnsafeOpenEmailLinkDefault(email);
}
inline bool UnsafeShowOpenWithDropdown(const QString &filepath) { inline bool UnsafeShowOpenWithDropdown(const QString &filepath) {
return false; return false;
} }
inline void UnsafeLaunch(const QString &filepath) {
return ::File::internal::UnsafeLaunchDefault(filepath);
}
inline void PostprocessDownloaded(const QString &filepath) { inline void PostprocessDownloaded(const QString &filepath) {
} }

View file

@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/linux/linux_wayland_integration.h" #include "platform/linux/linux_wayland_integration.h"
#include "base/platform/linux/base_linux_wayland_utilities.h" #include "base/platform/linux/base_linux_wayland_utilities.h"
#include "base/platform/base_platform_info.h"
#include "base/qt_signal_producer.h" #include "base/qt_signal_producer.h"
#include "base/flat_map.h" #include "base/flat_map.h"
@ -20,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <qwayland-wayland.h> #include <qwayland-wayland.h>
#include <qwayland-plasma-shell.h> #include <qwayland-plasma-shell.h>
using namespace QNativeInterface; using QWlApp = QNativeInterface::QWaylandApplication;
using namespace QNativeInterface::Private; using namespace QNativeInterface::Private;
using namespace base::Platform::Wayland; using namespace base::Platform::Wayland;
@ -38,9 +37,17 @@ public:
} // namespace } // namespace
struct WaylandIntegration::Private : public AutoDestroyer<QtWayland::wl_registry> { struct WaylandIntegration::Private
: public AutoDestroyer<QtWayland::wl_registry> {
Private(not_null<QWlApp*> native)
: AutoDestroyer(wl_display_get_registry(native->display()))
, display(native->display()) {
wl_display_roundtrip(display);
}
QtWayland::org_kde_plasma_surface plasmaSurface(QWindow *window); QtWayland::org_kde_plasma_surface plasmaSurface(QWindow *window);
const not_null<wl_display*> display;
std::optional<PlasmaShell> plasmaShell; std::optional<PlasmaShell> plasmaShell;
protected: protected:
@ -102,36 +109,27 @@ QtWayland::org_kde_plasma_surface WaylandIntegration::Private::plasmaSurface(
} }
WaylandIntegration::WaylandIntegration() WaylandIntegration::WaylandIntegration()
: _private(std::make_unique<Private>()) { : _private(std::make_unique<Private>(qApp->nativeInterface<QWlApp>())) {
const auto native = qApp->nativeInterface<QWaylandApplication>();
if (!native) {
return;
}
const auto display = native->display();
if (!display) {
return;
}
_private->init(wl_display_get_registry(display));
wl_display_roundtrip(display);
} }
WaylandIntegration::~WaylandIntegration() = default; WaylandIntegration::~WaylandIntegration() = default;
WaylandIntegration *WaylandIntegration::Instance() { WaylandIntegration *WaylandIntegration::Instance() {
if (!IsWayland()) return nullptr; const auto native = qApp->nativeInterface<QWlApp>();
static std::optional<WaylandIntegration> instance(std::in_place); if (!native) return nullptr;
[[maybe_unused]] static const auto Inited = [] { static std::optional<WaylandIntegration> instance;
if (instance && native->display() != instance->_private->display) {
instance.reset();
}
if (!instance) {
instance.emplace();
base::qt_signal_producer( base::qt_signal_producer(
QGuiApplication::platformNativeInterface(), QGuiApplication::platformNativeInterface(),
&QObject::destroyed &QObject::destroyed
) | rpl::start_with_next([] { ) | rpl::start_with_next([] {
instance = std::nullopt; instance = std::nullopt;
}, instance->_private->lifetime()); }, instance->_private->lifetime());
return true; }
}();
if (!instance) return nullptr;
return &*instance; return &*instance;
} }

View file

@ -274,14 +274,14 @@ void GetInhibited(Fn<void(bool)> callback) {
[=](const Glib::RefPtr<Gio::AsyncResult> &result) { [=](const Glib::RefPtr<Gio::AsyncResult> &result) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] { Core::Sandbox::Instance().customEnterFromEventLoop([&] {
Noexcept([&] { Noexcept([&] {
const auto value = connection->call_finish( callback(
result connection->call_finish(
).get_child( result
0 ).get_child(
).get_dynamic<Glib::Variant<bool>>( 0
).get(); ).get_dynamic<Glib::Variant<bool>>(
).get()
callback(value); );
}, [&] { }, [&] {
callback(false); callback(false);
}); });

View file

@ -399,6 +399,15 @@ protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
private: private:
struct GradientParams {
int left = 0;
int width = 0;
int outer = 0;
friend inline constexpr bool operator==(
GradientParams,
GradientParams) = default;
};
void animateTo(BubbleRowState state); void animateTo(BubbleRowState state);
const style::PremiumBubble &_st; const style::PremiumBubble &_st;
@ -414,6 +423,7 @@ private:
QSize _spaceForDeflection; QSize _spaceForDeflection;
QLinearGradient _cachedGradient; QLinearGradient _cachedGradient;
std::optional<GradientParams> _cachedGradientParams;
float64 _deflection; float64 _deflection;
@ -580,13 +590,19 @@ void BubbleWidget::paintEvent(QPaintEvent *e) {
0); 0);
const auto bubbleRect = rect() - padding; const auto bubbleRect = rect() - padding;
if (_appearanceAnimation.animating()) { const auto params = GradientParams{
auto gradient = ComputeGradient( .left = x(),
.width = bubbleRect.width(),
.outer = parentWidget()->parentWidget()->width(),
};
if (_cachedGradientParams != params) {
_cachedGradient = ComputeGradient(
parentWidget(), parentWidget(),
x(), params.left,
bubbleRect.width()); params.width);
_cachedGradient = std::move(gradient); _cachedGradientParams = params;
}
if (_appearanceAnimation.animating()) {
const auto progress = _appearanceAnimation.value(1.); const auto progress = _appearanceAnimation.value(1.);
const auto finalScale = (_animatingFromResultRatio > 0.) const auto finalScale = (_animatingFromResultRatio > 0.)
|| (_state.current().ratio < 0.001); || (_state.current().ratio < 0.001);

View file

@ -506,7 +506,7 @@ void SessionNavigation::showPeerByLinkResolved(
info.messageId, info.messageId,
callback); callback);
} }
} else if (peer->isUser() && info.storyId) { } else if (info.storyId) {
const auto storyId = FullStoryId{ peer->id, info.storyId }; const auto storyId = FullStoryId{ peer->id, info.storyId };
peer->owner().stories().resolve(storyId, crl::guard(this, [=] { peer->owner().stories().resolve(storyId, crl::guard(this, [=] {
if (peer->owner().stories().lookup(storyId)) { if (peer->owner().stories().lookup(storyId)) {

View file

@ -1,7 +1,7 @@
{%- set GIT = "https://github.com" -%} {%- set GIT = "https://github.com" -%}
{%- set GIT_FREEDESKTOP = GIT ~ "/gitlab-freedesktop-mirrors" -%} {%- set GIT_FREEDESKTOP = GIT ~ "/gitlab-freedesktop-mirrors" -%}
{%- set QT = "6.5.2" -%} {%- set QT = "6.6.0" -%}
{%- set QT_TAG = "v" ~ QT -%} {%- set QT_TAG = "v" ~ QT ~ "-rc1" -%}
{%- set QT_PREFIX = "/usr/local/desktop-app/Qt-" ~ QT -%} {%- set QT_PREFIX = "/usr/local/desktop-app/Qt-" ~ QT -%}
{%- set OPENSSL_VER = "1_1_1" -%} {%- set OPENSSL_VER = "1_1_1" -%}
{%- set OPENSSL_PREFIX = "/usr/local/desktop-app/openssl-1.1.1" -%} {%- set OPENSSL_PREFIX = "/usr/local/desktop-app/openssl-1.1.1" -%}
@ -54,7 +54,7 @@ FROM builder AS patches
RUN git init patches \ RUN git init patches \
&& cd patches \ && cd patches \
&& git remote add origin {{ GIT }}/desktop-app/patches.git \ && git remote add origin {{ GIT }}/desktop-app/patches.git \
&& git fetch --depth=1 origin 2c464cfbd9fa3c1d88335cf9462c8ef96542f87c \ && git fetch --depth=1 origin 6442ae042bb6d43391747f7413c7a88a6a37c7ef \
&& git reset --hard FETCH_HEAD \ && git reset --hard FETCH_HEAD \
&& rm -rf .git && rm -rf .git

View file

@ -82,19 +82,33 @@ for singlePrefix in pathPrefixes:
pathPrefix = pathPrefix + os.path.join(rootDir, singlePrefix) + pathSep pathPrefix = pathPrefix + os.path.join(rootDir, singlePrefix) + pathSep
environment = { environment = {
'MAKE_THREADS_CNT': '-j8',
'MACOSX_DEPLOYMENT_TARGET': '10.13',
'UNGUARDED': '-Werror=unguarded-availability-new',
'MIN_VER': '-mmacosx-version-min=10.13',
'USED_PREFIX': usedPrefix, 'USED_PREFIX': usedPrefix,
'ROOT_DIR': rootDir, 'ROOT_DIR': rootDir,
'LIBS_DIR': libsDir, 'LIBS_DIR': libsDir,
'THIRDPARTY_DIR': thirdPartyDir, 'THIRDPARTY_DIR': thirdPartyDir,
'SPECIAL_TARGET': 'win' if win32 else 'win64' if win64 else 'mac',
'X8664': 'x86' if win32 else 'x64',
'WIN32X64': 'Win32' if win32 else 'x64',
'PATH_PREFIX': pathPrefix, 'PATH_PREFIX': pathPrefix,
} }
if (win32):
environment.update({
'SPECIAL_TARGET': 'win',
'X8664': 'x86',
'WIN32X64': 'Win32',
})
elif (win64):
environment.update({
'SPECIAL_TARGET': 'win64',
'X8664': 'x64',
'WIN32X64': 'x64',
})
elif (mac):
environment.update({
'SPECIAL_TARGET': 'mac',
'MAKE_THREADS_CNT': '-j8',
'MACOSX_DEPLOYMENT_TARGET': '10.13',
'UNGUARDED': '-Werror=unguarded-availability-new',
'MIN_VER': '-mmacosx-version-min=10.13',
})
ignoreInCacheForThirdParty = [ ignoreInCacheForThirdParty = [
'USED_PREFIX', 'USED_PREFIX',
'LIBS_DIR', 'LIBS_DIR',
@ -404,7 +418,7 @@ if customRunCommand:
stage('patches', """ stage('patches', """
git clone https://github.com/desktop-app/patches.git git clone https://github.com/desktop-app/patches.git
cd patches cd patches
git checkout b1907e1250 git checkout 81a81ffb5a
""") """)
stage('msys64', """ stage('msys64', """
@ -461,9 +475,9 @@ win:
cd gyp cd gyp
git checkout 9d09418933 git checkout 9d09418933
mac: mac:
python3 -m pip install ^ python3 -m pip install \\
--ignore-installed ^ --ignore-installed \\
--target=$THIRDPARTY_DIR/gyp ^ --target=$THIRDPARTY_DIR/gyp \\
git+https://chromium.googlesource.com/external/gyp@master git+https://chromium.googlesource.com/external/gyp@master
""", 'ThirdParty') """, 'ThirdParty')
@ -1144,8 +1158,7 @@ depends:patches/breakpad.diff
cd src/third_party/lss cd src/third_party/lss
git checkout e1e7b0ad8e git checkout e1e7b0ad8e
cd ../../build cd ../../build
PYTHONPATH=$THIRDPARTY_DIR/gyp PYTHONPATH=$THIRDPARTY_DIR/gyp python3 gyp_breakpad
python3 gyp_breakpad
cd ../processor cd ../processor
xcodebuild -project processor.xcodeproj -target minidump_stackwalk -configuration Release build xcodebuild -project processor.xcodeproj -target minidump_stackwalk -configuration Release build
""") """)

View file

@ -1,7 +1,7 @@
AppVersion 4010000 AppVersion 4010001
AppVersionStrMajor 4.10 AppVersionStrMajor 4.10
AppVersionStrSmall 4.10 AppVersionStrSmall 4.10.1
AppVersionStr 4.10.0 AppVersionStr 4.10.1
BetaChannel 0 BetaChannel 0
AlphaVersion 0 AlphaVersion 0
AppVersionOriginal 4.10 AppVersionOriginal 4.10.1

@ -1 +1 @@
Subproject commit 6de11e6ab705f27158f338e3e17c8b0767750721 Subproject commit d67a11776ad720a718bae026b78ddd150f13fac5

@ -1 +1 @@
Subproject commit cea8958ca2007192dbd6ce14967f74484d1fcc6f Subproject commit 5c4f890f356f1ccb1401581b2a960b35455a7488

View file

@ -1,3 +1,7 @@
4.10.1 (23.09.23)
- Rebuild macOS version with Xcode 14.0.1.
4.10 (22.09.23) 4.10 (22.09.23)
- Stories for Channels. - Stories for Channels.

2
cmake

@ -1 +1 @@
Subproject commit 0ae4e78a12d7fdea687ca52647465b9cf9ef95ee Subproject commit 218a34d9b5e3267b86a938b48bd74c045455bd3c