mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-18 07:07:08 +02:00
Add basic new messages animation.
This commit is contained in:
parent
551732738b
commit
7bbc4b7191
8 changed files with 189 additions and 80 deletions
|
@ -1441,12 +1441,12 @@ rpl::producer<not_null<const ViewElement*>> Session::viewLayoutChanged() const {
|
|||
return _viewLayoutChanges.events();
|
||||
}
|
||||
|
||||
void Session::notifyUnreadItemAdded(not_null<HistoryItem*> item) {
|
||||
_unreadItemAdded.fire_copy(item);
|
||||
void Session::notifyNewItemAdded(not_null<HistoryItem*> item) {
|
||||
_newItemAdded.fire_copy(item);
|
||||
}
|
||||
|
||||
rpl::producer<not_null<HistoryItem*>> Session::unreadItemAdded() const {
|
||||
return _unreadItemAdded.events();
|
||||
rpl::producer<not_null<HistoryItem*>> Session::newItemAdded() const {
|
||||
return _newItemAdded.events();
|
||||
}
|
||||
|
||||
void Session::changeMessageId(ChannelId channel, MsgId wasId, MsgId nowId) {
|
||||
|
|
|
@ -235,8 +235,8 @@ public:
|
|||
[[nodiscard]] rpl::producer<not_null<const HistoryItem*>> itemLayoutChanged() const;
|
||||
void notifyViewLayoutChange(not_null<const ViewElement*> view);
|
||||
[[nodiscard]] rpl::producer<not_null<const ViewElement*>> viewLayoutChanged() const;
|
||||
void notifyUnreadItemAdded(not_null<HistoryItem*> item);
|
||||
[[nodiscard]] rpl::producer<not_null<HistoryItem*>> unreadItemAdded() const;
|
||||
void notifyNewItemAdded(not_null<HistoryItem*> item);
|
||||
[[nodiscard]] rpl::producer<not_null<HistoryItem*>> newItemAdded() const;
|
||||
void requestItemRepaint(not_null<const HistoryItem*> item);
|
||||
[[nodiscard]] rpl::producer<not_null<const HistoryItem*>> itemRepaintRequest() const;
|
||||
void requestViewRepaint(not_null<const ViewElement*> view);
|
||||
|
@ -836,7 +836,7 @@ private:
|
|||
rpl::event_stream<IdChange> _itemIdChanges;
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemLayoutChanges;
|
||||
rpl::event_stream<not_null<const ViewElement*>> _viewLayoutChanges;
|
||||
rpl::event_stream<not_null<HistoryItem*>> _unreadItemAdded;
|
||||
rpl::event_stream<not_null<HistoryItem*>> _newItemAdded;
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemRepaintRequest;
|
||||
rpl::event_stream<not_null<const ViewElement*>> _viewRepaintRequest;
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemResizeRequest;
|
||||
|
|
|
@ -1072,16 +1072,16 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
|
|||
item->contributeToSlowmode();
|
||||
if (item->showNotification()) {
|
||||
_notifications.push_back(item);
|
||||
owner().notifyUnreadItemAdded(item);
|
||||
const auto stillShow = item->showNotification();
|
||||
if (stillShow) {
|
||||
Core::App().notifications().schedule(item);
|
||||
if (!item->out() && item->unread()) {
|
||||
if (unreadCountKnown()) {
|
||||
setUnreadCount(unreadCount() + 1);
|
||||
} else {
|
||||
owner().histories().requestDialogEntry(this);
|
||||
}
|
||||
}
|
||||
owner().notifyNewItemAdded(item);
|
||||
const auto stillShow = item->showNotification(); // Could be read already.
|
||||
if (stillShow) {
|
||||
Core::App().notifications().schedule(item);
|
||||
if (!item->out() && item->unread()) {
|
||||
if (unreadCountKnown()) {
|
||||
setUnreadCount(unreadCount() + 1);
|
||||
} else {
|
||||
owner().histories().requestDialogEntry(this);
|
||||
}
|
||||
}
|
||||
} else if (item->out()) {
|
||||
|
|
|
@ -2369,6 +2369,21 @@ void HistoryInner::repaintScrollDateCallback() {
|
|||
update(0, updateTop, width(), updateHeight);
|
||||
}
|
||||
|
||||
void HistoryInner::setItemsRevealHeight(int revealHeight) {
|
||||
_revealHeight = revealHeight;
|
||||
}
|
||||
|
||||
void HistoryInner::changeItemsRevealHeight(int revealHeight) {
|
||||
if (_revealHeight == revealHeight) {
|
||||
return;
|
||||
}
|
||||
const auto old = std::exchange(_revealHeight, revealHeight);
|
||||
resize(_scroll->width(), height() + old - _revealHeight);
|
||||
if (!_revealHeight) {
|
||||
mouseActionUpdate(QCursor::pos());
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryInner::updateSize() {
|
||||
int visibleHeight = _scroll->height();
|
||||
int newHistoryPaddingTop = qMax(visibleHeight - historyHeight() - st::historyPaddingBottom, 0);
|
||||
|
@ -2393,11 +2408,13 @@ void HistoryInner::updateSize() {
|
|||
|
||||
_historyPaddingTop = newHistoryPaddingTop;
|
||||
|
||||
int newHeight = _historyPaddingTop + historyHeight() + st::historyPaddingBottom;
|
||||
int newHeight = _historyPaddingTop + historyHeight() + st::historyPaddingBottom - _revealHeight;
|
||||
if (width() != _scroll->width() || height() != newHeight) {
|
||||
resize(_scroll->width(), newHeight);
|
||||
|
||||
mouseActionUpdate(QCursor::pos());
|
||||
if (!_revealHeight) {
|
||||
mouseActionUpdate(QCursor::pos());
|
||||
}
|
||||
} else {
|
||||
update();
|
||||
}
|
||||
|
|
|
@ -64,6 +64,8 @@ public:
|
|||
|
||||
void touchScrollUpdated(const QPoint &screenPos);
|
||||
|
||||
void setItemsRevealHeight(int revealHeight);
|
||||
void changeItemsRevealHeight(int revealHeight);
|
||||
void checkHistoryActivation();
|
||||
void recountHistoryGeometry();
|
||||
void updateSize();
|
||||
|
@ -345,6 +347,7 @@ private:
|
|||
History *_migrated = nullptr;
|
||||
int _contentWidth = 0;
|
||||
int _historyPaddingTop = 0;
|
||||
int _revealHeight = 0;
|
||||
|
||||
// Save visible area coords for painting / pressing userpics.
|
||||
int _visibleAreaTop = 0;
|
||||
|
|
|
@ -154,6 +154,7 @@ constexpr auto kSaveDraftTimeout = 1000;
|
|||
constexpr auto kSaveDraftAnywayTimeout = 5000;
|
||||
constexpr auto kSaveCloudDraftIdleTimeout = 14000;
|
||||
constexpr auto kRefreshSlowmodeLabelTimeout = crl::time(200);
|
||||
constexpr auto kItemRevealDuration = crl::time(200);
|
||||
constexpr auto kCommonModifiers = 0
|
||||
| Qt::ShiftModifier
|
||||
| Qt::MetaModifier
|
||||
|
@ -425,14 +426,9 @@ HistoryWidget::HistoryWidget(
|
|||
}
|
||||
}, lifetime());
|
||||
|
||||
session().data().unreadItemAdded(
|
||||
session().data().newItemAdded(
|
||||
) | rpl::start_with_next([=](not_null<HistoryItem*> item) {
|
||||
unreadMessageAdded(item);
|
||||
}, lifetime());
|
||||
|
||||
session().data().itemRemoved(
|
||||
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
|
||||
itemRemoved(item);
|
||||
newItemAdded(item);
|
||||
}, lifetime());
|
||||
|
||||
session().data().historyChanged(
|
||||
|
@ -539,43 +535,25 @@ HistoryWidget::HistoryWidget(
|
|||
}, lifetime());
|
||||
|
||||
session().changes().messageUpdates(
|
||||
Data::MessageUpdate::Flag::Edited
|
||||
Data::MessageUpdate::Flag::Destroyed
|
||||
| Data::MessageUpdate::Flag::Edited
|
||||
| Data::MessageUpdate::Flag::ReplyMarkup
|
||||
| Data::MessageUpdate::Flag::BotCallbackSent
|
||||
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
||||
itemEdited(update.item);
|
||||
}, lifetime());
|
||||
|
||||
session().changes().messageUpdates(
|
||||
Data::MessageUpdate::Flag::ReplyMarkup
|
||||
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
||||
if (_keyboard->forMsgId() == update.item->fullId()) {
|
||||
updateBotKeyboard(update.item->history(), true);
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
session().changes().messageUpdates(
|
||||
Data::MessageUpdate::Flag::BotCallbackSent
|
||||
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
||||
const auto item = update.item;
|
||||
if (item->id < 0 || _peer != item->history()->peer) {
|
||||
if (update.flags & Data::MessageUpdate::Flag::Destroyed) {
|
||||
itemRemoved(update.item);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto keyId = _keyboard->forMsgId();
|
||||
const auto lastKeyboardUsed = (keyId == FullMsgId(_channel, item->id))
|
||||
&& (keyId == FullMsgId(_channel, _history->lastKeyboardId));
|
||||
|
||||
session().data().requestItemRepaint(item);
|
||||
|
||||
if (_replyToId == item->id) {
|
||||
cancelReply();
|
||||
if (update.flags & Data::MessageUpdate::Flag::Edited) {
|
||||
itemEdited(update.item);
|
||||
}
|
||||
if (_keyboard->singleUse()
|
||||
&& _keyboard->hasMarkup()
|
||||
&& lastKeyboardUsed) {
|
||||
if (_kbShown) {
|
||||
toggleKeyboard(false);
|
||||
if (update.flags & Data::MessageUpdate::Flag::ReplyMarkup) {
|
||||
if (_keyboard->forMsgId() == update.item->fullId()) {
|
||||
updateBotKeyboard(update.item->history(), true);
|
||||
}
|
||||
_history->lastKeyboardUsed = true;
|
||||
}
|
||||
if (update.flags & Data::MessageUpdate::Flag::BotCallbackSent) {
|
||||
botCallbackSent(update.item);
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
|
@ -1887,7 +1865,6 @@ void HistoryWidget::showHistory(
|
|||
|
||||
App::clearMousedItems();
|
||||
|
||||
_addToScroll = 0;
|
||||
_saveEditMsgRequestId = 0;
|
||||
_replyEditMsg = nullptr;
|
||||
_editMsgId = _replyToId = 0;
|
||||
|
@ -1939,6 +1916,7 @@ void HistoryWidget::showHistory(
|
|||
|
||||
noSelectingScroll();
|
||||
_nonEmptySelection = false;
|
||||
_itemRevealPending.clear();
|
||||
|
||||
if (_peer) {
|
||||
_history = _peer->owner().history(_peer);
|
||||
|
@ -2447,8 +2425,10 @@ void HistoryWidget::destroyUnreadBarOnClose() {
|
|||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::unreadMessageAdded(not_null<HistoryItem*> item) {
|
||||
if (_history != item->history() || !_historyInited) {
|
||||
void HistoryWidget::newItemAdded(not_null<HistoryItem*> item) {
|
||||
if (_history != item->history()
|
||||
|| !_historyInited
|
||||
|| item->isScheduled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2459,21 +2439,28 @@ void HistoryWidget::unreadMessageAdded(not_null<HistoryItem*> item) {
|
|||
// - on second we get wrong doWeReadServerHistory() and read both.
|
||||
session().data().sendHistoryChangeNotifications();
|
||||
|
||||
const auto atBottom = (_scroll->scrollTop() >= _scroll->scrollTopMax());
|
||||
if (!atBottom) {
|
||||
if (item->isSending()) {
|
||||
synteticScrollToY(_scroll->scrollTopMax());
|
||||
} else if (_scroll->scrollTop() < _scroll->scrollTopMax()) {
|
||||
return;
|
||||
}
|
||||
destroyUnreadBar();
|
||||
if (!doWeReadServerHistory()) {
|
||||
return;
|
||||
}
|
||||
if (item->isUnreadMention() && !item->isUnreadMedia()) {
|
||||
session().api().markMediaRead(item);
|
||||
}
|
||||
session().data().histories().readInboxOnNewMessage(item);
|
||||
if (item->showNotification()) {
|
||||
destroyUnreadBar();
|
||||
if (doWeReadServerHistory()) {
|
||||
if (item->isUnreadMention() && !item->isUnreadMedia()) {
|
||||
session().api().markMediaRead(item);
|
||||
}
|
||||
session().data().histories().readInboxOnNewMessage(item);
|
||||
|
||||
// Also clear possible scheduled messages notifications.
|
||||
Core::App().notifications().clearFromHistory(_history);
|
||||
// Also clear possible scheduled messages notifications.
|
||||
Core::App().notifications().clearFromHistory(_history);
|
||||
}
|
||||
}
|
||||
const auto view = item->mainView();
|
||||
if (anim::Disabled() || !view) {
|
||||
return;
|
||||
}
|
||||
_itemRevealPending.emplace(item);
|
||||
}
|
||||
|
||||
void HistoryWidget::unreadCountUpdated() {
|
||||
|
@ -2904,9 +2891,13 @@ void HistoryWidget::delayedShowAt(MsgId showAtMsgId) {
|
|||
}
|
||||
|
||||
void HistoryWidget::handleScroll() {
|
||||
preloadHistoryIfNeeded();
|
||||
if (!_itemsRevealHeight) {
|
||||
preloadHistoryIfNeeded();
|
||||
}
|
||||
visibleAreaUpdated();
|
||||
updatePinnedViewer();
|
||||
if (!_itemsRevealHeight) {
|
||||
updatePinnedViewer();
|
||||
}
|
||||
if (!_synteticScrollEvent) {
|
||||
_lastUserScrolled = crl::now();
|
||||
}
|
||||
|
@ -4692,6 +4683,11 @@ void HistoryWidget::itemRemoved(not_null<const HistoryItem*> item) {
|
|||
updateControlsGeometry();
|
||||
}
|
||||
}
|
||||
const auto i = _itemRevealAnimations.find(item);
|
||||
if (i != end(_itemRevealAnimations)) {
|
||||
_itemRevealAnimations.erase(i);
|
||||
revealItemsCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::itemEdited(not_null<HistoryItem*> item) {
|
||||
|
@ -4784,6 +4780,9 @@ void HistoryWidget::updateHistoryGeometry(
|
|||
bool initial,
|
||||
bool loadedDown,
|
||||
const ScrollChange &change) {
|
||||
const auto guard = gsl::finally([&] {
|
||||
_itemRevealPending.clear();
|
||||
});
|
||||
if (!_history || (initial && _historyInited) || (!initial && !_historyInited)) {
|
||||
return;
|
||||
}
|
||||
|
@ -4867,20 +4866,74 @@ void HistoryWidget::updateHistoryGeometry(
|
|||
newScrollTop += change.value;
|
||||
} else if (change.type == ScrollChangeNoJumpToBottom) {
|
||||
newScrollTop = wasScrollTop;
|
||||
} else if (const auto add = base::take(_addToScroll)) {
|
||||
newScrollTop += add;
|
||||
}
|
||||
}
|
||||
const auto toY = std::clamp(newScrollTop, 0, _scroll->scrollTopMax());
|
||||
synteticScrollToY(toY);
|
||||
}
|
||||
|
||||
void HistoryWidget::revealItemsCallback() {
|
||||
auto height = 0;
|
||||
if (!_historyInited) {
|
||||
_itemRevealAnimations.clear();
|
||||
}
|
||||
for (auto i = begin(_itemRevealAnimations)
|
||||
; i != end(_itemRevealAnimations);) {
|
||||
if (!i->second.animation.animating()) {
|
||||
i = _itemRevealAnimations.erase(i);
|
||||
} else {
|
||||
height += anim::interpolate(
|
||||
i->second.startHeight,
|
||||
0,
|
||||
i->second.animation.value(1.));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
if (_itemsRevealHeight != height) {
|
||||
const auto wasScrollTop = _scroll->scrollTop();
|
||||
const auto wasAtBottom = (wasScrollTop == _scroll->scrollTopMax());
|
||||
|
||||
_itemsRevealHeight = height;
|
||||
_list->changeItemsRevealHeight(_itemsRevealHeight);
|
||||
|
||||
const auto newScrollTop = (wasAtBottom && !_history->unreadBar())
|
||||
? countAutomaticScrollTop()
|
||||
: _list->historyScrollTop();
|
||||
const auto toY = std::clamp(newScrollTop, 0, _scroll->scrollTopMax());
|
||||
synteticScrollToY(toY);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::updateListSize() {
|
||||
_list->recountHistoryGeometry();
|
||||
auto washidden = _scroll->isHidden();
|
||||
if (washidden) {
|
||||
_scroll->show();
|
||||
}
|
||||
for (const auto item : base::take(_itemRevealPending)) {
|
||||
if (const auto view = item->mainView()) {
|
||||
if (const auto top = _list->itemTop(view); top >= 0) {
|
||||
if (const auto height = view->height()) {
|
||||
if (!_itemRevealAnimations.contains(item)) {
|
||||
auto &animation = _itemRevealAnimations[item];
|
||||
if (!animation.animation.animating()) {
|
||||
animation.startHeight
|
||||
= animation.currentHeight
|
||||
= height;
|
||||
_itemsRevealHeight += height;
|
||||
animation.animation.start(
|
||||
[=] { revealItemsCallback(); },
|
||||
0.,
|
||||
1.,
|
||||
kItemRevealDuration,
|
||||
anim::easeOutCirc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_list->setItemsRevealHeight(_itemsRevealHeight);
|
||||
_list->updateSize();
|
||||
if (washidden) {
|
||||
_scroll->hide();
|
||||
|
@ -5029,6 +5082,30 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
|
|||
update();
|
||||
}
|
||||
|
||||
void HistoryWidget::botCallbackSent(not_null<HistoryItem*> item) {
|
||||
if (item->id < 0 || _peer != item->history()->peer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto keyId = _keyboard->forMsgId();
|
||||
const auto lastKeyboardUsed = (keyId == FullMsgId(_channel, item->id))
|
||||
&& (keyId == FullMsgId(_channel, _history->lastKeyboardId));
|
||||
|
||||
session().data().requestItemRepaint(item);
|
||||
|
||||
if (_replyToId == item->id) {
|
||||
cancelReply();
|
||||
}
|
||||
if (_keyboard->singleUse()
|
||||
&& _keyboard->hasMarkup()
|
||||
&& lastKeyboardUsed) {
|
||||
if (_kbShown) {
|
||||
toggleKeyboard(false);
|
||||
}
|
||||
_history->lastKeyboardUsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
int HistoryWidget::computeMaxFieldHeight() const {
|
||||
const auto available = height()
|
||||
- _topBar->height()
|
||||
|
|
|
@ -225,6 +225,7 @@ public:
|
|||
// With force=true the markup is updated even if it is
|
||||
// already shown for the passed history item.
|
||||
void updateBotKeyboard(History *h = nullptr, bool force = false);
|
||||
void botCallbackSent(not_null<HistoryItem*> item);
|
||||
|
||||
void fastShowAtEnd(not_null<History*> history);
|
||||
void applyDraft(
|
||||
|
@ -321,6 +322,11 @@ private:
|
|||
Fn<void(MessageIdsList)> callback;
|
||||
bool active = false;
|
||||
};
|
||||
struct ItemRevealAnimation {
|
||||
int startHeight = 0;
|
||||
int currentHeight = 0;
|
||||
Ui::Animations::Simple animation;
|
||||
};
|
||||
enum class TextUpdateEvent {
|
||||
SaveDraft = (1 << 0),
|
||||
SendTyping = (1 << 1),
|
||||
|
@ -414,7 +420,7 @@ private:
|
|||
void historyDownAnimationFinish();
|
||||
void unreadMentionsAnimationFinish();
|
||||
void sendButtonClicked();
|
||||
void unreadMessageAdded(not_null<HistoryItem*> item);
|
||||
void newItemAdded(not_null<HistoryItem*> item);
|
||||
|
||||
bool canSendFiles(not_null<const QMimeData*> data) const;
|
||||
bool confirmSendingFiles(
|
||||
|
@ -530,6 +536,7 @@ private:
|
|||
|
||||
void updateHistoryGeometry(bool initial = false, bool loadedDown = false, const ScrollChange &change = { ScrollChangeNone, 0 });
|
||||
void updateListSize();
|
||||
void revealItemsCallback();
|
||||
|
||||
// Does any of the shown histories has this flag set.
|
||||
bool hasPendingResizedItems() const;
|
||||
|
@ -664,7 +671,6 @@ private:
|
|||
bool _historyInited = false;
|
||||
// If updateListSize() was called without updateHistoryGeometry().
|
||||
bool _updateHistoryGeometryRequired = false;
|
||||
int _addToScroll = 0;
|
||||
|
||||
int _lastScrollTop = 0; // gifs optimization
|
||||
crl::time _lastScrolled = 0;
|
||||
|
@ -755,6 +761,12 @@ private:
|
|||
base::weak_ptr<Ui::Toast::Instance> _topToast;
|
||||
std::unique_ptr<ChooseMessagesForReport> _chooseForReport;
|
||||
|
||||
base::flat_set<not_null<HistoryItem*>> _itemRevealPending;
|
||||
base::flat_map<
|
||||
not_null<HistoryItem*>,
|
||||
ItemRevealAnimation> _itemRevealAnimations;
|
||||
int _itemsRevealHeight = 0;
|
||||
|
||||
object_ptr<Ui::PlainShadow> _topShadow;
|
||||
bool _inGrab = false;
|
||||
|
||||
|
|
|
@ -1124,7 +1124,7 @@ bool ListWidget::loadedAtBottom() const {
|
|||
}
|
||||
|
||||
bool ListWidget::isEmpty() const {
|
||||
return loadedAtTop() && loadedAtBottom() && (_itemsHeight == 0);
|
||||
return loadedAtTop() && loadedAtBottom();
|
||||
}
|
||||
|
||||
int ListWidget::itemMinimalHeight() const {
|
||||
|
|
Loading…
Add table
Reference in a new issue