From 55e284729e7cf7d10fe30be4b3e49088ca0cb7ac Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 3 Apr 2026 21:51:23 +0700 Subject: [PATCH 1/5] Initial implementation of gradual draft streaming. --- Telegram/SourceFiles/data/data_session.cpp | 18 ++ Telegram/SourceFiles/data/data_session.h | 8 + Telegram/SourceFiles/data/data_types.h | 2 + Telegram/SourceFiles/history/history.cpp | 32 +++ Telegram/SourceFiles/history/history.h | 2 + Telegram/SourceFiles/history/history_item.cpp | 14 +- Telegram/SourceFiles/history/history_item.h | 5 + .../history/history_streamed_drafts.cpp | 6 +- .../SourceFiles/history/history_widget.cpp | 9 + .../history/view/history_view_element.cpp | 6 + .../history/view/history_view_element.h | 1 + .../history/view/history_view_list_widget.cpp | 30 +++ .../history/view/history_view_list_widget.h | 1 + .../history/view/history_view_message.cpp | 214 +++++++++++++++++- .../history/view/history_view_message.h | 18 ++ .../history/view/history_view_object.h | 3 + 16 files changed, 364 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 565aea8497..6ce87a8dbc 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -2050,6 +2050,16 @@ rpl::producer> Session::viewResizeRequest() const { return _viewResizeRequest.events(); } +void Session::notifyViewHeightAdjusted( + not_null view, + int delta) { + _viewHeightAdjusted.fire({ view, delta }); +} + +rpl::producer Session::viewHeightAdjusted() const { + return _viewHeightAdjusted.events(); +} + void Session::requestItemShowHighlight(not_null item) { _itemShowHighlightRequest.fire_copy(item); } @@ -2092,6 +2102,14 @@ void Session::requestItemTextRefresh(not_null item) { } } +void Session::requestItemTextRefreshStreaming( + not_null item) { + enumerateItemViews(item, [&](not_null view) { + view->itemTextUpdatedStreaming(); + }); + requestItemResize(item); +} + void Session::registerRestricted( not_null item, const QString &reason) { diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 874d126ed0..1e54a52ab4 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -395,11 +395,18 @@ public: [[nodiscard]] rpl::producer> itemResizeRequest() const; void requestViewResize(not_null view); [[nodiscard]] rpl::producer> viewResizeRequest() const; + struct ViewHeightAdjusted { + not_null view; + int delta = 0; + }; + void notifyViewHeightAdjusted(not_null view, int delta); + [[nodiscard]] rpl::producer viewHeightAdjusted() const; void requestItemShowHighlight(not_null item); [[nodiscard]] rpl::producer> itemShowHighlightRequest() const; void requestItemViewRefresh(not_null item); [[nodiscard]] rpl::producer> itemViewRefreshRequest() const; void requestItemTextRefresh(not_null item); + void requestItemTextRefreshStreaming(not_null item); void requestUnreadReactionsAnimation(not_null item); void notifyHistoryUnloaded(not_null history); [[nodiscard]] rpl::producer> historyUnloaded() const; @@ -1137,6 +1144,7 @@ private: rpl::event_stream _viewRepaintRequest; rpl::event_stream> _itemResizeRequest; rpl::event_stream> _viewResizeRequest; + rpl::event_stream _viewHeightAdjusted; rpl::event_stream> _itemShowHighlightRequest; rpl::event_stream> _itemViewRefreshRequest; rpl::event_stream> _itemTextRefreshRequest; diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index 7017b37cd6..2a1dc3b616 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -364,6 +364,8 @@ enum class MessageFlag : uint64 { HasSummaryEntry = (1ULL << 57), CanBeSummarized = (1ULL << 58), HasUnreadPollVote = (1ULL << 59), + + TextAppearing = (1ULL << 60), }; inline constexpr bool is_flag_type(MessageFlag) { return true; } using MessageFlags = base::flags; diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 7d5e2500cb..497c1f91c4 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -1484,6 +1484,38 @@ void History::applyServiceChanges( }); } +void History::viewHeightAdjusted(not_null view, int delta) { + if (view->data()->mainView() == view) { + mainViewHeightAdjusted(view, delta); + } + owner().notifyViewHeightAdjusted(view, delta); +} + +void History::mainViewHeightAdjusted(not_null view, int delta) { + const auto viewInBlock = view->indexInBlock(); + if (viewInBlock < 0 || _width <= 0) { + return; + } + const auto block = view->block(); + for (auto i = viewInBlock + 1, count = int(block->messages.size()) + ; i != count + ; ++i) { + const auto view = block->messages[i].get(); + view->setY(view->y() + delta); + } + block->resizeGetHeight( + _width, + HistoryBlock::ResizeRequest::ResizePending); + const auto blockInHistory = block->indexInHistory(); + for (auto i = blockInHistory + 1, count = int(blocks.size()) + ; i != count + ; ++i) { + const auto block = blocks[i].get(); + block->setY(block->y() + delta); + } + _height += delta; +} + void History::mainViewRemoved( not_null block, not_null view) { diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index aec389e3f8..e8848ecae3 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -420,6 +420,7 @@ public: PeerId dataPeerId, const MTPmessages_Messages &data); + void viewHeightAdjusted(not_null view, int delta); void forgetScrollState() { scrollTopItem = nullptr; } @@ -558,6 +559,7 @@ private: void mainViewRemoved( not_null block, not_null view); + void mainViewHeightAdjusted(not_null view, int delta); TimeId adjustedChatListTimeId() const override; void changedChatListPinHook() override; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 5777ba7b68..4682c93d59 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -3937,7 +3937,8 @@ FullReplyTo HistoryItem::replyTo() const { return result; } -void HistoryItem::setText(const TextWithEntities &textWithEntities) { +void HistoryItem::detectTextLinks( + const TextWithEntities &textWithEntities) { for (const auto &entity : textWithEntities.entities) { auto type = entity.type(); if (type == EntityType::Url @@ -3949,6 +3950,10 @@ void HistoryItem::setText(const TextWithEntities &textWithEntities) { break; } } +} + +void HistoryItem::setText(const TextWithEntities &textWithEntities) { + detectTextLinks(textWithEntities); setTextValue((_media && _media->consumeMessageText(textWithEntities)) ? TextWithEntities() : std::move(textWithEntities)); @@ -3967,6 +3972,13 @@ void HistoryItem::setTextValue(TextWithEntities text, bool force) { } } +void HistoryItem::setTextStreaming(TextWithEntities text) { + detectTextLinks(text); + _text = std::move(text); + RemoveComponents(HistoryMessageTranslation::Bit()); + history()->owner().requestItemTextRefreshStreaming(this); +} + bool HistoryItem::inHighlightProcess() const { return _flags & MessageFlag::InHighlightProcess; } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 8be77c129c..da5f581b04 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -343,6 +343,9 @@ public: [[nodiscard]] bool canBeSummarized() const { return _flags & MessageFlag::CanBeSummarized; } + [[nodiscard]] bool isTextAppearing() const { + return _flags & MessageFlag::TextAppearing; + } [[nodiscard]] bool hasRealFromId() const; [[nodiscard]] bool isPostHidingAuthor() const; [[nodiscard]] bool isPostShowingAuthor() const; @@ -519,6 +522,7 @@ public: } [[nodiscard]] bool computeDropForwardedInfo() const; void setText(const TextWithEntities &textWithEntities); + void setTextStreaming(TextWithEntities text); [[nodiscard]] MsgId replyToId() const; [[nodiscard]] FullMsgId replyToFullId() const; @@ -628,6 +632,7 @@ private: [[nodiscard]] bool generateLocalEntitiesByReply() const; [[nodiscard]] TextWithEntities withLocalEntities( const TextWithEntities &textWithEntities) const; + void detectTextLinks(const TextWithEntities &textWithEntities); void setTextValue(TextWithEntities text, bool force = false); [[nodiscard]] bool isTooOldForEdit(TimeId now) const; [[nodiscard]] bool isLegacyMessage() const { diff --git a/Telegram/SourceFiles/history/history_streamed_drafts.cpp b/Telegram/SourceFiles/history/history_streamed_drafts.cpp index 7459825658..30abd5fe03 100644 --- a/Telegram/SourceFiles/history/history_streamed_drafts.cpp +++ b/Telegram/SourceFiles/history/history_streamed_drafts.cpp @@ -56,7 +56,9 @@ void HistoryStreamedDrafts::apply( _drafts.emplace(rootId, Draft{ .message = _history->addNewLocalMessage({ .id = _history->owner().nextLocalMessageId(), - .flags = MessageFlag::Local | MessageFlag::HasReplyInfo, + .flags = (MessageFlag::Local + | MessageFlag::HasReplyInfo + | MessageFlag::TextAppearing), .from = fromId, .replyTo = { .messageId = replyToId, @@ -80,7 +82,7 @@ bool HistoryStreamedDrafts::update( if (i == end(_drafts) || i->second.randomId != randomId) { return false; } - i->second.message->setText(text); + i->second.message->setTextStreaming(text); i->second.updated = crl::now(); return true; } diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index cd39b38f1c..15ccb49782 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -630,6 +630,15 @@ HistoryWidget::HistoryWidget( updateHistoryGeometry(); } }, lifetime()); + session().data().viewHeightAdjusted( + ) | rpl::on_next([=](Data::Session::ViewHeightAdjusted data) { + const auto item = data.view->data(); + const auto history = item->history(); + if (item->mainView() == data.view + && (history == _history || history == _migrated)) { + updateHistoryGeometry(); + } + }, lifetime()); session().data().itemShowHighlightRequest( ) | rpl::on_next([=](not_null item) { diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index e568895c74..6b884a5bd2 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -2539,6 +2539,12 @@ void Element::itemTextUpdated() { } } +void Element::itemTextUpdatedStreaming() { + clearSpecialOnlyEmoji(); + _text = Ui::Text::String(st::msgMinWidth); + invalidateTextSizeCache(); +} + void Element::blockquoteExpandChanged() { invalidateTextSizeCache(); history()->owner().requestViewResize(this); diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 08a1e3d55e..f790f1ace5 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -621,6 +621,7 @@ public: virtual void itemDataChanged(); void itemTextUpdated(); + void itemTextUpdatedStreaming(); void blockquoteExpandChanged(); virtual void unloadHeavyPart(); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 2055c8396d..30e029d41d 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -463,6 +463,12 @@ ListWidget::ListWidget( resizeItem(view); } }, lifetime()); + _session->data().viewHeightAdjusted( + ) | rpl::on_next([this](Data::Session::ViewHeightAdjusted data) { + if (data.view->delegate() == this) { + viewHeightAdjusted(data.view); + } + }, lifetime()); _session->data().itemViewRefreshRequest( ) | rpl::on_next([this](auto item) { if (const auto view = viewForItem(item)) { @@ -4352,6 +4358,30 @@ void ListWidget::refreshAttachmentsFromTill(int from, int till) { } } +void ListWidget::viewHeightAdjusted(not_null view) { + const auto i = ranges::find(_items, view); + if (i == end(_items)) { + return; + } + auto next = i + 1; + const auto was = (next != end(_items)) + ? (*next)->y() + : _itemsHeight; + const auto now = view->y() + view->height(); + const auto delta = now - was; + for (; next != end(_items); ++next) { + (*next)->setY((*next)->y() + delta); + } + _itemsHeight += delta; + _itemsTop = (_minHeight > _itemsHeight + st::historyPaddingBottom) + ? (_minHeight - _itemsHeight - st::historyPaddingBottom) + : 0; + resize(width(), _itemsTop + _itemsHeight + st::historyPaddingBottom); + restoreScrollPosition(); + updateVisibleTopItem(); + update(); +} + void ListWidget::refreshItem(not_null view) { const auto i = ranges::find(_items, view); const auto index = i - begin(_items); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index effc5f36c0..8162fda81e 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -596,6 +596,7 @@ private: void repaintItem(const Element *view, QRect rect); void resizeItem(not_null view); void refreshItem(not_null view); + void viewHeightAdjusted(not_null view); void showItemHighlight(not_null item); void itemRemoved(not_null item); QPoint mapPointToItem(QPoint point, const Element *view) const; diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 374622d77e..71750f23b3 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -67,6 +67,8 @@ constexpr auto kSummarizeThreshold = 512; constexpr auto kPlayStatusLimit = 2; constexpr auto kMaxNiceToReadLines = 6; const auto kPsaTooltipPrefix = "cloud_lng_tooltip_psa_"; +constexpr auto kFullLineAppearDuration = crl::time(300); +constexpr auto kLineHeightAppearDuration = crl::time(100); struct SecondRightAction { std::unique_ptr ripple; @@ -640,6 +642,11 @@ QSize Message::performCountOptimalSize() { } else { RemoveComponents(Factcheck::Bit()); } + if (item->isTextAppearing()) { + AddComponents(TextAppearing::Bit()); + } else { + RemoveComponents(TextAppearing::Bit()); + } refreshRightBadge(); const auto markup = item->inlineReplyMarkup(); @@ -873,6 +880,9 @@ QSize Message::performCountOptimalSize() { minHeight -= text().minHeight(); minHeight += textHeightFor(bubbleTextWidth(maxWidth)); } + if (const auto appearing = Get()) { + appearing->geometryValid = false; + } return QSize(maxWidth, minHeight); } @@ -2181,6 +2191,37 @@ void Message::paintText( .outPath = &ripplePath, }; + const auto appearing = Get(); + if (appearing + && (appearing->shownLines > 0) + && (appearing->shownLines <= int(appearing->lines.size()))) { + const auto lastLineIndex = appearing->shownLines - 1; + p.save(); + auto clipPath = QPainterPath(); + const auto prevBottom = (lastLineIndex > 0) + ? appearing->lines[lastLineIndex - 1].top + : 0; + if (prevBottom > 0) { + clipPath.addRect( + trect.x(), + trect.y(), + trect.width(), + prevBottom); + } + const auto lastLineHeight = appearing->lines[lastLineIndex].top + - prevBottom; + const auto &lastLine = appearing->lines[lastLineIndex]; + const auto clipLeft = lastLine.rtl + ? (trect.x() + trect.width() - appearing->revealedLineWidth) + : trect.x(); + clipPath.addRect( + clipLeft, + trect.y() + prevBottom, + appearing->revealedLineWidth, + lastLineHeight); + p.setClipPath(clipPath); + } + auto highlightRequest = context.computeHighlightCache(); text().draw(p, { .position = trect.topLeft(), @@ -2211,6 +2252,10 @@ void Message::paintText( } else if (needRippleMask) { _linkRipple = nullptr; } + if (appearing && appearing->shownLines > 0 + && appearing->shownLines <= int(appearing->lines.size())) { + p.restore(); + } } PointState Message::pointState(QPoint point) const { @@ -4872,6 +4917,19 @@ QRect Message::countGeometry() const { contentWidth = mediaWidth; } } + if (const auto appearing = Get()) { + if (appearing->shownWidth > 0) { + const auto animatedWidth = st::msgPadding.left() + + appearing->shownWidth + + st::msgPadding.right(); + const auto minWidth = _bottomInfo.optimalSize().width() + + st::msgPadding.left() + + st::msgPadding.right(); + accumulate_min(contentWidth, std::max( + animatedWidth, + minWidth)); + } + } if (contentWidth < availableWidth && delegate()->elementChatMode() != ElementChatMode::Wide) { if (outbg) { @@ -5031,7 +5089,8 @@ int Message::resizeContentGetHeight(int newWidth) { _reactions->resizeGetHeight(textWidth); } - if (contentWidth == maxWidth()) { + const auto appearing = Get(); + if (contentWidth == maxWidth() && !appearing) { if (mediaDisplayed) { if (check) { newHeight += check->resizeGetHeight(contentWidth) + st::mediaInBubbleSkip; @@ -5055,7 +5114,38 @@ int Message::resizeContentGetHeight(int newWidth) { if (botTop) { newHeight += botTop->height; } - newHeight += textHeightFor(textWidth); + const auto fullTextHeight = textHeightFor(textWidth); + if (appearing) { + if (!appearing->geometryValid + || appearing->textWidth != textWidth) { + appearing->textWidth = textWidth; + appearing->lines = text().countLinesGeometry( + textWidth); + appearing->geometryValid = true; + if (appearing->shownHeight <= 0) { + appearing->shownHeight = appearing->lines.empty() + ? text().lineHeight() + : appearing->lines.front().top; + appearing->shownLines = 1; + appearing->revealedLineWidth = 0; + appearing->shownWidth = 0; + startTextAppearingAnimation(); + } else if (!appearing->heightStarted + && !appearing->widthAnimation.animating() + && !appearing->heightAnimation.animating() + && (appearing->shownLines + < appearing->lines.size())) { + startTextAppearingAnimation(); + } + } + LOG(("appearing: %1 lines, shown %2, height %3") + .arg(appearing->lines.size()) + .arg(appearing->shownLines) + .arg(appearing->shownHeight)); + newHeight += appearing->shownHeight; + } else { + newHeight += fullTextHeight; + } } if (!mediaOnBottom && (!_viewButton || !reactionsInBubble)) { newHeight += st::msgPadding.bottom(); @@ -5159,6 +5249,9 @@ int Message::resizeContentGetHeight(int newWidth) { } newHeight += marginTop() + marginBottom(); + if (const auto appearing = Get()) { + LOG(("appearing result: %1").arg(newHeight)); + } return newHeight; } @@ -5167,6 +5260,123 @@ void Message::invalidateTextDependentCache() { _bubbleTextualWidthCache = 0; } +void Message::startTextAppearingAnimation() { + const auto appearing = Get(); + if (!appearing || appearing->lines.empty()) { + return; + } + const auto lineIndex = appearing->shownLines - 1; + if (lineIndex < 0 + || lineIndex >= int(appearing->lines.size())) { + return; + } + appearing->heightStarted = false; + appearing->revealedLineWidth = 0; + const auto lineWidth = appearing->lines[lineIndex].width; + const auto duration = std::max( + kFullLineAppearDuration * lineWidth / st::msgMaxWidth, + crl::time(10)); + appearing->widthDuration = duration; + LOG(("appearance - starting width: %1, duration: %2") + .arg(lineWidth) + .arg(duration)); + appearing->widthAnimation.start( + [=] { textAppearingTick(); }, + 0., + 1., + duration, + anim::linear); +} + +void Message::textAppearingTick() { + auto appearing = Get(); + if (!appearing) { + return; + } + const auto lineIndex = appearing->shownLines - 1; + if (lineIndex < 0 + || lineIndex >= int(appearing->lines.size())) { + return; + } + const auto progress = appearing->widthAnimation.value(1.); + appearing->revealedLineWidth = int( + appearing->lines[lineIndex].width * progress); + appearing->shownWidth = std::max( + appearing->shownWidth, + appearing->revealedLineWidth); + + LOG(("appearance width progress - line %1, progress: %2, revealed width: %3") + .arg(lineIndex) + .arg(progress) + .arg(appearing->revealedLineWidth)); + + const auto hasMoreLines = + (appearing->shownLines < int(appearing->lines.size())); + const auto remaining = crl::time( + appearing->widthDuration * (1. - progress)); + if (hasMoreLines + && (remaining <= kLineHeightAppearDuration) + && !appearing->heightStarted) { + appearing->heightStarted = true; + const auto oldHeight = appearing->shownHeight; + const auto newTargetHeight + = appearing->lines[appearing->shownLines].top; + LOG(("appearance - starting height: %1, target: %2, duration: %3") + .arg(oldHeight) + .arg(newTargetHeight) + .arg(kLineHeightAppearDuration)); + appearing->heightAnimation.start( + [=] { textAppearingHeightTick(); }, + double(oldHeight), + double(newTargetHeight), + kLineHeightAppearDuration, + anim::easeOutCubic); + } + + if (!appearing->widthAnimation.animating()) { + tryAdvanceTextAppearing(); + } + + repaint(); +} + +void Message::textAppearingHeightTick() { + auto appearing = Get(); + if (!appearing) { + return; + } + + const auto targetHeight = appearing->lines[appearing->shownLines].top; + const auto oldShownHeight = appearing->shownHeight; + appearing->shownHeight = int(base::SafeRound( + appearing->heightAnimation.value(targetHeight))); + const auto delta = appearing->shownHeight - oldShownHeight; + if (delta) { + adjustHeight(delta); + history()->viewHeightAdjusted(this, delta); + } + + if (!appearing->heightAnimation.animating()) { + tryAdvanceTextAppearing(); + } +} + +void Message::tryAdvanceTextAppearing() { + auto appearing = Get(); + if (!appearing) { + return; + } + if (appearing->widthAnimation.animating() + || appearing->heightAnimation.animating()) { + return; + } + LOG(("appearance - line %1 fully appeared").arg(appearing->shownLines - 1)); + if (appearing->shownLines < int(appearing->lines.size())) { + appearing->shownLines++; + startTextAppearingAnimation(); + } +} + bool Message::needInfoDisplay() const { const auto media = this->media(); const auto mediaDisplayed = media ? media->isDisplayed() : false; diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index 09c8c3ad52..3eba3d531b 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -82,6 +82,20 @@ struct RightBadge : RuntimeComponent { mutable QPoint lastPoint; }; +struct TextAppearing : RuntimeComponent { + std::vector lines; + int textWidth = 0; + int shownLines = 0; + int revealedLineWidth = 0; + int shownWidth = 0; + int shownHeight = 0; + crl::time widthDuration = 0; + Ui::Animations::Simple widthAnimation; + Ui::Animations::Simple heightAnimation; + bool geometryValid = false; + bool heightStarted = false; +}; + struct BottomRippleMask { QImage image; int shift = 0; @@ -372,6 +386,10 @@ private: [[nodiscard]] ClickHandlerPtr psaTooltipLink() const; void psaTooltipToggled(bool shown) const; void invalidateTextDependentCache() override; + void startTextAppearingAnimation(); + void textAppearingTick(); + void textAppearingHeightTick(); + void tryAdvanceTextAppearing(); void refreshRightBadge(); [[nodiscard]] int rightBadgeWidth() const; diff --git a/Telegram/SourceFiles/history/view/history_view_object.h b/Telegram/SourceFiles/history/view/history_view_object.h index 9072b90bed..c61847c1bf 100644 --- a/Telegram/SourceFiles/history/view/history_view_object.h +++ b/Telegram/SourceFiles/history/view/history_view_object.h @@ -54,6 +54,9 @@ protected: _width = size.width(); _height = size.height(); } + void adjustHeight(int delta) { + _height += delta; + } private: virtual QSize countOptimalSize() = 0; From 98f342227f5957991b3d687d0b271575f504a117 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Apr 2026 21:17:43 +0700 Subject: [PATCH 2/5] Remove reveal animation on draft replacement. --- .../history/history_streamed_drafts.cpp | 7 ++++ .../history/history_streamed_drafts.h | 1 + .../SourceFiles/history/history_widget.cpp | 5 ++- .../view/history_view_chat_preview.cpp | 6 ++- .../view/history_view_chat_section.cpp | 13 +++++- .../history/view/history_view_chat_section.h | 3 +- .../history/view/history_view_list_widget.cpp | 40 ++++++++++++------- .../history/view/history_view_list_widget.h | 5 ++- .../view/history_view_pinned_section.cpp | 3 +- .../view/history_view_pinned_section.h | 3 +- .../view/history_view_scheduled_section.cpp | 3 +- .../view/history_view_scheduled_section.h | 3 +- .../info/polls/info_polls_list_widget.cpp | 7 ++-- .../business/settings_shortcut_messages.cpp | 6 ++- 14 files changed, 74 insertions(+), 31 deletions(-) diff --git a/Telegram/SourceFiles/history/history_streamed_drafts.cpp b/Telegram/SourceFiles/history/history_streamed_drafts.cpp index 30abd5fe03..5b84c4e516 100644 --- a/Telegram/SourceFiles/history/history_streamed_drafts.cpp +++ b/Telegram/SourceFiles/history/history_streamed_drafts.cpp @@ -96,6 +96,13 @@ void HistoryStreamedDrafts::clear(MsgId rootId) { } } +bool HistoryStreamedDrafts::hasFor(not_null item) const { + const auto rootId = item->topicRootId(); + const auto i = _drafts.find(rootId); + return (i != end(_drafts)) + && (i->second.message->from() == item->from()); +} + void HistoryStreamedDrafts::applyItemAdded(not_null item) { const auto rootId = item->topicRootId(); const auto i = _drafts.find(rootId); diff --git a/Telegram/SourceFiles/history/history_streamed_drafts.h b/Telegram/SourceFiles/history/history_streamed_drafts.h index 657a0177ed..d521ccc80c 100644 --- a/Telegram/SourceFiles/history/history_streamed_drafts.h +++ b/Telegram/SourceFiles/history/history_streamed_drafts.h @@ -25,6 +25,7 @@ public: TimeId when, const MTPDsendMessageTextDraftAction &data); + [[nodiscard]] bool hasFor(not_null item) const; void applyItemAdded(not_null item); void applyItemRemoved(not_null item); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 15ccb49782..4d15429833 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -101,6 +101,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_drag_area.h" #include "history/history_inner_widget.h" #include "history/history_item_components.h" +#include "history/history_streamed_drafts.h" #include "history/history_unread_things.h" #include "history/admin_log/history_admin_log_section.h" #include "history/view/controls/history_view_characters_limit.h" @@ -3862,7 +3863,9 @@ void HistoryWidget::newItemAdded(not_null item) { } return; } - _itemRevealPending.emplace(item); + if (!item->history()->streamedDrafts().hasFor(item)) { + _itemRevealPending.emplace(item); + } } void HistoryWidget::maybeMarkReactionsRead(not_null item) { diff --git a/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp b/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp index fe2e362e63..64314bb749 100644 --- a/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp +++ b/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp @@ -99,7 +99,8 @@ private: void listMarkContentsRead( const base::flat_set> &items) override; MessagesBarData listMessagesBar( - const std::vector> &elements) override; + const std::vector> &elements, + bool markLastAsRead) override; void listContentRefreshed() override; void listUpdateDateLink( ClickHandlerPtr &link, @@ -648,7 +649,8 @@ void Item::listMarkContentsRead( } MessagesBarData Item::listMessagesBar( - const std::vector> &elements) { + const std::vector> &elements, + bool markLastAsRead) { if (elements.empty()) { return {}; } else if (!_replies && !_sublist && !_history->unreadCount()) { diff --git a/Telegram/SourceFiles/history/view/history_view_chat_section.cpp b/Telegram/SourceFiles/history/view/history_view_chat_section.cpp index d36b963456..38d40b83b0 100644 --- a/Telegram/SourceFiles/history/view/history_view_chat_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_chat_section.cpp @@ -3113,7 +3113,8 @@ void ChatWidget::listMarkContentsRead( } MessagesBarData ChatWidget::listMessagesBar( - const std::vector> &elements) { + const std::vector> &elements, + bool markLastAsRead) { if ((!_sublist && !_replies) || elements.empty()) { return {}; } @@ -3124,7 +3125,15 @@ MessagesBarData ChatWidget::listMessagesBar( for (auto i = 0, count = int(elements.size()); i != count; ++i) { const auto item = elements[i]->data(); if (item->isRegular() && item->id > till) { - if (item->out() || (_replies && !item->replyToId())) { + if (markLastAsRead + || item->out() + || (_replies && !item->replyToId())) { + if (markLastAsRead) { + if (item->isUnreadMention() && !item->isUnreadMedia()) { + session().api().markContentsRead(item); + } + item->markClientSideAsRead(); + } if (_replies) { _replies->readTill(item); } else { diff --git a/Telegram/SourceFiles/history/view/history_view_chat_section.h b/Telegram/SourceFiles/history/view/history_view_chat_section.h index 7b47e792b1..1abbd70664 100644 --- a/Telegram/SourceFiles/history/view/history_view_chat_section.h +++ b/Telegram/SourceFiles/history/view/history_view_chat_section.h @@ -163,7 +163,8 @@ public: void listMarkContentsRead( const base::flat_set> &items) override; MessagesBarData listMessagesBar( - const std::vector> &elements) override; + const std::vector> &elements, + bool markLastAsRead) override; void listContentRefreshed() override; void listUpdateDateLink( ClickHandlerPtr &link, diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 30e029d41d..fd89ecd1c7 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_components.h" #include "history/history_item_helpers.h" #include "history/history_item_text.h" +#include "history/history_streamed_drafts.h" #include "history/view/media/history_view_media.h" #include "history/view/media/history_view_sticker.h" #include "history/view/reactions/history_view_reactions.h" @@ -647,6 +648,10 @@ void ListWidget::refreshRows(const Data::MessagesSlice &old) { saveScrollState(); + const auto scrolledTillEnd = _itemsKnownTillEnd + && (_visibleBottom == height()) + && (_visibleBottom > _visibleTop); + const auto addedToEndFrom = (old.skippedAfter == 0 && (_slice.skippedAfter == 0) && !old.ids.empty()) @@ -684,7 +689,10 @@ void ListWidget::refreshRows(const Data::MessagesSlice &old) { _translateTracker->addBunchFrom(_items); } for (auto e = end(_items), i = e - addedToEndCount; i != e; ++i) { - _itemRevealPending.emplace(*i); + const auto item = (*i)->data(); + if (!item->history()->streamedDrafts().hasFor(item)) { + _itemRevealPending.emplace(*i); + } } updateAroundPositionFromNearest(nearestIndex); @@ -705,7 +713,8 @@ void ListWidget::refreshRows(const Data::MessagesSlice &old) { } _viewsCapacity.clear(); - checkUnreadBarCreation(); + const auto markLastAsRead = (scrolledTillEnd && markingMessagesRead()); + checkUnreadBarCreation(markLastAsRead); restoreScrollState(); if (!_itemsRevealHeight) { mouseActionUpdate(QCursor::pos()); @@ -986,18 +995,21 @@ void ListWidget::computeScrollTo( scrollTo(wanted, position, scrollDelta, type); } -void ListWidget::checkUnreadBarCreation() { - if (!_bar.element) { - if (auto data = _delegate->listMessagesBar(_items); data.bar.element) { - _bar = std::move(data.bar); - _barText = std::move(data.text); - if (!_bar.hidden) { - _bar.element->createUnreadBar(_barText.value()); - const auto i = ranges::find(_items, not_null{ _bar.element }); - Assert(i != end(_items)); - refreshAttachmentsAtIndex(i - begin(_items)); - } - } +void ListWidget::checkUnreadBarCreation(bool markLastAsRead) { + if (_bar.element) { + return; + } + auto data = _delegate->listMessagesBar(_items, markLastAsRead); + if (!data.bar.element) { + return; + } + _bar = std::move(data.bar); + _barText = std::move(data.text); + if (!_bar.hidden) { + _bar.element->createUnreadBar(_barText.value()); + const auto i = ranges::find(_items, not_null{ _bar.element }); + Assert(i != end(_items)); + refreshAttachmentsAtIndex(i - begin(_items)); } } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 8162fda81e..a3dcdb83c3 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -126,7 +126,8 @@ public: virtual void listMarkContentsRead( const base::flat_set> &items) = 0; virtual MessagesBarData listMessagesBar( - const std::vector> &elements) = 0; + const std::vector> &elements, + bool markLastAsRead) = 0; virtual void listContentRefreshed() = 0; virtual void listUpdateDateLink( ClickHandlerPtr &link, @@ -717,7 +718,7 @@ private: TextSelection computeRenderSelection( not_null selected, not_null view) const; - void checkUnreadBarCreation(); + void checkUnreadBarCreation(bool markLastAsRead = false); void applyUpdatedScrollState(); void scrollToAnimationCallback(FullMsgId attachToId, int relativeTo); void startItemRevealAnimations(); diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp index 29262304b1..d9f9196f49 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp @@ -600,7 +600,8 @@ void PinnedWidget::listMarkContentsRead( } MessagesBarData PinnedWidget::listMessagesBar( - const std::vector> &elements) { + const std::vector> &elements, + bool markLastAsRead) { return {}; } diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.h b/Telegram/SourceFiles/history/view/history_view_pinned_section.h index be7a515d72..23b7f0a137 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.h +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.h @@ -97,7 +97,8 @@ public: void listMarkContentsRead( const base::flat_set> &items) override; MessagesBarData listMessagesBar( - const std::vector> &elements) override; + const std::vector> &elements, + bool markLastAsRead) override; void listContentRefreshed() override; void listUpdateDateLink( ClickHandlerPtr &link, diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index bf1d2721e8..ab9ed36b9d 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -1463,7 +1463,8 @@ void ScheduledWidget::listMarkContentsRead( } MessagesBarData ScheduledWidget::listMessagesBar( - const std::vector> &elements) { + const std::vector> &elements, + bool markLastAsRead) { return {}; } diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h index d5239a2e86..3d03c52a37 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -139,7 +139,8 @@ public: void listMarkContentsRead( const base::flat_set> &items) override; MessagesBarData listMessagesBar( - const std::vector> &elements) override; + const std::vector> &elements, + bool markLastAsRead) override; void listContentRefreshed() override; void listUpdateDateLink( ClickHandlerPtr &link, diff --git a/Telegram/SourceFiles/info/polls/info_polls_list_widget.cpp b/Telegram/SourceFiles/info/polls/info_polls_list_widget.cpp index 0c848e506f..0ccfc8b4a2 100644 --- a/Telegram/SourceFiles/info/polls/info_polls_list_widget.cpp +++ b/Telegram/SourceFiles/info/polls/info_polls_list_widget.cpp @@ -106,8 +106,8 @@ private: void listMarkContentsRead( const base::flat_set> &items) override; HistoryView::MessagesBarData listMessagesBar( - const std::vector> &elements) - override; + const std::vector> &elements, + bool markLastAsRead) override; void listContentRefreshed() override; void listUpdateDateLink( ClickHandlerPtr &link, @@ -522,7 +522,8 @@ void ListWidget::Inner::listMarkContentsRead( } HistoryView::MessagesBarData ListWidget::Inner::listMessagesBar( - const std::vector> &elements) { + const std::vector> &elements, + bool markLastAsRead) { return {}; } diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp index b85873fd6b..5663edbb61 100644 --- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp +++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp @@ -128,7 +128,8 @@ private: void listMarkContentsRead( const base::flat_set> &items) override; MessagesBarData listMessagesBar( - const std::vector> &elements) override; + const std::vector> &elements, + bool markLastAsRead) override; void listContentRefreshed() override; void listUpdateDateLink( ClickHandlerPtr &link, @@ -911,7 +912,8 @@ void ShortcutMessages::listMarkContentsRead( } MessagesBarData ShortcutMessages::listMessagesBar( - const std::vector> &elements) { + const std::vector> &elements, + bool markLastAsRead) { return {}; } From a8f9cf430d6267b73e23a86286a24ecabd69e271 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Apr 2026 22:10:13 +0700 Subject: [PATCH 3/5] Fix draft streaming continuation. --- .../history/view/history_view_message.cpp | 52 +++++++++++-------- .../history/view/history_view_message.h | 3 +- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 71750f23b3..edef85319a 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -5129,13 +5129,12 @@ int Message::resizeContentGetHeight(int newWidth) { appearing->shownLines = 1; appearing->revealedLineWidth = 0; appearing->shownWidth = 0; - startTextAppearingAnimation(); + LOG(("appearing start")); + startTextAppearingWidthAnimation(); } else if (!appearing->heightStarted - && !appearing->widthAnimation.animating() - && !appearing->heightAnimation.animating() - && (appearing->shownLines - < appearing->lines.size())) { - startTextAppearingAnimation(); + && !appearing->widthAnimation.animating()) { + LOG(("appearing continue")); + startTextAppearingHeightAnimation(); } } LOG(("appearing: %1 lines, shown %2, height %3") @@ -5260,7 +5259,7 @@ void Message::invalidateTextDependentCache() { _bubbleTextualWidthCache = 0; } -void Message::startTextAppearingAnimation() { +void Message::startTextAppearingWidthAnimation() { const auto appearing = Get(); if (!appearing || appearing->lines.empty()) { return; @@ -5288,6 +5287,28 @@ void Message::startTextAppearingAnimation() { anim::linear); } +void Message::startTextAppearingHeightAnimation() { + const auto appearing = Get(); + if (!appearing + || appearing->heightStarted + || (appearing->shownLines >= int(appearing->lines.size()))) { + return; + } + appearing->heightStarted = true; + const auto oldHeight = appearing->shownHeight; + const auto newTargetHeight = appearing->lines[appearing->shownLines].top; + LOG(("appearance - starting height: %1, target: %2, duration: %3") + .arg(oldHeight) + .arg(newTargetHeight) + .arg(kLineHeightAppearDuration)); + appearing->heightAnimation.start( + [=] { textAppearingHeightTick(); }, + double(oldHeight), + double(newTargetHeight), + kLineHeightAppearDuration, + anim::easeOutCubic); +} + void Message::textAppearingTick() { auto appearing = Get(); if (!appearing) { @@ -5317,20 +5338,7 @@ void Message::textAppearingTick() { if (hasMoreLines && (remaining <= kLineHeightAppearDuration) && !appearing->heightStarted) { - appearing->heightStarted = true; - const auto oldHeight = appearing->shownHeight; - const auto newTargetHeight - = appearing->lines[appearing->shownLines].top; - LOG(("appearance - starting height: %1, target: %2, duration: %3") - .arg(oldHeight) - .arg(newTargetHeight) - .arg(kLineHeightAppearDuration)); - appearing->heightAnimation.start( - [=] { textAppearingHeightTick(); }, - double(oldHeight), - double(newTargetHeight), - kLineHeightAppearDuration, - anim::easeOutCubic); + startTextAppearingHeightAnimation(); } if (!appearing->widthAnimation.animating()) { @@ -5373,7 +5381,7 @@ void Message::tryAdvanceTextAppearing() { LOG(("appearance - line %1 fully appeared").arg(appearing->shownLines - 1)); if (appearing->shownLines < int(appearing->lines.size())) { appearing->shownLines++; - startTextAppearingAnimation(); + startTextAppearingWidthAnimation(); } } diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index 3eba3d531b..24a89991a7 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -386,7 +386,8 @@ private: [[nodiscard]] ClickHandlerPtr psaTooltipLink() const; void psaTooltipToggled(bool shown) const; void invalidateTextDependentCache() override; - void startTextAppearingAnimation(); + void startTextAppearingWidthAnimation(); + void startTextAppearingHeightAnimation(); void textAppearingTick(); void textAppearingHeightTick(); void tryAdvanceTextAppearing(); From f6abf7a2d2f0b784bc6086c4ab26e8eee7435dac Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Apr 2026 22:51:01 +0700 Subject: [PATCH 4/5] Remove some debug logs. --- .../history/view/history_view_message.cpp | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index edef85319a..cbf4f8972d 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -5129,18 +5129,12 @@ int Message::resizeContentGetHeight(int newWidth) { appearing->shownLines = 1; appearing->revealedLineWidth = 0; appearing->shownWidth = 0; - LOG(("appearing start")); startTextAppearingWidthAnimation(); } else if (!appearing->heightStarted && !appearing->widthAnimation.animating()) { - LOG(("appearing continue")); startTextAppearingHeightAnimation(); } } - LOG(("appearing: %1 lines, shown %2, height %3") - .arg(appearing->lines.size()) - .arg(appearing->shownLines) - .arg(appearing->shownHeight)); newHeight += appearing->shownHeight; } else { newHeight += fullTextHeight; @@ -5248,9 +5242,6 @@ int Message::resizeContentGetHeight(int newWidth) { } newHeight += marginTop() + marginBottom(); - if (const auto appearing = Get()) { - LOG(("appearing result: %1").arg(newHeight)); - } return newHeight; } @@ -5276,9 +5267,6 @@ void Message::startTextAppearingWidthAnimation() { kFullLineAppearDuration * lineWidth / st::msgMaxWidth, crl::time(10)); appearing->widthDuration = duration; - LOG(("appearance - starting width: %1, duration: %2") - .arg(lineWidth) - .arg(duration)); appearing->widthAnimation.start( [=] { textAppearingTick(); }, 0., @@ -5297,10 +5285,6 @@ void Message::startTextAppearingHeightAnimation() { appearing->heightStarted = true; const auto oldHeight = appearing->shownHeight; const auto newTargetHeight = appearing->lines[appearing->shownLines].top; - LOG(("appearance - starting height: %1, target: %2, duration: %3") - .arg(oldHeight) - .arg(newTargetHeight) - .arg(kLineHeightAppearDuration)); appearing->heightAnimation.start( [=] { textAppearingHeightTick(); }, double(oldHeight), @@ -5326,11 +5310,6 @@ void Message::textAppearingTick() { appearing->shownWidth, appearing->revealedLineWidth); - LOG(("appearance width progress - line %1, progress: %2, revealed width: %3") - .arg(lineIndex) - .arg(progress) - .arg(appearing->revealedLineWidth)); - const auto hasMoreLines = (appearing->shownLines < int(appearing->lines.size())); const auto remaining = crl::time( @@ -5378,7 +5357,6 @@ void Message::tryAdvanceTextAppearing() { || appearing->heightAnimation.animating()) { return; } - LOG(("appearance - line %1 fully appeared").arg(appearing->shownLines - 1)); if (appearing->shownLines < int(appearing->lines.size())) { appearing->shownLines++; startTextAppearingWidthAnimation(); From ca3eb7dca4fbbe0ec12df8145c13157f245535e1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Apr 2026 22:41:42 +0700 Subject: [PATCH 5/5] Version 6.7.5. - Support for gradual appearing of bot reply messages. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- changelog.txt | 4 ++++ 6 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index b39491fa45..f602bb2280 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="6.7.5.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 62e9a6b36d..2e03739eef 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 6,7,4,0 - PRODUCTVERSION 6,7,4,0 + FILEVERSION 6,7,5,0 + PRODUCTVERSION 6,7,5,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "6.7.4.0" + VALUE "FileVersion", "6.7.5.0" VALUE "LegalCopyright", "Copyright (C) 2014-2026" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "6.7.4.0" + VALUE "ProductVersion", "6.7.5.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 56a173fda4..eccc2e95f8 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 6,7,4,0 - PRODUCTVERSION 6,7,4,0 + FILEVERSION 6,7,5,0 + PRODUCTVERSION 6,7,5,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "6.7.4.0" + VALUE "FileVersion", "6.7.5.0" VALUE "LegalCopyright", "Copyright (C) 2014-2026" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "6.7.4.0" + VALUE "ProductVersion", "6.7.5.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index c5a0f9e772..944baf38af 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 6007004; -constexpr auto AppVersionStr = "6.7.4"; +constexpr auto AppVersion = 6007005; +constexpr auto AppVersionStr = "6.7.5"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index ad6d505e3f..3dcc371bbf 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 6007004 +AppVersion 6007005 AppVersionStrMajor 6.7 -AppVersionStrSmall 6.7.4 -AppVersionStr 6.7.4 +AppVersionStrSmall 6.7.5 +AppVersionStr 6.7.5 BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 6.7.4 +AppVersionOriginal 6.7.5 diff --git a/changelog.txt b/changelog.txt index 94170bcb3e..7c2d45400b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +6.7.5 (07.04.26) + +- Support for gradual appearing of bot reply messages. + 6.7.4 (07.04.26) - Add experimental option for "AI Tools" button.