diff --git a/.github/workflows/snap.yml b/.github/workflows/snap.yml index 4191aced9..c2e5dbafe 100644 --- a/.github/workflows/snap.yml +++ b/.github/workflows/snap.yml @@ -61,7 +61,7 @@ jobs: sudo snap run lxd waitready - name: Free up some disk space. - uses: jlumbroso/free-disk-space@76866dbe54312617f00798d1762df7f43def6e5c + uses: jlumbroso/free-disk-space@f68fdb76e2ea636224182cfb7377ff9a1708f9b8 - name: Telegram Desktop snap build. run: sg lxd -c 'snap run snapcraft -v' diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index f5ee77da9..a64e3b239 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="4.10.2.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 64f248acc..3c262b4b3 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,10,1,0 - PRODUCTVERSION 4,10,1,0 + FILEVERSION 4,10,2,0 + PRODUCTVERSION 4,10,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Radolyn Labs" VALUE "FileDescription", "AyuGram Desktop" - VALUE "FileVersion", "4.10.1.0" + VALUE "FileVersion", "4.10.2.0" VALUE "LegalCopyright", "Copyright (C) 2014-2023" VALUE "ProductName", "AyuGram Desktop" - VALUE "ProductVersion", "4.10.1.0" + VALUE "ProductVersion", "4.10.2.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 1d26f8846..760e5f1b7 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,10,1,0 - PRODUCTVERSION 4,10,1,0 + FILEVERSION 4,10,2,0 + PRODUCTVERSION 4,10,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Radolyn Labs" VALUE "FileDescription", "AyuGram Desktop Updater" - VALUE "FileVersion", "4.10.1.0" + VALUE "FileVersion", "4.10.2.0" VALUE "LegalCopyright", "Copyright (C) 2014-2023" VALUE "ProductName", "AyuGram Desktop" - VALUE "ProductVersion", "4.10.1.0" + VALUE "ProductVersion", "4.10.2.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 5b95559f5..3c3270595 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -2450,7 +2450,13 @@ void ApiWrap::refreshFileReference( }; v::match(origin.data, [&](Data::FileOriginMessage data) { if (const auto item = _session->data().message(data)) { - if (item->isScheduled()) { + const auto media = item->media(); + const auto storyId = media ? media->storyId() : FullStoryId(); + if (storyId) { + request(MTPstories_GetStoriesByID( + _session->data().peer(storyId.peer)->input, + MTP_vector(1, MTP_int(storyId.story)))); + } else if (item->isScheduled()) { const auto &scheduled = _session->data().scheduledMessages(); const auto realId = scheduled.lookupId(item); request(MTPmessages_GetScheduledMessages( diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 65c0b2aa8..ea54c2b39 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs; constexpr auto AppNameOld = "AyuGram for Windows"_cs; constexpr auto AppName = "AyuGram Desktop"_cs; constexpr auto AppFile = "AyuGram"_cs; -constexpr auto AppVersion = 4010001; -constexpr auto AppVersionStr = "4.10.1"; +constexpr auto AppVersion = 4010002; +constexpr auto AppVersionStr = "4.10.2"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/SourceFiles/data/data_stories.cpp b/Telegram/SourceFiles/data/data_stories.cpp index e9474d26a..d33412478 100644 --- a/Telegram/SourceFiles/data/data_stories.cpp +++ b/Telegram/SourceFiles/data/data_stories.cpp @@ -1230,7 +1230,7 @@ void Stories::toggleHidden( const auto name = peer->shortName(); const auto guard = gsl::finally([&] { - if (show) { + if (show && !justRemove) { const auto phrase = hidden ? tr::lng_stories_hidden_to_contacts : tr::lng_stories_shown_in_chats; diff --git a/Telegram/SourceFiles/data/data_story.cpp b/Telegram/SourceFiles/data/data_story.cpp index 237c3323d..13c453e06 100644 --- a/Telegram/SourceFiles/data/data_story.cpp +++ b/Telegram/SourceFiles/data/data_story.cpp @@ -43,6 +43,19 @@ using UpdateFlag = StoryUpdate::Flag; }; } +[[nodiscard]] TextWithEntities StripLinks(TextWithEntities text) { + const auto link = [&](const EntityInText &entity) { + return (entity.type() == EntityType::CustomUrl) + || (entity.type() == EntityType::Url) + || (entity.type() == EntityType::Mention) + || (entity.type() == EntityType::Hashtag); + }; + text.entities.erase( + ranges::remove_if(text.entities, link), + text.entities.end()); + return text; +} + [[nodiscard]] auto ParseLocation(const MTPMediaArea &area) -> std::optional { auto result = std::optional(); @@ -586,6 +599,11 @@ void Story::applyFields( &owner().session(), data.ventities().value_or_empty()), }; + if (const auto user = _peer->asUser()) { + if (!user->isVerified() && !user->isPremium()) { + caption = StripLinks(std::move(caption)); + } + } auto counts = ViewsCounts(); auto viewsKnown = _views.known; if (const auto info = data.vviews()) { diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.cpp b/Telegram/SourceFiles/dialogs/dialogs_row.cpp index c937f10f2..a6459bbc4 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_row.cpp @@ -444,10 +444,12 @@ void Row::paintUserpic( const auto cornerBadgeShown = !_cornerBadgeUserpic ? _cornerBadgeShown : !_cornerBadgeUserpic->layersManager.isDisplayedNone(); - const auto storiesUser = peer ? peer->asUser() : nullptr; + const auto storiesPeer = peer + ? ((peer->isUser() || peer->isBroadcast()) ? peer : nullptr) + : nullptr; const auto storiesFolder = peer ? nullptr : _id.folder(); - const auto storiesHas = storiesUser - ? storiesUser->hasActiveStories() + const auto storiesHas = storiesPeer + ? storiesPeer->hasActiveStories() : storiesFolder ? storiesFolder->storiesCount() : false; @@ -467,8 +469,8 @@ void Row::paintUserpic( const auto frameSide = (2 * framePadding + context.st->photoSize) * ratio; const auto frameSize = QSize(frameSide, frameSide); - const auto storiesSource = (storiesHas && storiesUser) - ? storiesUser->owner().stories().source(storiesUser->id) + const auto storiesSource = (storiesHas && storiesPeer) + ? storiesPeer->owner().stories().source(storiesPeer->id) : nullptr; const auto storiesCountReal = storiesSource ? int(storiesSource->ids.size()) @@ -481,7 +483,7 @@ void Row::paintUserpic( ? storiesSource->unreadCount() : storiesFolder ? storiesFolder->storiesUnreadCount() - : (storiesUser && storiesUser->hasUnreadStories()) + : (storiesPeer && storiesPeer->hasUnreadStories()) ? 1 : 0; const auto limit = Ui::kOutlineSegmentsMax; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 62b98a007..0bc3d256d 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -2336,8 +2336,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { const auto item = _dragStateItem; const auto itemId = item ? item->fullId() : FullMsgId(); if (isUponSelected > 0) { + const auto selectedText = getSelectedText(); if (!hasCopyRestrictionForSelected() - && !getSelectedText().empty()) { + && !selectedText.empty()) { _menu->addAction( (isUponSelected > 1 ? tr::lng_context_copy_selected_items(tr::now) @@ -2345,11 +2346,12 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { [=] { copySelectedText(); }, &st::menuIconCopy); } - if (!Ui::SkipTranslate(getSelectedText().rich)) { + if (item && !Ui::SkipTranslate(selectedText.rich)) { + const auto peer = item->history()->peer; _menu->addAction(tr::lng_context_translate_selected({}), [=] { _controller->show(Box( Ui::TranslateBox, - item->history()->peer, + peer, MsgId(), getSelectedText().rich, hasCopyRestrictionForSelected())); @@ -2452,7 +2454,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { [=] { copySelectedText(); }, &st::menuIconCopy); } - if (!Ui::SkipTranslate(selectedText.rich)) { + if (item && !Ui::SkipTranslate(selectedText.rich)) { const auto peer = item->history()->peer; _menu->addAction(tr::lng_context_translate_selected({}), [=] { _controller->show(Box( diff --git a/Telegram/SourceFiles/mtproto/scheme/api.tl b/Telegram/SourceFiles/mtproto/scheme/api.tl index 93ee43704..8a386d988 100644 --- a/Telegram/SourceFiles/mtproto/scheme/api.tl +++ b/Telegram/SourceFiles/mtproto/scheme/api.tl @@ -1564,7 +1564,7 @@ peerStories#9a35e999 flags:# peer:Peer max_read_id:flags.0?int stories:Vector users:Vector = stories.PeerStories; -stories.boostsStatus#66ea1fef flags:# my_boost:flags.2?true level:int current_level_boosts:int boosts:int next_level_boosts:flags.0?int premium_audience:flags.1?StatsPercentValue = stories.BoostsStatus; +stories.boostsStatus#e5c1aa5c flags:# my_boost:flags.2?true level:int current_level_boosts:int boosts:int next_level_boosts:flags.0?int premium_audience:flags.1?StatsPercentValue boost_url:string = stories.BoostsStatus; stories.canApplyBoostOk#c3173587 = stories.CanApplyBoostResult; stories.canApplyBoostReplace#712c4655 current_boost:Peer chats:Vector = stories.CanApplyBoostResult; @@ -1573,6 +1573,8 @@ booster#e9e6380 user_id:long expires:int = Booster; stories.boostersList#f3dd3d1d flags:# count:int boosters:Vector next_offset:flags.0?string users:Vector = stories.BoostersList; +messages.webPage#fd5e12bd webpage:WebPage chats:Vector users:Vector = messages.WebPage; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1801,7 +1803,7 @@ messages.setInlineGameScore#15ad9f64 flags:# edit_message:flags.0?true force:fla messages.getGameHighScores#e822649d peer:InputPeer id:int user_id:InputUser = messages.HighScores; messages.getInlineGameHighScores#f635e1b id:InputBotInlineMessageID user_id:InputUser = messages.HighScores; messages.getCommonChats#e40ca104 user_id:InputUser max_id:long limit:int = messages.Chats; -messages.getWebPage#32ca8f91 url:string hash:int = WebPage; +messages.getWebPage#8d9692a3 url:string hash:int = messages.WebPage; messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool; messages.reorderPinnedDialogs#3b1adf37 flags:# force:flags.0?true folder_id:int order:Vector = Bool; messages.getPinnedDialogs#d6b94df2 folder_id:int = messages.PeerDialogs; diff --git a/Telegram/SourceFiles/mtproto/scheme/layer.tl b/Telegram/SourceFiles/mtproto/scheme/layer.tl index dcdebd9f5..7a644419f 100644 --- a/Telegram/SourceFiles/mtproto/scheme/layer.tl +++ b/Telegram/SourceFiles/mtproto/scheme/layer.tl @@ -1 +1 @@ -// LAYER 164 +// LAYER 165 diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp index 58420b719..612b0c35e 100644 --- a/Telegram/SourceFiles/settings/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/settings_premium.cpp @@ -33,10 +33,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_premium.h" #include "ui/abstract_button.h" #include "ui/basic_click_handlers.h" -#include "ui/color_contrast.h" #include "ui/effects/gradient.h" #include "ui/effects/premium_graphics.h" #include "ui/effects/premium_stars_colored.h" +#include "ui/effects/premium_top_bar.h" #include "ui/layers/generic_box.h" #include "ui/text/format_values.h" #include "ui/text/text_utilities.h" @@ -71,41 +71,6 @@ namespace { using SectionCustomTopBarData = Info::Settings::SectionCustomTopBarData; -constexpr auto kBodyAnimationPart = 0.90; -constexpr auto kTitleAdditionalScale = 0.15; -constexpr auto kMinAcceptableContrast = 4.5; // 1.14; - -[[nodiscard]] QString Svg() { - return u":/gui/icons/settings/star.svg"_q; -} - -[[nodiscard]] QByteArray ColorizedSvg() { - auto f = QFile(Svg()); - if (!f.open(QIODevice::ReadOnly)) { - return QByteArray(); - } - auto content = qs(f.readAll()); - auto stops = [] { - auto s = QString(); - for (const auto &stop : Ui::Premium::ButtonGradientStops()) { - s += QString("") - .arg(QString::number(stop.first), stop.second.name()); - } - return s; - }(); - const auto color = QString("%5") - .arg(0) - .arg(1) - .arg(1) - .arg(0) - .arg(std::move(stops)); - content.replace(u"gradientPlaceholder"_q, color); - content.replace(u"#fff"_q, u"url(#Gradient2)"_q); - f.close(); - return content.toUtf8(); -} - [[nodiscard]] Data::SubscriptionOptions SubscriptionOptionsForRows( Data::SubscriptionOptions result) { for (auto &option : result) { @@ -416,85 +381,6 @@ void SendScreenAccept(not_null controller) { MTP_jsonNull()); } -class TopBarAbstract : public Ui::RpWidget { -public: - using Ui::RpWidget::RpWidget; - - void setRoundEdges(bool value); - - virtual void setPaused(bool paused) = 0; - virtual void setTextPosition(int x, int y) = 0; - - [[nodiscard]] virtual rpl::producer additionalHeight() const = 0; - -protected: - void paintEdges(QPainter &p, const QBrush &brush) const; - void paintEdges(QPainter &p) const; - - [[nodiscard]] QRectF starRect( - float64 topProgress, - float64 sizeProgress) const; - - [[nodiscard]] bool isDark() const; - void computeIsDark(); - -private: - bool _roundEdges = true; - bool _isDark = false; - -}; - -void TopBarAbstract::setRoundEdges(bool value) { - _roundEdges = value; - update(); -} - -void TopBarAbstract::paintEdges(QPainter &p, const QBrush &brush) const { - const auto r = rect(); - if (_roundEdges) { - PainterHighQualityEnabler hq(p); - const auto radius = st::boxRadius; - p.setPen(Qt::NoPen); - p.setBrush(brush); - p.drawRoundedRect( - r + QMargins{ 0, 0, 0, radius + 1 }, - radius, - radius); - } else { - p.fillRect(r, brush); - } -} - -void TopBarAbstract::paintEdges(QPainter &p) const { - paintEdges(p, st::boxBg); - if (isDark()) { - paintEdges(p, st::shadowFg); - paintEdges(p, st::shadowFg); - } -} - -QRectF TopBarAbstract::starRect( - float64 topProgress, - float64 sizeProgress) const { - const auto starSize = st::settingsPremiumStarSize * sizeProgress; - return QRectF( - QPointF( - (width() - starSize.width()) / 2, - st::settingsPremiumStarTopSkip * topProgress), - starSize); -}; - -bool TopBarAbstract::isDark() const { - return _isDark; -} - -void TopBarAbstract::computeIsDark() { - const auto contrast = Ui::CountContrast( - st::boxBg->c, - st::premiumButtonFg->c); - _isDark = (contrast > kMinAcceptableContrast); -} - class EmojiStatusTopBar final { public: EmojiStatusTopBar( @@ -587,7 +473,7 @@ void EmojiStatusTopBar::paint(QPainter &p) { } } -class TopBarUser final : public TopBarAbstract { +class TopBarUser final : public Ui::Premium::TopBarAbstract { public: TopBarUser( not_null parent, @@ -678,38 +564,8 @@ TopBarUser::TopBarUser( HistoryView::Sticker::EmojiSize()); _imageStar = QImage(); } else { - auto svg = QSvgRenderer(Svg()); - - const auto size = _starRect.size().toSize(); - auto frame = QImage( - size * style::DevicePixelRatio(), - QImage::Format_ARGB32_Premultiplied); - frame.setDevicePixelRatio(style::DevicePixelRatio()); - - auto mask = frame; - mask.fill(Qt::transparent); - { - auto p = QPainter(&mask); - auto gradient = QLinearGradient( - 0, - size.height(), - size.width(), - 0); - gradient.setStops(Ui::Premium::ButtonGradientStops()); - p.setPen(Qt::NoPen); - p.setBrush(gradient); - p.drawRect(0, 0, size.width(), size.height()); - } - frame.fill(Qt::transparent); - { - auto q = QPainter(&frame); - svg.render(&q, QRect(QPoint(), size)); - q.setCompositionMode(QPainter::CompositionMode_SourceIn); - q.drawImage(0, 0, mask); - } - _imageStar = std::move(frame); - _emojiStatus = nullptr; + _imageStar = Ui::Premium::GenerateStarForLightTopBar(_starRect); } updateTitle(document, { name }, controller); @@ -929,194 +785,6 @@ void TopBarUser::resizeEvent(QResizeEvent *e) { } } -class TopBar final : public TopBarAbstract { -public: - TopBar( - not_null parent, - not_null controller, - rpl::producer title, - rpl::producer about); - - void setPaused(bool paused) override; - void setTextPosition(int x, int y) override; - - rpl::producer additionalHeight() const override; - -protected: - void paintEvent(QPaintEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - -private: - const style::font &_titleFont; - const style::margins &_titlePadding; - object_ptr _about; - Ui::Premium::ColoredMiniStars _ministars; - QSvgRenderer _star; - - struct { - float64 top = 0.; - float64 body = 0.; - float64 title = 0.; - float64 scaleTitle = 0.; - } _progress; - - QRectF _starRect; - - QPoint _titlePosition; - QPainterPath _titlePath; - -}; - -TopBar::TopBar( - not_null parent, - not_null controller, - rpl::producer title, - rpl::producer about) -: TopBarAbstract(parent) -, _titleFont(st::boxTitle.style.font) -, _titlePadding(st::settingsPremiumTitlePadding) -, _about(this, std::move(about), st::settingsPremiumAbout) -, _ministars(this) { - std::move( - title - ) | rpl::start_with_next([=](QString text) { - _titlePath = QPainterPath(); - _titlePath.addText(0, _titleFont->ascent, _titleFont, text); - update(); - }, lifetime()); - - _about->setClickHandlerFilter([=]( - const ClickHandlerPtr &handler, - Qt::MouseButton button) { - ActivateClickHandler(_about, handler, { - button, - QVariant::fromValue(ClickHandlerContext{ - .sessionWindow = base::make_weak(controller), - .botStartAutoSubmit = true, - }) - }); - return false; - }); - - rpl::single() | rpl::then( - style::PaletteChanged() - ) | rpl::start_with_next([=] { - TopBarAbstract::computeIsDark(); - - if (!TopBarAbstract::isDark()) { - _star.load(Svg()); - _ministars.setColorOverride(st::premiumButtonFg->c); - } else { - _star.load(ColorizedSvg()); - _ministars.setColorOverride(std::nullopt); - } - auto event = QResizeEvent(size(), size()); - resizeEvent(&event); - }, lifetime()); -} - -void TopBar::setPaused(bool paused) { - _ministars.setPaused(paused); -} - -void TopBar::setTextPosition(int x, int y) { - _titlePosition = { x, y }; -} - -rpl::producer TopBar::additionalHeight() const { - return _about->heightValue( - ) | rpl::map([l = st::settingsPremiumAbout.style.lineHeight](int height) { - return std::max(height - l * 2, 0); - }); -} - -void TopBar::resizeEvent(QResizeEvent *e) { - const auto progress = (e->size().height() - minimumHeight()) - / float64(maximumHeight() - minimumHeight()); - _progress.top = 1. - - std::clamp( - (1. - progress) / kBodyAnimationPart, - 0., - 1.); - _progress.body = _progress.top; - _progress.title = 1. - progress; - _progress.scaleTitle = 1. + kTitleAdditionalScale * progress; - - _ministars.setCenter(starRect(_progress.top, 1.).toRect()); - - _starRect = starRect(_progress.top, _progress.body); - - const auto &padding = st::boxRowPadding; - const auto availableWidth = width() - padding.left() - padding.right(); - const auto titleTop = _starRect.top() - + _starRect.height() - + _titlePadding.top(); - const auto titlePathRect = _titlePath.boundingRect(); - const auto aboutTop = titleTop - + titlePathRect.height() - + _titlePadding.bottom(); - _about->resizeToWidth(availableWidth); - _about->moveToLeft(padding.left(), aboutTop); - _about->setOpacity(_progress.body); - - Ui::RpWidget::resizeEvent(e); -} - -void TopBar::paintEvent(QPaintEvent *e) { - auto p = QPainter(this); - - p.fillRect(e->rect(), Qt::transparent); - - const auto r = rect(); - - if (!TopBarAbstract::isDark()) { - const auto gradientPointTop = r.height() / 3. * 2.; - auto gradient = QLinearGradient( - QPointF(0, gradientPointTop), - QPointF(r.width(), r.height() - gradientPointTop)); - gradient.setStops(Ui::Premium::ButtonGradientStops()); - - TopBarAbstract::paintEdges(p, gradient); - } else { - TopBarAbstract::paintEdges(p); - } - - p.setOpacity(_progress.body); - p.translate(_starRect.center()); - p.scale(_progress.body, _progress.body); - p.translate(-_starRect.center()); - if (_progress.top) { - _ministars.paint(p); - } - p.resetTransform(); - - _star.render(&p, _starRect); - - p.setPen(st::premiumButtonFg); - - const auto titlePathRect = _titlePath.boundingRect(); - - // Title. - PainterHighQualityEnabler hq(p); - p.setOpacity(1.); - p.setFont(_titleFont); - const auto fullStarRect = starRect(1., 1.); - const auto fullTitleTop = fullStarRect.top() - + fullStarRect.height() - + _titlePadding.top(); - p.translate( - anim::interpolate( - (width() - titlePathRect.width()) / 2, - _titlePosition.x(), - _progress.title), - anim::interpolate(fullTitleTop, _titlePosition.y(), _progress.title)); - - p.translate(titlePathRect.center()); - p.scale(_progress.scaleTitle, _progress.scaleTitle); - p.translate(-titlePathRect.center()); - p.fillPath(_titlePath, st::premiumButtonFg); -} - class Premium : public Section { public: Premium( @@ -1499,7 +1167,7 @@ QPointer Premium::createPinnedToTop( return nullptr; }(); - const auto content = [&]() -> TopBarAbstract* { + const auto content = [&]() -> Ui::Premium::TopBarAbstract* { if (peerWithPremium) { return Ui::CreateChild( parent.get(), @@ -1507,9 +1175,16 @@ QPointer Premium::createPinnedToTop( peerWithPremium, _showFinished.events()); } - return Ui::CreateChild( + const auto weak = base::make_weak(_controller); + const auto clickContextOther = [=] { + return QVariant::fromValue(ClickHandlerContext{ + .sessionWindow = weak, + .botStartAutoSubmit = true, + }); + }; + return Ui::CreateChild( parent.get(), - _controller, + clickContextOther, std::move(title), std::move(about)); }(); diff --git a/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp b/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp new file mode 100644 index 000000000..309db7a6a --- /dev/null +++ b/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp @@ -0,0 +1,317 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/effects/premium_top_bar.h" + +#include "ui/color_contrast.h" +#include "ui/painter.h" +#include "ui/effects/premium_graphics.h" +#include "ui/widgets/labels.h" +#include "ui/wrap/fade_wrap.h" +#include "styles/style_layers.h" +#include "styles/style_settings.h" + +#include + +namespace Ui::Premium { +namespace { + +constexpr auto kBodyAnimationPart = 0.90; +constexpr auto kTitleAdditionalScale = 0.15; +constexpr auto kMinAcceptableContrast = 4.5; // 1.14; + +} // namespace + +QString Svg() { + return u":/gui/icons/settings/star.svg"_q; +} + +QByteArray ColorizedSvg() { + auto f = QFile(Svg()); + if (!f.open(QIODevice::ReadOnly)) { + return QByteArray(); + } + auto content = QString::fromUtf8(f.readAll()); + auto stops = [] { + auto s = QString(); + for (const auto &stop : Ui::Premium::ButtonGradientStops()) { + s += QString("") + .arg(QString::number(stop.first), stop.second.name()); + } + return s; + }(); + const auto color = QString("%5") + .arg(0) + .arg(1) + .arg(1) + .arg(0) + .arg(std::move(stops)); + content.replace(u"gradientPlaceholder"_q, color); + content.replace(u"#fff"_q, u"url(#Gradient2)"_q); + f.close(); + return content.toUtf8(); +} + +QImage GenerateStarForLightTopBar(QRectF rect) { + auto svg = QSvgRenderer(Ui::Premium::Svg()); + + const auto size = rect.size().toSize(); + auto frame = QImage( + size * style::DevicePixelRatio(), + QImage::Format_ARGB32_Premultiplied); + frame.setDevicePixelRatio(style::DevicePixelRatio()); + + auto mask = frame; + mask.fill(Qt::transparent); + { + auto p = QPainter(&mask); + auto gradient = QLinearGradient( + 0, + size.height(), + size.width(), + 0); + gradient.setStops(Ui::Premium::ButtonGradientStops()); + p.setPen(Qt::NoPen); + p.setBrush(gradient); + p.drawRect(0, 0, size.width(), size.height()); + } + frame.fill(Qt::transparent); + { + auto q = QPainter(&frame); + svg.render(&q, QRect(QPoint(), size)); + q.setCompositionMode(QPainter::CompositionMode_SourceIn); + q.drawImage(0, 0, mask); + } + return frame; +} + +void TopBarAbstract::setRoundEdges(bool value) { + _roundEdges = value; + update(); +} + +void TopBarAbstract::paintEdges(QPainter &p, const QBrush &brush) const { + const auto r = rect(); + if (_roundEdges) { + PainterHighQualityEnabler hq(p); + const auto radius = st::boxRadius; + p.setPen(Qt::NoPen); + p.setBrush(brush); + p.drawRoundedRect( + r + QMargins{ 0, 0, 0, radius + 1 }, + radius, + radius); + } else { + p.fillRect(r, brush); + } +} + +void TopBarAbstract::paintEdges(QPainter &p) const { + paintEdges(p, st::boxBg); + if (isDark()) { + paintEdges(p, st::shadowFg); + paintEdges(p, st::shadowFg); + } +} + +QRectF TopBarAbstract::starRect( + float64 topProgress, + float64 sizeProgress) const { + const auto starSize = st::settingsPremiumStarSize * sizeProgress; + return QRectF( + QPointF( + (width() - starSize.width()) / 2, + st::settingsPremiumStarTopSkip * topProgress), + starSize); +}; + +bool TopBarAbstract::isDark() const { + return _isDark; +} + +void TopBarAbstract::computeIsDark() { + const auto contrast = CountContrast( + st::boxBg->c, + st::premiumButtonFg->c); + _isDark = (contrast > kMinAcceptableContrast); +} + +TopBar::TopBar( + not_null parent, + Fn clickContextOther, + rpl::producer title, + rpl::producer about, + bool light) +: TopBarAbstract(parent) +, _light(light) +, _titleFont(st::boxTitle.style.font) +, _titlePadding(st::settingsPremiumTitlePadding) +, _about( + this, + std::move(about), + _light ? st::settingsPremiumUserAbout : st::settingsPremiumAbout) +, _ministars(this) { + std::move( + title + ) | rpl::start_with_next([=](QString text) { + _titlePath = QPainterPath(); + _titlePath.addText(0, _titleFont->ascent, _titleFont, text); + update(); + }, lifetime()); + + if (clickContextOther) { + _about->setClickHandlerFilter([=]( + const ClickHandlerPtr &handler, + Qt::MouseButton button) { + ActivateClickHandler(_about, handler, { + button, + clickContextOther() + }); + return false; + }); + } + + rpl::single() | rpl::then( + style::PaletteChanged() + ) | rpl::start_with_next([=] { + TopBarAbstract::computeIsDark(); + + if (!_light && !TopBarAbstract::isDark()) { + _star.load(Svg()); + _ministars.setColorOverride(st::premiumButtonFg->c); + } else { + _star.load(ColorizedSvg()); + _ministars.setColorOverride(std::nullopt); + } + auto event = QResizeEvent(size(), size()); + resizeEvent(&event); + }, lifetime()); + + if (_light) { + const auto smallTopShadow = CreateChild(this); + smallTopShadow->setDuration(st::fadeWrapDuration); + sizeValue( + ) | rpl::start_with_next([=](QSize size) { + smallTopShadow->resizeToWidth(size.width()); + smallTopShadow->moveToLeft( + 0, + height() - smallTopShadow->height()); + const auto shown = (minimumHeight() * 2 > size.height()); + smallTopShadow->toggle(shown, anim::type::normal); + }, lifetime()); + } +} + +TopBar::~TopBar() = default; + +void TopBar::setPaused(bool paused) { + _ministars.setPaused(paused); +} + +void TopBar::setTextPosition(int x, int y) { + _titlePosition = { x, y }; +} + +rpl::producer TopBar::additionalHeight() const { + return _about->heightValue( + ) | rpl::map([l = st::settingsPremiumAbout.style.lineHeight](int height) { + return std::max(height - l * 2, 0); + }); +} + +void TopBar::resizeEvent(QResizeEvent *e) { + const auto progress = (e->size().height() - minimumHeight()) + / float64(maximumHeight() - minimumHeight()); + _progress.top = 1. - + std::clamp( + (1. - progress) / kBodyAnimationPart, + 0., + 1.); + _progress.body = _progress.top; + _progress.title = 1. - progress; + _progress.scaleTitle = 1. + kTitleAdditionalScale * progress; + + _ministars.setCenter(starRect(_progress.top, 1.).toRect()); + + _starRect = starRect(_progress.top, _progress.body); + + const auto &padding = st::boxRowPadding; + const auto availableWidth = width() - padding.left() - padding.right(); + const auto titleTop = _starRect.top() + + _starRect.height() + + _titlePadding.top(); + const auto titlePathRect = _titlePath.boundingRect(); + const auto aboutTop = titleTop + + titlePathRect.height() + + _titlePadding.bottom(); + _about->resizeToWidth(availableWidth); + _about->moveToLeft(padding.left(), aboutTop); + _about->setOpacity(_progress.body); + + RpWidget::resizeEvent(e); +} + +void TopBar::paintEvent(QPaintEvent *e) { + auto p = QPainter(this); + + p.fillRect(e->rect(), Qt::transparent); + + const auto r = rect(); + + if (!_light && !TopBarAbstract::isDark()) { + const auto gradientPointTop = r.height() / 3. * 2.; + auto gradient = QLinearGradient( + QPointF(0, gradientPointTop), + QPointF(r.width(), r.height() - gradientPointTop)); + gradient.setStops(ButtonGradientStops()); + + TopBarAbstract::paintEdges(p, gradient); + } else { + TopBarAbstract::paintEdges(p); + } + + p.setOpacity(_progress.body); + p.translate(_starRect.center()); + p.scale(_progress.body, _progress.body); + p.translate(-_starRect.center()); + if (_progress.top) { + _ministars.paint(p); + } + p.resetTransform(); + + _star.render(&p, _starRect); + + const auto color = _light + ? st::settingsPremiumUserTitle.textFg + : st::premiumButtonFg; + p.setPen(color); + + const auto titlePathRect = _titlePath.boundingRect(); + + // Title. + PainterHighQualityEnabler hq(p); + p.setOpacity(1.); + p.setFont(_titleFont); + const auto fullStarRect = starRect(1., 1.); + const auto fullTitleTop = fullStarRect.top() + + fullStarRect.height() + + _titlePadding.top(); + p.translate( + anim::interpolate( + (width() - titlePathRect.width()) / 2, + _titlePosition.x(), + _progress.title), + anim::interpolate(fullTitleTop, _titlePosition.y(), _progress.title)); + + p.translate(titlePathRect.center()); + p.scale(_progress.scaleTitle, _progress.scaleTitle); + p.translate(-titlePathRect.center()); + p.fillPath(_titlePath, color); +} + +} // namespace Ui::Premium diff --git a/Telegram/SourceFiles/ui/effects/premium_top_bar.h b/Telegram/SourceFiles/ui/effects/premium_top_bar.h new file mode 100644 index 000000000..0cef38dc5 --- /dev/null +++ b/Telegram/SourceFiles/ui/effects/premium_top_bar.h @@ -0,0 +1,93 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "base/object_ptr.h" +#include "ui/rp_widget.h" +#include "ui/effects/premium_stars_colored.h" + +namespace Ui { +class FlatLabel; +} // namespace Ui + +namespace Ui::Premium { + +[[nodiscard]] QString Svg(); +[[nodiscard]] QByteArray ColorizedSvg(); +[[nodiscard]] QImage GenerateStarForLightTopBar(QRectF rect); + +class TopBarAbstract : public RpWidget { +public: + using RpWidget::RpWidget; + + void setRoundEdges(bool value); + + virtual void setPaused(bool paused) = 0; + virtual void setTextPosition(int x, int y) = 0; + + [[nodiscard]] virtual rpl::producer additionalHeight() const = 0; + +protected: + void paintEdges(QPainter &p, const QBrush &brush) const; + void paintEdges(QPainter &p) const; + + [[nodiscard]] QRectF starRect( + float64 topProgress, + float64 sizeProgress) const; + + [[nodiscard]] bool isDark() const; + void computeIsDark(); + +private: + bool _roundEdges = true; + bool _isDark = false; + +}; + +class TopBar final : public TopBarAbstract { +public: + TopBar( + not_null parent, + Fn clickContextOther, + rpl::producer title, + rpl::producer about, + bool light = false); + ~TopBar(); + + void setPaused(bool paused) override; + void setTextPosition(int x, int y) override; + + rpl::producer additionalHeight() const override; + +protected: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + +private: + const bool _light = false; + const style::font &_titleFont; + const style::margins &_titlePadding; + object_ptr _about; + ColoredMiniStars _ministars; + QSvgRenderer _star; + + struct { + float64 top = 0.; + float64 body = 0.; + float64 title = 0.; + float64 scaleTitle = 0.; + } _progress; + + QRectF _starRect; + + QPoint _titlePosition; + QPainterPath _titlePath; + +}; + +} // namespace Ui::Premium diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index c6cc7117f..1a92eb135 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -211,7 +211,7 @@ FROM builder AS libvpx RUN git init libvpx \ && cd libvpx \ && git remote add origin {{ GIT }}/webmproject/libvpx.git \ - && git fetch --depth=1 origin e1c124f8965f166d3e9ca26c9215ebc3ec3a1d72 \ + && git fetch --depth=1 origin 51057f4ba894e13f9bba278905bacf6aaaecd992 \ && git reset --hard FETCH_HEAD \ && CFLAGS="$CFLAGS -fno-lto" CXXFLAGS="$CXXFLAGS -fno-lto" ./configure \ --disable-examples \ diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index 1e5aa170d..33ae63dc3 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -837,7 +837,7 @@ stage('libvpx', """ git clone https://github.com/webmproject/libvpx.git depends:patches/libvpx/*.patch cd libvpx - git checkout e1c124f89 + git checkout 51057f4ba8 win: for /r %%i in (..\\patches\\libvpx\\*) do git apply %%i diff --git a/Telegram/build/version b/Telegram/build/version index 29156e692..aa5d6c6c7 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 4010001 +AppVersion 4010002 AppVersionStrMajor 4.10 -AppVersionStrSmall 4.10.1 -AppVersionStr 4.10.1 +AppVersionStrSmall 4.10.2 +AppVersionStr 4.10.2 BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 4.10.1 +AppVersionOriginal 4.10.2 diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 3a5a539ba..8f0e06312 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -282,6 +282,8 @@ PRIVATE ui/effects/premium_stars.h ui/effects/premium_stars_colored.cpp ui/effects/premium_stars_colored.h + ui/effects/premium_top_bar.cpp + ui/effects/premium_top_bar.h ui/effects/round_checkbox.cpp ui/effects/round_checkbox.h ui/effects/scroll_content_shadow.cpp diff --git a/Telegram/lib_base b/Telegram/lib_base index d67a11776..cb8e07fa7 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit d67a11776ad720a718bae026b78ddd150f13fac5 +Subproject commit cb8e07fa7febcff1dbdbac7bdb0ac46e4eda5c8e diff --git a/changelog.txt b/changelog.txt index d94ce148b..821c95794 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +4.10.2 (28.09.23) + +- Bug fixes and other minor improvements. + 4.10.1 (23.09.23) - Rebuild macOS version with Xcode 14.0.1. diff --git a/cmake b/cmake index 218a34d9b..b1b0e95b0 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 218a34d9b5e3267b86a938b48bd74c045455bd3c +Subproject commit b1b0e95b091f298c87cb9ec4458f426574221ca4