mirror of
https://github.com/kotatogram/kotatogram-desktop
synced 2025-08-31 14:45:14 +00:00
Display unread reactions badge in chats list.
This commit is contained in:
@@ -848,10 +848,6 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
mouseActionUpdate();
|
||||
}
|
||||
|
||||
const auto guard = gsl::finally([&] {
|
||||
_userpicsCache.clear();
|
||||
});
|
||||
|
||||
Painter p(this);
|
||||
auto clip = e->rect();
|
||||
|
||||
@@ -872,7 +868,6 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
const auto now = crl::now();
|
||||
const auto historyDisplayedEmpty = _history->isDisplayedEmpty()
|
||||
&& (!_migrated || _migrated->isDisplayedEmpty());
|
||||
bool noHistoryDisplayed = historyDisplayedEmpty;
|
||||
if (_botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) {
|
||||
const auto st = context.st;
|
||||
const auto stm = &st->messageStyle(false, false);
|
||||
@@ -899,254 +894,251 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
_emptyPainter = nullptr;
|
||||
}
|
||||
|
||||
_reactionsManager->startEffectsCollection();
|
||||
if (!noHistoryDisplayed) {
|
||||
auto readMentions = base::flat_set<not_null<HistoryItem*>>();
|
||||
const auto mtop = migratedTop();
|
||||
const auto htop = historyTop();
|
||||
if (historyDisplayedEmpty || (mtop < 0 && htop < 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
adjustCurrent(clip.top());
|
||||
|
||||
auto drawToY = clip.y() + clip.height();
|
||||
|
||||
auto selfromy = itemTop(_dragSelFrom);
|
||||
auto seltoy = itemTop(_dragSelTo);
|
||||
if (selfromy < 0 || seltoy < 0) {
|
||||
selfromy = seltoy = -1;
|
||||
} else {
|
||||
seltoy += _dragSelTo->height();
|
||||
auto readTill = (HistoryItem*)nullptr;
|
||||
auto readContents = base::flat_set<not_null<HistoryItem*>>();
|
||||
const auto guard = gsl::finally([&] {
|
||||
if (readTill && _widget->doWeReadServerHistory()) {
|
||||
session().data().histories().readInboxTill(readTill);
|
||||
}
|
||||
if (!readContents.empty() && _widget->doWeReadMentions()) {
|
||||
session().api().markContentsRead(readContents);
|
||||
}
|
||||
_userpicsCache.clear();
|
||||
});
|
||||
|
||||
auto mtop = migratedTop();
|
||||
auto htop = historyTop();
|
||||
auto hdrawtop = historyDrawTop();
|
||||
if (mtop >= 0) {
|
||||
auto iBlock = (_curHistory == _migrated ? _curBlock : (_migrated->blocks.size() - 1));
|
||||
auto block = _migrated->blocks[iBlock].get();
|
||||
auto iItem = (_curHistory == _migrated ? _curItem : (block->messages.size() - 1));
|
||||
auto view = block->messages[iItem].get();
|
||||
auto item = view->data();
|
||||
const auto processPainted = [&](
|
||||
not_null<Element*> view,
|
||||
int top,
|
||||
int height) {
|
||||
const auto item = view->data();
|
||||
const auto isSponsored = item->isSponsored();
|
||||
const auto isUnread = !item->out()
|
||||
&& item->unread()
|
||||
&& (item->history() == _history);
|
||||
const auto withReaction = item->hasUnreadReaction();
|
||||
const auto yShown = [&](int y) {
|
||||
return (_visibleAreaBottom >= y && _visibleAreaTop <= y);
|
||||
};
|
||||
const auto markShown = isSponsored
|
||||
? view->markSponsoredViewed(_visibleAreaBottom - top)
|
||||
: withReaction
|
||||
? yShown(top + context.reactionInfo->position.y())
|
||||
: isUnread
|
||||
? yShown(top + height)
|
||||
: yShown(top + height / 2);
|
||||
if (markShown) {
|
||||
if (isSponsored) {
|
||||
session().data().sponsoredMessages().view(
|
||||
item->fullId());
|
||||
} else if (isUnread) {
|
||||
readTill = item;
|
||||
}
|
||||
if (item->hasViews()) {
|
||||
session().api().views().scheduleIncrement(item);
|
||||
}
|
||||
if (withReaction) {
|
||||
readContents.insert(item);
|
||||
} else if (item->isUnreadMention()
|
||||
&& !item->isUnreadMedia()) {
|
||||
readContents.insert(item);
|
||||
_widget->enqueueMessageHighlight(view);
|
||||
}
|
||||
}
|
||||
session().data().reactions().poll(item, now);
|
||||
_reactionsManager->recordCurrentReactionEffect(
|
||||
item->fullId(),
|
||||
QPoint(0, top));
|
||||
};
|
||||
|
||||
auto top = mtop + block->y() + view->y();
|
||||
context.translate(0, -top);
|
||||
p.translate(0, top);
|
||||
if (context.clip.y() < view->height()) while (top < drawToY) {
|
||||
context.reactionEffects
|
||||
= _reactionsManager->currentReactionEffect();
|
||||
adjustCurrent(clip.top());
|
||||
|
||||
const auto drawToY = clip.y() + clip.height();
|
||||
|
||||
auto selfromy = itemTop(_dragSelFrom);
|
||||
auto seltoy = itemTop(_dragSelTo);
|
||||
if (selfromy < 0 || seltoy < 0) {
|
||||
selfromy = seltoy = -1;
|
||||
} else {
|
||||
seltoy += _dragSelTo->height();
|
||||
}
|
||||
|
||||
const auto hdrawtop = historyDrawTop();
|
||||
if (mtop >= 0) {
|
||||
auto iBlock = (_curHistory == _migrated ? _curBlock : (_migrated->blocks.size() - 1));
|
||||
auto block = _migrated->blocks[iBlock].get();
|
||||
auto iItem = (_curHistory == _migrated ? _curItem : (block->messages.size() - 1));
|
||||
auto view = block->messages[iItem].get();
|
||||
auto top = mtop + block->y() + view->y();
|
||||
context.translate(0, -top);
|
||||
p.translate(0, top);
|
||||
if (context.clip.y() < view->height()) while (top < drawToY) {
|
||||
const auto height = view->height();
|
||||
context.reactionInfo
|
||||
= _reactionsManager->currentReactionPaintInfo();
|
||||
context.outbg = view->hasOutLayout();
|
||||
context.selection = itemRenderSelection(
|
||||
view,
|
||||
selfromy - mtop,
|
||||
seltoy - mtop);
|
||||
view->draw(p, context);
|
||||
processPainted(view, top, height);
|
||||
|
||||
top += height;
|
||||
context.translate(0, -height);
|
||||
p.translate(0, height);
|
||||
|
||||
++iItem;
|
||||
if (iItem == block->messages.size()) {
|
||||
iItem = 0;
|
||||
++iBlock;
|
||||
if (iBlock == _migrated->blocks.size()) {
|
||||
break;
|
||||
}
|
||||
block = _migrated->blocks[iBlock].get();
|
||||
}
|
||||
view = block->messages[iItem].get();
|
||||
}
|
||||
context.translate(0, top);
|
||||
p.translate(0, -top);
|
||||
}
|
||||
if (htop >= 0) {
|
||||
auto iBlock = (_curHistory == _history ? _curBlock : 0);
|
||||
auto block = _history->blocks[iBlock].get();
|
||||
auto iItem = (_curHistory == _history ? _curItem : 0);
|
||||
auto view = block->messages[iItem].get();
|
||||
auto top = htop + block->y() + view->y();
|
||||
context.clip = clip.intersected(
|
||||
QRect(0, hdrawtop, width(), clip.top() + clip.height()));
|
||||
context.translate(0, -top);
|
||||
p.translate(0, top);
|
||||
while (top < drawToY) {
|
||||
const auto height = view->height();
|
||||
if (context.clip.y() < height && hdrawtop < top + height) {
|
||||
context.reactionInfo
|
||||
= _reactionsManager->currentReactionPaintInfo();
|
||||
context.outbg = view->hasOutLayout();
|
||||
context.selection = itemRenderSelection(
|
||||
view,
|
||||
selfromy - mtop,
|
||||
seltoy - mtop);
|
||||
selfromy - htop,
|
||||
seltoy - htop);
|
||||
view->draw(p, context);
|
||||
_reactionsManager->recordCurrentReactionEffect(
|
||||
item->fullId(),
|
||||
QPoint(0, top));
|
||||
|
||||
const auto height = view->height();
|
||||
const auto middle = top + height / 2;
|
||||
if (_visibleAreaBottom >= middle
|
||||
&& _visibleAreaTop <= middle) {
|
||||
if (item->hasViews()) {
|
||||
session().api().views().scheduleIncrement(item);
|
||||
}
|
||||
if (item->isUnreadMention() && !item->isUnreadMedia()) {
|
||||
readMentions.insert(item);
|
||||
_widget->enqueueMessageHighlight(view);
|
||||
}
|
||||
session().data().reactions().poll(item, now);
|
||||
}
|
||||
|
||||
top += height;
|
||||
context.translate(0, -height);
|
||||
p.translate(0, height);
|
||||
|
||||
++iItem;
|
||||
if (iItem == block->messages.size()) {
|
||||
iItem = 0;
|
||||
++iBlock;
|
||||
if (iBlock == _migrated->blocks.size()) {
|
||||
break;
|
||||
}
|
||||
block = _migrated->blocks[iBlock].get();
|
||||
}
|
||||
view = block->messages[iItem].get();
|
||||
item = view->data();
|
||||
processPainted(view, top, height);
|
||||
}
|
||||
context.translate(0, top);
|
||||
p.translate(0, -top);
|
||||
}
|
||||
if (htop >= 0) {
|
||||
auto iBlock = (_curHistory == _history ? _curBlock : 0);
|
||||
auto block = _history->blocks[iBlock].get();
|
||||
auto iItem = (_curHistory == _history ? _curItem : 0);
|
||||
auto view = block->messages[iItem].get();
|
||||
auto item = view->data();
|
||||
auto readTill = (HistoryItem*)nullptr;
|
||||
auto top = htop + block->y() + view->y();
|
||||
context.clip = clip.intersected(
|
||||
QRect(0, hdrawtop, width(), clip.top() + clip.height()));
|
||||
context.translate(0, -top);
|
||||
p.translate(0, top);
|
||||
while (top < drawToY) {
|
||||
const auto height = view->height();
|
||||
if (context.clip.y() < height && hdrawtop < top + height) {
|
||||
context.reactionEffects
|
||||
= _reactionsManager->currentReactionEffect();
|
||||
context.outbg = view->hasOutLayout();
|
||||
context.selection = itemRenderSelection(
|
||||
view,
|
||||
selfromy - htop,
|
||||
seltoy - htop);
|
||||
view->draw(p, context);
|
||||
_reactionsManager->recordCurrentReactionEffect(
|
||||
item->fullId(),
|
||||
QPoint(0, top));
|
||||
top += height;
|
||||
context.translate(0, -height);
|
||||
p.translate(0, height);
|
||||
|
||||
const auto middle = top + height / 2;
|
||||
const auto bottom = top + height;
|
||||
if (_visibleAreaBottom >= bottom) {
|
||||
if (!item->out() && item->unread()) {
|
||||
readTill = item;
|
||||
}
|
||||
}
|
||||
if (item->isSponsored()
|
||||
&& view->markSponsoredViewed(
|
||||
_visibleAreaBottom - top)) {
|
||||
session().data().sponsoredMessages().view(
|
||||
item->fullId());
|
||||
}
|
||||
if (_visibleAreaBottom >= middle
|
||||
&& _visibleAreaTop <= middle) {
|
||||
if (item->hasViews()) {
|
||||
session().api().views().scheduleIncrement(item);
|
||||
}
|
||||
if (item->isUnreadMention()
|
||||
&& !item->isUnreadMedia()) {
|
||||
readMentions.insert(item);
|
||||
_widget->enqueueMessageHighlight(view);
|
||||
}
|
||||
}
|
||||
session().data().reactions().poll(item, now);
|
||||
++iItem;
|
||||
if (iItem == block->messages.size()) {
|
||||
iItem = 0;
|
||||
++iBlock;
|
||||
if (iBlock == _history->blocks.size()) {
|
||||
break;
|
||||
}
|
||||
top += height;
|
||||
context.translate(0, -height);
|
||||
p.translate(0, height);
|
||||
|
||||
++iItem;
|
||||
if (iItem == block->messages.size()) {
|
||||
iItem = 0;
|
||||
++iBlock;
|
||||
if (iBlock == _history->blocks.size()) {
|
||||
break;
|
||||
}
|
||||
block = _history->blocks[iBlock].get();
|
||||
}
|
||||
view = block->messages[iItem].get();
|
||||
item = view->data();
|
||||
}
|
||||
context.translate(0, top);
|
||||
p.translate(0, -top);
|
||||
|
||||
if (readTill && _widget->doWeReadServerHistory()) {
|
||||
session().data().histories().readInboxTill(readTill);
|
||||
block = _history->blocks[iBlock].get();
|
||||
}
|
||||
view = block->messages[iItem].get();
|
||||
}
|
||||
|
||||
if (!readMentions.empty() && _widget->doWeReadMentions()) {
|
||||
session().api().markContentsRead(readMentions);
|
||||
}
|
||||
|
||||
if (mtop >= 0 || htop >= 0) {
|
||||
enumerateUserpics([&](not_null<Element*> view, int userpicTop) {
|
||||
// stop the enumeration if the userpic is below the painted rect
|
||||
if (userpicTop >= clip.top() + clip.height()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// paint the userpic if it intersects the painted rect
|
||||
if (userpicTop + st::msgPhotoSize > clip.top()) {
|
||||
if (const auto from = view->data()->displayFrom()) {
|
||||
from->paintUserpicLeft(
|
||||
p,
|
||||
_userpics[from],
|
||||
st::historyPhotoLeft,
|
||||
userpicTop,
|
||||
width(),
|
||||
st::msgPhotoSize);
|
||||
} else if (const auto info = view->data()->hiddenSenderInfo()) {
|
||||
info->userpic.paint(
|
||||
p,
|
||||
st::historyPhotoLeft,
|
||||
userpicTop,
|
||||
width(),
|
||||
st::msgPhotoSize);
|
||||
} else {
|
||||
Unexpected("Corrupt forwarded information in message.");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
int dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top();
|
||||
//QDate lastDate;
|
||||
//if (!_history->isEmpty()) {
|
||||
// lastDate = _history->blocks.back()->messages.back()->data()->date.date();
|
||||
//}
|
||||
|
||||
//// if item top is before this value always show date as a floating date
|
||||
//int showFloatingBefore = height() - 2 * (_visibleAreaBottom - _visibleAreaTop) - dateHeight;
|
||||
|
||||
|
||||
auto scrollDateOpacity = _scrollDateOpacity.value(_scrollDateShown ? 1. : 0.);
|
||||
enumerateDates([&](not_null<Element*> view, int itemtop, int dateTop) {
|
||||
// stop the enumeration if the date is above the painted rect
|
||||
if (dateTop + dateHeight <= clip.top()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto displayDate = view->displayDate();
|
||||
auto dateInPlace = displayDate;
|
||||
if (dateInPlace) {
|
||||
const auto correctDateTop = itemtop + st::msgServiceMargin.top();
|
||||
dateInPlace = (dateTop < correctDateTop + dateHeight);
|
||||
}
|
||||
//bool noFloatingDate = (item->date.date() == lastDate && displayDate);
|
||||
//if (noFloatingDate) {
|
||||
// if (itemtop < showFloatingBefore) {
|
||||
// noFloatingDate = false;
|
||||
// }
|
||||
//}
|
||||
|
||||
// paint the date if it intersects the painted rect
|
||||
if (dateTop < clip.top() + clip.height()) {
|
||||
auto opacity = (dateInPlace/* || noFloatingDate*/) ? 1. : scrollDateOpacity;
|
||||
if (opacity > 0.) {
|
||||
p.setOpacity(opacity);
|
||||
const auto dateY = false // noFloatingDate
|
||||
? itemtop
|
||||
: (dateTop - st::msgServiceMargin.top());
|
||||
if (const auto date = view->Get<HistoryView::DateBadge>()) {
|
||||
date->paint(p, context.st, dateY, _contentWidth, _isChatWide);
|
||||
} else {
|
||||
HistoryView::ServiceMessagePainter::PaintDate(
|
||||
p,
|
||||
context.st,
|
||||
view->dateTime(),
|
||||
dateY,
|
||||
_contentWidth,
|
||||
_isChatWide);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
p.setOpacity(1.);
|
||||
|
||||
_reactionsManager->paint(p, context);
|
||||
|
||||
p.translate(0, _historyPaddingTop);
|
||||
_emojiInteractions->paint(p);
|
||||
}
|
||||
context.translate(0, top);
|
||||
p.translate(0, -top);
|
||||
}
|
||||
|
||||
enumerateUserpics([&](not_null<Element*> view, int userpicTop) {
|
||||
// stop the enumeration if the userpic is below the painted rect
|
||||
if (userpicTop >= clip.top() + clip.height()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// paint the userpic if it intersects the painted rect
|
||||
if (userpicTop + st::msgPhotoSize > clip.top()) {
|
||||
if (const auto from = view->data()->displayFrom()) {
|
||||
from->paintUserpicLeft(
|
||||
p,
|
||||
_userpics[from],
|
||||
st::historyPhotoLeft,
|
||||
userpicTop,
|
||||
width(),
|
||||
st::msgPhotoSize);
|
||||
} else if (const auto info = view->data()->hiddenSenderInfo()) {
|
||||
info->userpic.paint(
|
||||
p,
|
||||
st::historyPhotoLeft,
|
||||
userpicTop,
|
||||
width(),
|
||||
st::msgPhotoSize);
|
||||
} else {
|
||||
Unexpected("Corrupt forwarded information in message.");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
const auto dateHeight = st::msgServicePadding.bottom()
|
||||
+ st::msgServiceFont->height
|
||||
+ st::msgServicePadding.top();
|
||||
//QDate lastDate;
|
||||
//if (!_history->isEmpty()) {
|
||||
// lastDate = _history->blocks.back()->messages.back()->data()->date.date();
|
||||
//}
|
||||
|
||||
//// if item top is before this value always show date as a floating date
|
||||
//int showFloatingBefore = height() - 2 * (_visibleAreaBottom - _visibleAreaTop) - dateHeight;
|
||||
|
||||
auto scrollDateOpacity = _scrollDateOpacity.value(_scrollDateShown ? 1. : 0.);
|
||||
enumerateDates([&](not_null<Element*> view, int itemtop, int dateTop) {
|
||||
// stop the enumeration if the date is above the painted rect
|
||||
if (dateTop + dateHeight <= clip.top()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto displayDate = view->displayDate();
|
||||
auto dateInPlace = displayDate;
|
||||
if (dateInPlace) {
|
||||
const auto correctDateTop = itemtop + st::msgServiceMargin.top();
|
||||
dateInPlace = (dateTop < correctDateTop + dateHeight);
|
||||
}
|
||||
//bool noFloatingDate = (item->date.date() == lastDate && displayDate);
|
||||
//if (noFloatingDate) {
|
||||
// if (itemtop < showFloatingBefore) {
|
||||
// noFloatingDate = false;
|
||||
// }
|
||||
//}
|
||||
|
||||
// paint the date if it intersects the painted rect
|
||||
if (dateTop < clip.top() + clip.height()) {
|
||||
auto opacity = (dateInPlace/* || noFloatingDate*/) ? 1. : scrollDateOpacity;
|
||||
if (opacity > 0.) {
|
||||
p.setOpacity(opacity);
|
||||
const auto dateY = false // noFloatingDate
|
||||
? itemtop
|
||||
: (dateTop - st::msgServiceMargin.top());
|
||||
if (const auto date = view->Get<HistoryView::DateBadge>()) {
|
||||
date->paint(p, context.st, dateY, _contentWidth, _isChatWide);
|
||||
} else {
|
||||
HistoryView::ServiceMessagePainter::PaintDate(
|
||||
p,
|
||||
context.st,
|
||||
view->dateTime(),
|
||||
dateY,
|
||||
_contentWidth,
|
||||
_isChatWide);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
p.setOpacity(1.);
|
||||
|
||||
_reactionsManager->paint(p, context);
|
||||
|
||||
p.translate(0, _historyPaddingTop);
|
||||
_emojiInteractions->paint(p);
|
||||
}
|
||||
|
||||
bool HistoryInner::eventHook(QEvent *e) {
|
||||
|
Reference in New Issue
Block a user