Show toast/tooltip info on video processing.

This commit is contained in:
John Preston 2024-10-29 22:06:24 +04:00
parent 3137c9f3f7
commit 66be2ac6ca
17 changed files with 480 additions and 65 deletions

View file

@ -3304,6 +3304,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_scheduled_send_now" = "Send message now?";
"lng_scheduled_send_now_many#one" = "Send {count} message now?";
"lng_scheduled_send_now_many#other" = "Send {count} messages now?";
"lng_scheduled_video_tip_title" = "Improving video...";
"lng_scheduled_video_tip_text" = "The video will be published after it's optimized for the bast viewing experience.";
"lng_scheduled_video_tip" = "Processing video may take a few minutes.";
"lng_scheduled_video_published" = "Video Published.";
"lng_scheduled_video_view" = "View";
"lng_replies_view#one" = "View {count} Reply";
"lng_replies_view#other" = "View {count} Replies";

View file

@ -332,6 +332,15 @@ void Updates::feedUpdateVector(
session().data().sendHistoryChangeNotifications();
}
void Updates::checkForSentToScheduled(const MTPUpdates &updates) {
updates.match([&](const MTPDupdates &data) {
applyConvertToScheduledOnSend(data.vupdates(), true);
}, [&](const MTPDupdatesCombined &data) {
applyConvertToScheduledOnSend(data.vupdates(), true);
}, [](const auto &) {
});
}
void Updates::feedMessageIds(const MTPVector<MTPUpdate> &updates) {
for (const auto &update : updates.v) {
if (update.type() == mtpc_updateMessageID) {
@ -887,23 +896,35 @@ void Updates::mtpUpdateReceived(const MTPUpdates &updates) {
}
void Updates::applyConvertToScheduledOnSend(
const MTPVector<MTPUpdate> &other) {
const MTPVector<MTPUpdate> &other,
bool skipScheduledCheck) {
for (const auto &update : other.v) {
update.match([&](const MTPDupdateNewScheduledMessage &data) {
const auto id = IdFromMessage(data.vmessage());
const auto &message = data.vmessage();
const auto id = IdFromMessage(message);
const auto scheduledMessages = &_session->scheduledMessages();
const auto scheduledId = scheduledMessages->localMessageId(id);
for (const auto &updateId : other.v) {
updateId.match([&](const MTPDupdateMessageID &dataId) {
if (dataId.vid().v == id) {
const auto rand = dataId.vrandom_id().v;
auto &owner = session().data();
if (skipScheduledCheck) {
const auto peerId = PeerFromMessage(message);
const auto history = owner.historyLoaded(peerId);
if (history) {
_session->data().sentToScheduled({
.history = history,
.scheduledId = scheduledId,
});
}
return;
}
const auto rand = dataId.vrandom_id().v;
const auto localId = owner.messageIdByRandomId(rand);
if (const auto local = owner.message(localId)) {
if (!local->isScheduled()) {
using Flag = Data::MessageUpdate::Flag;
_session->data().sentToScheduled({
.item = local,
.history = local->history(),
.scheduledId = scheduledId,
});

View file

@ -40,6 +40,8 @@ public:
void applyUpdatesNoPtsCheck(const MTPUpdates &updates);
void applyUpdateNoPtsCheck(const MTPUpdate &update);
void checkForSentToScheduled(const MTPUpdates &updates);
[[nodiscard]] int32 pts() const;
void updateOnline(crl::time lastNonIdleTime = 0);
@ -131,7 +133,9 @@ private:
// Doesn't call sendHistoryChangeNotifications itself.
void feedUpdate(const MTPUpdate &update);
void applyConvertToScheduledOnSend(const MTPVector<MTPUpdate> &other);
void applyConvertToScheduledOnSend(
const MTPVector<MTPUpdate> &other,
bool skipScheduledCheck = false);
void applyGroupCallParticipantUpdates(const MTPUpdates &updates);
bool whenGetDiffChanged(

View file

@ -3329,6 +3329,7 @@ void ApiWrap::forwardMessages(
}
const auto requestType = Data::Histories::RequestType::Send;
const auto idsCopy = localIds;
const auto scheduled = action.options.scheduled;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
history->sendRequestId = request(MTPmessages_ForwardMessages(
MTP_flags(sendFlags),
@ -3341,6 +3342,9 @@ void ApiWrap::forwardMessages(
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, action.options.shortcutId)
)).done([=](const MTPUpdates &result) {
if (!scheduled) {
this->updates().checkForSentToScheduled(result);
}
applyUpdates(result);
if (shared && !--shared->requestsLeft) {
shared->callback();

View file

@ -1519,3 +1519,22 @@ pickLocationChooseOnMap: RoundButton(defaultActiveButton) {
sendGifBox: Box(defaultBox) {
shadowIgnoreBottomSkip: true;
}
processingVideoTipMaxWidth: 364px;
processingVideoTipShift: 10px;
processingVideoToast: Toast(defaultToast) {
minWidth: 32px;
maxWidth: 380px;
padding: margins(19px, 17px, 19px, 16px);
}
processingVideoPreviewSkip: 0px;
processingVideoView: RoundButton(defaultActiveButton) {
width: -24px;
height: 68px;
textTop: 26px;
textFg: mediaviewTextLinkFg;
textFgOver: mediaviewTextLinkFg;
textBg: transparent;
textBgOver: transparent;
ripple: emptyRippleAnimation;
}

View file

@ -343,10 +343,20 @@ void ScheduledMessages::apply(
if (i == end(_data)) {
return;
}
for (const auto &id : update.vmessages().v) {
const auto sent = update.vsent_messages();
const auto &ids = update.vmessages().v;
for (auto k = 0, count = int(ids.size()); k != count; ++k) {
const auto id = ids[k].v;
const auto &list = i->second;
const auto j = list.itemById.find(id.v);
const auto j = list.itemById.find(id);
if (j != end(list.itemById)) {
if (sent && k < sent->v.size()) {
const auto &sentId = sent->v[k];
_session->data().sentFromScheduled({
.item = j->second,
.sentId = sentId.v,
});
}
j->second->destroy();
i = _data.find(history);
if (i == end(_data)) {

View file

@ -4821,6 +4821,14 @@ rpl::producer<SentToScheduled> Session::sentToScheduled() const {
return _sentToScheduled.events();
}
void Session::sentFromScheduled(SentFromScheduled value) {
_sentFromScheduled.fire(std::move(value));
}
rpl::producer<SentFromScheduled> Session::sentFromScheduled() const {
return _sentFromScheduled.events();
}
void Session::clearLocalStorage() {
_cache->close();
_cache->clear();

View file

@ -90,9 +90,13 @@ struct GiftUpdate {
};
struct SentToScheduled {
not_null<HistoryItem*> item;
not_null<History*> history;
MsgId scheduledId = 0;
};
struct SentFromScheduled {
not_null<HistoryItem*> item;
MsgId sentId = 0;
};
class Session final {
public:
@ -798,6 +802,8 @@ public:
void sentToScheduled(SentToScheduled value);
[[nodiscard]] rpl::producer<SentToScheduled> sentToScheduled() const;
void sentFromScheduled(SentFromScheduled value);
[[nodiscard]] rpl::producer<SentFromScheduled> sentFromScheduled() const;
void clearLocalStorage();
@ -972,6 +978,7 @@ private:
rpl::event_stream<> _unreadBadgeChanges;
rpl::event_stream<RepliesReadTillUpdate> _repliesReadTillUpdates;
rpl::event_stream<SentToScheduled> _sentToScheduled;
rpl::event_stream<SentFromScheduled> _sentFromScheduled;
Dialogs::MainList _chatsList;
Dialogs::IndexedList _contactsList;

View file

@ -725,19 +725,33 @@ HistoryWidget::HistoryWidget(
session().data().sentToScheduled(
) | rpl::start_with_next([=](const Data::SentToScheduled &value) {
if (value.item->history() == _history) {
const auto history = value.item->history();
const auto history = value.history;
if (history == _history) {
const auto id = value.scheduledId;
crl::on_main(this, [=] {
controller->showSection(
std::make_shared<HistoryView::ScheduledMemento>(
history,
id));
if (history == _history) {
controller->showSection(
std::make_shared<HistoryView::ScheduledMemento>(
history,
id));
}
});
return;
}
}, lifetime());
session().data().sentFromScheduled(
) | rpl::start_with_next([=](const Data::SentFromScheduled &value) {
if (value.item->awaitingVideoProcessing()
&& !_sentFromScheduledTip
&& HistoryView::ShowScheduledVideoPublished(
controller,
value,
crl::guard(this, [=] { _sentFromScheduledTip = false; }))) {
_sentFromScheduledTip = true;
}
}, lifetime());
using MediaSwitch = Media::Player::Instance::Switch;
Media::Player::instance()->switchToNextEvents(
) | rpl::filter([=](const MediaSwitch &pair) {

View file

@ -702,6 +702,7 @@ private:
bool _preserveScrollTop = false;
bool _repaintFieldScheduled = false;
bool _sentFromScheduledTip = false;
mtpRequestId _saveEditMsgRequestId = 0;

View file

@ -16,10 +16,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_drag_area.h"
#include "history/history_item_helpers.h" // GetErrorTextForSending.
#include "menu/menu_send.h" // SendMenu::Type.
#include "ui/widgets/buttons.h"
#include "ui/widgets/tooltip.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/shadow.h"
#include "ui/chat/chat_style.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/dynamic_image.h"
#include "ui/dynamic_thumbnails.h"
#include "ui/ui_utility.h"
#include "api/api_editing.h"
#include "api/api_sending.h"
@ -34,7 +39,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/mime_type.h"
#include "chat_helpers/tabbed_selector.h"
#include "main/main_session.h"
#include "mainwindow.h"
#include "data/components/scheduled_messages.h"
#include "data/data_document.h"
#include "data/data_file_origin.h"
#include "data/data_forum.h"
#include "data/data_forum_topic.h"
#include "data/data_session.h"
@ -55,6 +63,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QMimeData>
namespace HistoryView {
namespace {
constexpr auto kVideoProcessingInfoDuration = 4 * crl::time(1000);
} // namespace
ScheduledMemento::ScheduledMemento(
not_null<History*> history,
@ -104,33 +117,33 @@ ScheduledWidget::ScheduledWidget(
not_null<Window::SessionController*> controller,
not_null<History*> history,
const Data::ForumTopic *forumTopic)
: Window::SectionWidget(parent, controller, history->peer)
, WindowListDelegate(controller)
, _show(controller->uiShow())
, _history(history)
, _forumTopic(forumTopic)
, _scroll(
this,
controller->chatStyle()->value(lifetime(), st::historyScroll),
false)
, _topBar(this, controller)
, _topBarShadow(this)
, _composeControls(std::make_unique<ComposeControls>(
this,
ComposeControlsDescriptor{
.show = controller->uiShow(),
.unavailableEmojiPasted = [=](not_null<DocumentData*> emoji) {
listShowPremiumToast(emoji);
},
.mode = ComposeControls::Mode::Scheduled,
.sendMenuDetails = [] { return SendMenu::Details(); },
.regularWindow = controller,
.stickerOrEmojiChosen = controller->stickerOrEmojiChosen(),
}))
, _cornerButtons(
_scroll.data(),
controller->chatStyle(),
static_cast<HistoryView::CornerButtonsDelegate*>(this)) {
: Window::SectionWidget(parent, controller, history->peer)
, WindowListDelegate(controller)
, _show(controller->uiShow())
, _history(history)
, _forumTopic(forumTopic)
, _scroll(
this,
controller->chatStyle()->value(lifetime(), st::historyScroll),
false)
, _topBar(this, controller)
, _topBarShadow(this)
, _composeControls(std::make_unique<ComposeControls>(
this,
ComposeControlsDescriptor{
.show = controller->uiShow(),
.unavailableEmojiPasted = [=](not_null<DocumentData*> emoji) {
listShowPremiumToast(emoji);
},
.mode = ComposeControls::Mode::Scheduled,
.sendMenuDetails = [] { return SendMenu::Details(); },
.regularWindow = controller,
.stickerOrEmojiChosen = controller->stickerOrEmojiChosen(),
}))
, _cornerButtons(
_scroll.data(),
controller->chatStyle(),
static_cast<HistoryView::CornerButtonsDelegate*>(this)) {
controller->chatStyle()->paletteChanged(
) | rpl::start_with_next([=] {
_scroll->updateBars();
@ -1070,6 +1083,24 @@ void ScheduledWidget::saveState(not_null<ScheduledMemento*> memento) {
void ScheduledWidget::restoreState(not_null<ScheduledMemento*> memento) {
_inner->restoreState(memento->list());
if (const auto id = memento->sentToScheduledId()) {
const auto item = _history->owner().message(_history->peer, id);
if (item) {
controller()->showToast({
.title = tr::lng_scheduled_video_tip_title(tr::now),
.text = { tr::lng_scheduled_video_tip_text(tr::now) },
.attach = RectPart::Top,
.duration = kVideoProcessingInfoDuration,
});
clearProcessingVideoTracking(false);
_processingVideoPosition = item->position();
_processingVideoTipTimer.setCallback([=] {
_processingVideoCanShow = true;
updateInnerVisibleArea();
});
_processingVideoTipTimer.callOnce(kVideoProcessingInfoDuration);
}
}
}
void ScheduledWidget::resizeEvent(QResizeEvent *e) {
@ -1141,9 +1172,153 @@ void ScheduledWidget::updateInnerVisibleArea() {
checkReplyReturns();
}
const auto scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
const auto scrollBottom = scrollTop + _scroll->height();
_inner->setVisibleTopBottom(scrollTop, scrollBottom);
_cornerButtons.updateJumpDownVisibility();
_cornerButtons.updateUnreadThingsVisibility();
if (!_processingVideoLifetime) {
if (const auto &position = _processingVideoPosition) {
if (const auto view = _inner->viewByPosition(position)) {
initProcessingVideoView(view);
}
}
}
checkProcessingVideoTooltip(scrollTop, scrollBottom);
}
void ScheduledWidget::initProcessingVideoView(not_null<Element*> view) {
_processingVideoView = view;
controller()->session().data().sentFromScheduled(
) | rpl::start_with_next([=](const Data::SentFromScheduled &value) {
if (value.item->position() == _processingVideoPosition) {
controller()->showPeerHistory(
value.item->history(),
Window::SectionShow::Way::Backward,
value.sentId);
}
}, _processingVideoLifetime);
controller()->session().data().viewRemoved(
) | rpl::start_with_next([=](not_null<const Element*> view) {
if (view == _processingVideoView.get()) {
const auto position = _processingVideoPosition;
if (const auto now = _inner->viewByPosition(position)) {
_processingVideoView = now;
updateProcessingVideoTooltipPosition();
} else {
clearProcessingVideoTracking(true);
}
}
}, _processingVideoLifetime);
controller()->session().data().viewResizeRequest(
) | rpl::start_with_next([this](not_null<const Element*> view) {
if (view->delegate() == _inner.data()) {
if (!_processingVideoUpdateScheduled) {
if (const auto tooltip = _processingVideoTooltip.get()) {
_processingVideoUpdateScheduled = true;
crl::on_main(tooltip, [=] {
_processingVideoUpdateScheduled = false;
updateProcessingVideoTooltipPosition();
});
}
}
}
}, _processingVideoLifetime);
}
void ScheduledWidget::clearProcessingVideoTracking(bool fast) {
if (const auto tooltip = _processingVideoTooltip.release()) {
tooltip->toggleAnimated(false);
}
_processingVideoPosition = {};
if (const auto tooltip = _processingVideoTooltip.release()) {
if (fast) {
tooltip->toggleFast(false);
} else {
tooltip->toggleAnimated(false);
}
}
_processingVideoTooltipShown = false;
_processingVideoCanShow = false;
_processingVideoView = nullptr;
_processingVideoTipTimer.cancel();
_processingVideoLifetime.destroy();
}
void ScheduledWidget::checkProcessingVideoTooltip(
int visibleTop,
int visibleBottom) {
if (_processingVideoTooltip
|| _processingVideoTooltipShown
|| !_processingVideoCanShow) {
return;
}
const auto view = _processingVideoView.get();
if (!view) {
_processingVideoCanShow = false;
return;
}
const auto rect = view->effectIconGeometry();
if (rect.top() > visibleTop
&& rect.top() + rect.height() <= visibleBottom) {
showProcessingVideoTooltip();
}
}
void ScheduledWidget::updateProcessingVideoTooltipPosition() {
const auto tooltip = _processingVideoTooltip.get();
if (!tooltip) {
return;
}
const auto view = _processingVideoView.get();
if (!view) {
clearProcessingVideoTracking(true);
return;
}
const auto shift = view->skipBlockWidth() / 2;
const auto rect = view->effectIconGeometry().translated(shift, 0);
const auto countPosition = [=](QSize size) {
const auto origin = rect.bottomLeft();
return origin - QPoint(
size.width() / 2,
size.height() + st::processingVideoTipShift);
};
tooltip->pointAt(rect, RectPart::Top, countPosition);
}
void ScheduledWidget::showProcessingVideoTooltip() {
_processingVideoTooltipShown = true;
_processingVideoTooltip = std::make_unique<Ui::ImportantTooltip>(
_inner.data(),
Ui::MakeNiceTooltipLabel(
_inner.data(),
tr::lng_scheduled_video_tip(Ui::Text::WithEntities),
st::processingVideoTipMaxWidth,
st::defaultImportantTooltipLabel),
st::defaultImportantTooltip);
const auto tooltip = _processingVideoTooltip.get();
const auto weak = QPointer<QWidget>(tooltip);
const auto destroy = [=] {
delete weak.data();
};
tooltip->setAttribute(Qt::WA_TransparentForMouseEvents);
tooltip->setHiddenCallback([=] {
const auto tip = _processingVideoTooltip.get();
if (tooltip == tip) {
_processingVideoTooltip.release();
}
crl::on_main(tip, [=] {
delete tip;
});
});
updateProcessingVideoTooltipPosition();
tooltip->toggleAnimated(true);
_processingVideoTipTimer.setCallback(crl::guard(tooltip, [=] {
tooltip->toggleAnimated(false);
}));
_processingVideoTipTimer.callOnce(kVideoProcessingInfoDuration);
}
void ScheduledWidget::showAnimatedHook(
@ -1495,4 +1670,114 @@ void ScheduledWidget::setupDragArea() {
areas.photo->setDroppedCallback(droppedCallback(true));
}
bool ShowScheduledVideoPublished(
not_null<Window::SessionController*> controller,
const Data::SentFromScheduled &info,
Fn<void()> hidden) {
if (!controller->widget()->isActive()) {
return false;
}
const auto media = info.item->media();
const auto document = media ? media->document() : nullptr;
if (!document->isVideoFile()) {
return false;
}
const auto history = info.item->history();
const auto itemId = info.sentId;
const auto text = tr::lng_scheduled_video_published(
tr::now,
Ui::Text::Bold);
const auto &st = st::processingVideoToast;
const auto skip = st::processingVideoPreviewSkip;
const auto size = st.style.font->height * 2;
const auto view = tr::lng_scheduled_video_view(tr::now);
const auto additional = QMargins(
skip + size,
0,
(st::processingVideoView.style.font->width(view)
- (st::processingVideoView.width / 2)),
0);
const auto parent = controller->uiShow()->toastParent();
const auto weak = Ui::Toast::Show(parent, Ui::Toast::Config{
.text = text,
.padding = rpl::single(additional),
.st = &st,
.attach = RectPart::Top,
.acceptinput = true,
.duration = kVideoProcessingInfoDuration,
});
const auto strong = weak.get();
if (!strong) {
return false;
}
const auto widget = strong->widget();
const auto hideToast = [weak] {
if (const auto strong = weak.get()) {
strong->hideAnimated();
}
};
const auto clickableBackground = Ui::CreateChild<Ui::AbstractButton>(
widget.get());
clickableBackground->setPointerCursor(false);
clickableBackground->setAcceptBoth();
clickableBackground->show();
clickableBackground->addClickHandler([=](Qt::MouseButton button) {
if (button == Qt::RightButton) {
hideToast();
}
});
const auto button = Ui::CreateChild<Ui::RoundButton>(
widget.get(),
rpl::single(view),
st::processingVideoView);
button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
button->show();
rpl::combine(
widget->sizeValue(),
button->sizeValue()
) | rpl::start_with_next([=](QSize outer, QSize inner) {
button->moveToRight(
0,
(outer.height() - inner.height()) / 2,
outer.width());
clickableBackground->resize(outer);
}, widget->lifetime());
const auto preview = Ui::CreateChild<Ui::RpWidget>(widget.get());
preview->moveToLeft(skip, skip);
preview->resize(size, size);
preview->show();
const auto thumbnail = Ui::MakeDocumentThumbnail(document, FullMsgId(
history->peer->id,
itemId));
thumbnail->subscribeToUpdates([=] {
preview->update();
});
preview->paintRequest(
) | rpl::start_with_next([=] {
auto p = QPainter(preview);
const auto image = Images::Round(
thumbnail->image(size),
ImageRoundRadius::Small);
p.drawImage(QRect(0, 0, size, size), image);
}, preview->lifetime());
button->setClickedCallback([=] {
controller->showPeerHistory(
history,
Window::SectionShow::Way::Forward,
itemId);
hideToast();
});
if (hidden) {
widget->lifetime().add(std::move(hidden));
}
return true;
}
} // namespace HistoryView

View file

@ -21,6 +21,10 @@ namespace ChatHelpers {
class Show;
} // namespace ChatHelpers
namespace Data {
struct SentFromScheduled;
} // namespace Data
namespace SendMenu {
struct Details;
} // namespace SendMenu
@ -37,6 +41,7 @@ class PlainShadow;
class FlatButton;
struct PreparedList;
class SendFilesWay;
class ImportantTooltip;
} // namespace Ui
namespace Profile {
@ -51,6 +56,10 @@ namespace HistoryView::Controls {
struct VoiceToSend;
} // namespace HistoryView::Controls
namespace Window {
class SessionController;
} // namespace Window
namespace HistoryView {
class Element;
@ -199,6 +208,12 @@ private:
Data::MessagePosition position,
FullMsgId originId = {});
void initProcessingVideoView(not_null<Element*> view);
void checkProcessingVideoTooltip(int visibleTop, int visibleBottom);
void showProcessingVideoTooltip();
void updateProcessingVideoTooltipPosition();
void clearProcessingVideoTracking(bool fast);
void setupComposeControls();
void setupDragArea();
@ -277,7 +292,16 @@ private:
std::unique_ptr<ComposeControls> _composeControls;
bool _skipScrollEvent = false;
Data::MessagePosition _processingVideoPosition;
base::weak_ptr<Element> _processingVideoView;
rpl::lifetime _processingVideoLifetime;
std::unique_ptr<HistoryView::StickerToast> _stickerToast;
std::unique_ptr<Ui::ImportantTooltip> _processingVideoTooltip;
base::Timer _processingVideoTipTimer;
bool _processingVideoUpdateScheduled = false;
bool _processingVideoTooltipShown = false;
bool _processingVideoCanShow = false;
CornerButtons _cornerButtons;
@ -299,20 +323,29 @@ public:
Window::Column column,
const QRect &geometry) override;
not_null<History*> getHistory() const {
[[nodiscard]] not_null<History*> getHistory() const {
return _history;
}
not_null<ListMemento*> list() {
[[nodiscard]] not_null<ListMemento*> list() {
return &_list;
}
[[nodiscard]] MsgId sentToScheduledId() const {
return _sentToScheduledId;
}
private:
const not_null<History*> _history;
const Data::ForumTopic *_forumTopic;
ListMemento _list;
MsgId _sentToScheduledId;
MsgId _sentToScheduledId = 0;
};
bool ShowScheduledVideoPublished(
not_null<Window::SessionController*> controller,
const Data::SentFromScheduled &info,
Fn<void()> hidden = nullptr);
} // namespace HistoryView

View file

@ -1982,10 +1982,12 @@ bool Gif::dataLoaded() const {
}
bool Gif::needInfoDisplay() const {
if (_parent->data()->isFakeAboutView()) {
const auto item = _parent->data();
if (item->isFakeAboutView()) {
return false;
}
return _parent->data()->isSending()
return item->isSending()
|| item->awaitingVideoProcessing()
|| _data->uploading()
|| _parent->isUnderCursor()
|| (_parent->delegate()->elementContext() == Context::ChatPreview)

View file

@ -893,9 +893,11 @@ bool GroupedMedia::computeNeedBubble() const {
}
bool GroupedMedia::needInfoDisplay() const {
const auto item = _parent->data();
return (_mode != Mode::Column)
&& (_parent->data()->isSending()
|| _parent->data()->hasFailed()
&& (item->isSending()
|| item->awaitingVideoProcessing()
|| item->hasFailed()
|| _parent->isUnderCursor()
|| (_parent->delegate()->elementContext() == Context::ChatPreview)
|| _parent->isLastAndSelfMessage());

View file

@ -820,7 +820,7 @@ rpl::producer<uint64> AddCurrencyAction(
) | rpl::start_with_error_done([=](const QString &error) {
currencyLoadLifetime->destroy();
}, [=] {
if (const auto strong = weak.get()) {
if (const auto strong = weak.data()) {
state->balance = currencyLoad->data().currentBalance;
currencyLoadLifetime->destroy();
}

View file

@ -59,9 +59,9 @@ private:
};
class StoryThumbnail : public DynamicImage {
class MediaThumbnail : public DynamicImage {
public:
explicit StoryThumbnail(Data::FileOrigin origin, bool forceRound);
explicit MediaThumbnail(Data::FileOrigin origin, bool forceRound);
QImage image(int size) override;
void subscribeToUpdates(Fn<void()> callback) override;
@ -89,7 +89,7 @@ private:
};
class PhotoThumbnail final : public StoryThumbnail {
class PhotoThumbnail final : public MediaThumbnail {
public:
PhotoThumbnail(
not_null<PhotoData*> photo,
@ -108,7 +108,7 @@ private:
};
class VideoThumbnail final : public StoryThumbnail {
class VideoThumbnail final : public MediaThumbnail {
public:
VideoThumbnail(
not_null<DocumentData*> video,
@ -304,12 +304,12 @@ void PeerUserpic::processNewPhoto() {
}, _subscribed->downloadLifetime);
}
StoryThumbnail::StoryThumbnail(Data::FileOrigin origin, bool forceRound)
MediaThumbnail::MediaThumbnail(Data::FileOrigin origin, bool forceRound)
: _origin(origin)
, _forceRound(forceRound) {
}
QImage StoryThumbnail::image(int size) {
QImage MediaThumbnail::image(int size) {
const auto ratio = style::DevicePixelRatio();
if (_prepared.width() != size * ratio) {
if (_full.isNull()) {
@ -333,7 +333,7 @@ QImage StoryThumbnail::image(int size) {
return _prepared;
}
void StoryThumbnail::subscribeToUpdates(Fn<void()> callback) {
void MediaThumbnail::subscribeToUpdates(Fn<void()> callback) {
_subscription.destroy();
if (!callback) {
clear();
@ -363,11 +363,11 @@ void StoryThumbnail::subscribeToUpdates(Fn<void()> callback) {
}
}
Data::FileOrigin StoryThumbnail::origin() const {
Data::FileOrigin MediaThumbnail::origin() const {
return _origin;
}
bool StoryThumbnail::forceRound() const {
bool MediaThumbnail::forceRound() const {
return _forceRound;
}
@ -375,7 +375,7 @@ PhotoThumbnail::PhotoThumbnail(
not_null<PhotoData*> photo,
Data::FileOrigin origin,
bool forceRound)
: StoryThumbnail(origin, forceRound)
: MediaThumbnail(origin, forceRound)
, _photo(photo) {
}
@ -387,7 +387,7 @@ Main::Session &PhotoThumbnail::session() {
return _photo->session();
}
StoryThumbnail::Thumb PhotoThumbnail::loaded(Data::FileOrigin origin) {
MediaThumbnail::Thumb PhotoThumbnail::loaded(Data::FileOrigin origin) {
if (!_media) {
_media = _photo->createMediaView();
_media->wanted(Data::PhotoSize::Small, origin);
@ -406,7 +406,7 @@ VideoThumbnail::VideoThumbnail(
not_null<DocumentData*> video,
Data::FileOrigin origin,
bool forceRound)
: StoryThumbnail(origin, forceRound)
: MediaThumbnail(origin, forceRound)
, _video(video) {
}
@ -418,7 +418,7 @@ Main::Session &VideoThumbnail::session() {
return _video->session();
}
StoryThumbnail::Thumb VideoThumbnail::loaded(Data::FileOrigin origin) {
MediaThumbnail::Thumb VideoThumbnail::loaded(Data::FileOrigin origin) {
if (!_media) {
_media = _video->createMediaView();
_media->thumbnailWanted(origin);

View file

@ -14,6 +14,7 @@ class PhotoData;
namespace Data {
class Story;
class Session;
struct FileOrigin;
} // namespace Data
namespace Ui {
@ -33,7 +34,6 @@ class DynamicImage;
[[nodiscard]] std::shared_ptr<DynamicImage> MakeEmojiThumbnail(
not_null<Data::Session*> owner,
const QString &data);
[[nodiscard]] std::shared_ptr<DynamicImage> MakePhotoThumbnail(
not_null<PhotoData*> photo,
FullMsgId fullId);