mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-19 07:37:11 +02:00
Show local sending messages in replies section.
This commit is contained in:
parent
beb623bee2
commit
78d83a2c69
11 changed files with 207 additions and 27 deletions
|
@ -20,7 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Data {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMessagesPerPage = 16;
|
||||
constexpr auto kMessagesPerPage = 50;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -45,6 +45,21 @@ rpl::producer<MessagesSlice> RepliesList::source(
|
|||
MessagePosition aroundId,
|
||||
int limitBefore,
|
||||
int limitAfter) {
|
||||
return rpl::combine(
|
||||
sourceFromServer(aroundId, limitBefore, limitAfter),
|
||||
_history->session().changes().historyFlagsValue(
|
||||
_history,
|
||||
Data::HistoryUpdate::Flag::LocalMessages)
|
||||
) | rpl::map([=](MessagesSlice &&server, const auto &) {
|
||||
appendLocalMessages(server);
|
||||
return std::move(server);
|
||||
});
|
||||
}
|
||||
|
||||
rpl::producer<MessagesSlice> RepliesList::sourceFromServer(
|
||||
MessagePosition aroundId,
|
||||
int limitBefore,
|
||||
int limitAfter) {
|
||||
const auto around = aroundId.fullId.msg;
|
||||
return [=](auto consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
|
@ -78,6 +93,65 @@ rpl::producer<MessagesSlice> RepliesList::source(
|
|||
};
|
||||
}
|
||||
|
||||
void RepliesList::appendLocalMessages(MessagesSlice &slice) {
|
||||
const auto &local = _history->localMessages();
|
||||
if (local.empty()) {
|
||||
return;
|
||||
} else if (slice.ids.empty()) {
|
||||
if (slice.skippedBefore != 0 || slice.skippedAfter != 0) {
|
||||
return;
|
||||
}
|
||||
slice.ids.reserve(local.size());
|
||||
for (const auto item : local) {
|
||||
if (item->replyToTop() != _rootId) {
|
||||
continue;
|
||||
}
|
||||
slice.ids.push_back(item->fullId());
|
||||
}
|
||||
ranges::sort(slice.ids);
|
||||
return;
|
||||
}
|
||||
auto &owner = _history->owner();
|
||||
auto dates = std::vector<TimeId>();
|
||||
dates.reserve(slice.ids.size());
|
||||
for (const auto &id : slice.ids) {
|
||||
const auto message = owner.message(id);
|
||||
Assert(message != nullptr);
|
||||
|
||||
dates.push_back(message->date());
|
||||
}
|
||||
for (const auto item : local) {
|
||||
if (item->replyToTop() != _rootId) {
|
||||
continue;
|
||||
}
|
||||
const auto date = item->date();
|
||||
if (date < dates.front()) {
|
||||
if (slice.skippedBefore != 0) {
|
||||
if (slice.skippedBefore) {
|
||||
++*slice.skippedBefore;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
dates.insert(dates.begin(), date);
|
||||
slice.ids.insert(slice.ids.begin(), item->fullId());
|
||||
} else {
|
||||
auto to = dates.size();
|
||||
for (; to != 0; --to) {
|
||||
const auto checkId = slice.ids[to - 1].msg;
|
||||
if (dates[to - 1] > date) {
|
||||
continue;
|
||||
} else if (dates[to - 1] < date
|
||||
|| IsServerMsgId(checkId)
|
||||
|| checkId < item->id) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
dates.insert(dates.begin() + to, date);
|
||||
slice.ids.insert(slice.ids.begin() + to, item->fullId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<int> RepliesList::fullCount() const {
|
||||
return _fullCount.value() | rpl::filter_optional();
|
||||
}
|
||||
|
|
|
@ -35,6 +35,12 @@ private:
|
|||
|
||||
[[nodiscard]] Histories &histories();
|
||||
|
||||
[[nodiscard]] rpl::producer<MessagesSlice> sourceFromServer(
|
||||
MessagePosition aroundId,
|
||||
int limitBefore,
|
||||
int limitAfter);
|
||||
void appendLocalMessages(MessagesSlice &slice);
|
||||
|
||||
[[nodiscard]] bool buildFromData(not_null<Viewer*> viewer);
|
||||
[[nodiscard]] bool applyUpdate(
|
||||
not_null<Viewer*> viewer,
|
||||
|
|
|
@ -1338,6 +1338,10 @@ void History::unregisterLocalMessage(not_null<HistoryItem*> item) {
|
|||
session().changes().historyUpdated(this, UpdateFlag::LocalMessages);
|
||||
}
|
||||
|
||||
const base::flat_set<not_null<HistoryItem*>> &History::localMessages() {
|
||||
return _localMessages;
|
||||
}
|
||||
|
||||
HistoryItem *History::latestSendingMessage() const {
|
||||
auto sending = ranges::view::all(
|
||||
_localMessages
|
||||
|
|
|
@ -77,6 +77,7 @@ public:
|
|||
void checkLocalMessages();
|
||||
void removeJoinedMessage();
|
||||
|
||||
|
||||
bool isEmpty() const;
|
||||
bool isDisplayedEmpty() const;
|
||||
Element *findFirstNonEmpty() const;
|
||||
|
@ -181,6 +182,8 @@ public:
|
|||
|
||||
void registerLocalMessage(not_null<HistoryItem*> item);
|
||||
void unregisterLocalMessage(not_null<HistoryItem*> item);
|
||||
[[nodiscard]] auto localMessages()
|
||||
-> const base::flat_set<not_null<HistoryItem*>> &;
|
||||
[[nodiscard]] HistoryItem *latestSendingMessage() const;
|
||||
|
||||
[[nodiscard]] bool readInboxTillNeedsRequest(MsgId tillId);
|
||||
|
|
|
@ -552,13 +552,36 @@ not_null<Element*> ListWidget::enforceViewForItem(
|
|||
}
|
||||
|
||||
void ListWidget::updateAroundPositionFromRows() {
|
||||
_aroundIndex = findNearestItem(_aroundPosition);
|
||||
if (_aroundIndex >= 0) {
|
||||
const auto newPosition = _items[_aroundIndex]->data()->position();
|
||||
if (_aroundPosition != newPosition) {
|
||||
_aroundPosition = newPosition;
|
||||
crl::on_main(this, [=] { refreshViewer(); });
|
||||
const auto nearestIndex = findNearestItem(_aroundPosition);
|
||||
if (nearestIndex < 0) {
|
||||
_aroundIndex = -1;
|
||||
return;
|
||||
}
|
||||
const auto isGoodIndex = [&](int index) {
|
||||
Expects(index >= 0 && index < _items.size());
|
||||
|
||||
return _delegate->listIsGoodForAroundPosition(_items[index]);
|
||||
};
|
||||
_aroundIndex = [&] {
|
||||
for (auto index = nearestIndex; index < _items.size(); ++index) {
|
||||
if (isGoodIndex(index)) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
for (auto index = nearestIndex; index != 0;) {
|
||||
if (isGoodIndex(--index)) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}();
|
||||
if (_aroundIndex < 0) {
|
||||
return;
|
||||
}
|
||||
const auto newPosition = _items[_aroundIndex]->data()->position();
|
||||
if (_aroundPosition != newPosition) {
|
||||
_aroundPosition = newPosition;
|
||||
crl::on_main(this, [=] { refreshViewer(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1055,8 +1078,8 @@ void ListWidget::checkMoveToOtherViewer() {
|
|||
return;
|
||||
}
|
||||
|
||||
auto topItem = findItemByY(_visibleTop);
|
||||
auto bottomItem = findItemByY(_visibleBottom);
|
||||
auto topItemIndex = findItemIndexByY(_visibleTop);
|
||||
auto bottomItemIndex = findItemIndexByY(_visibleBottom);
|
||||
auto preloadedHeight = kPreloadedScreensCountFull * visibleHeight;
|
||||
auto preloadedCount = preloadedHeight / _itemAverageHeight;
|
||||
auto preloadIdsLimitMin = (preloadedCount / 2) + 1;
|
||||
|
@ -1075,32 +1098,64 @@ void ListWidget::checkMoveToOtherViewer() {
|
|||
- kPreloadIfLessThanScreens;
|
||||
auto minUniversalIdDelta = (minScreenDelta * visibleHeight)
|
||||
/ _itemAverageHeight;
|
||||
auto preloadAroundMessage = [&](not_null<Element*> view) {
|
||||
const auto preloadAroundMessage = [&](int index) {
|
||||
Expects(index >= 0 && index < _items.size());
|
||||
|
||||
auto preloadRequired = false;
|
||||
auto itemPosition = view->data()->position();
|
||||
auto itemIndex = ranges::find(_items, view) - begin(_items);
|
||||
Assert(itemIndex < _items.size());
|
||||
auto itemPosition = _items[index]->data()->position();
|
||||
|
||||
if (!preloadRequired) {
|
||||
preloadRequired = (_idsLimit < preloadIdsLimitMin);
|
||||
}
|
||||
if (!preloadRequired) {
|
||||
Assert(_aroundIndex >= 0);
|
||||
auto delta = std::abs(itemIndex - _aroundIndex);
|
||||
auto delta = std::abs(index - _aroundIndex);
|
||||
preloadRequired = (delta >= minUniversalIdDelta);
|
||||
}
|
||||
if (preloadRequired) {
|
||||
_idsLimit = preloadIdsLimit;
|
||||
_aroundPosition = itemPosition;
|
||||
_aroundIndex = itemIndex;
|
||||
_aroundIndex = index;
|
||||
refreshViewer();
|
||||
}
|
||||
};
|
||||
|
||||
const auto findGoodAbove = [&](int index) {
|
||||
Expects(index >= 0 && index < _items.size());
|
||||
|
||||
for (; index != _items.size(); ++index) {
|
||||
if (_delegate->listIsGoodForAroundPosition(_items[index])) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
const auto findGoodBelow = [&](int index) {
|
||||
Expects(index >= 0 && index < _items.size());
|
||||
|
||||
for (++index; index != 0;) {
|
||||
if (_delegate->listIsGoodForAroundPosition(_items[--index])) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
if (preloadTop && !topLoaded) {
|
||||
preloadAroundMessage(topItem);
|
||||
const auto goodAboveIndex = findGoodAbove(topItemIndex);
|
||||
const auto goodIndex = (goodAboveIndex >= 0)
|
||||
? goodAboveIndex
|
||||
: findGoodBelow(topItemIndex);
|
||||
if (goodIndex >= 0) {
|
||||
preloadAroundMessage(goodIndex);
|
||||
}
|
||||
} else if (preloadBottom && !bottomLoaded) {
|
||||
preloadAroundMessage(bottomItem);
|
||||
const auto goodBelowIndex = findGoodBelow(bottomItemIndex);
|
||||
const auto goodIndex = (goodBelowIndex >= 0)
|
||||
? goodBelowIndex
|
||||
: findGoodAbove(bottomItemIndex);
|
||||
if (goodIndex >= 0) {
|
||||
preloadAroundMessage(goodIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1553,20 +1608,24 @@ MessageIdsList ListWidget::getSelectedItems() const {
|
|||
return collectSelectedIds();
|
||||
}
|
||||
|
||||
not_null<Element*> ListWidget::findItemByY(int y) const {
|
||||
int ListWidget::findItemIndexByY(int y) const {
|
||||
Expects(!_items.empty());
|
||||
|
||||
if (y < _itemsTop) {
|
||||
return _items.front();
|
||||
return 0;
|
||||
}
|
||||
auto i = std::lower_bound(
|
||||
begin(_items),
|
||||
end(_items),
|
||||
y,
|
||||
[this](auto &elem, int top) {
|
||||
return this->itemTop(elem) + elem->height() <= top;
|
||||
});
|
||||
return (i != end(_items)) ? i->get() : _items.back().get();
|
||||
return this->itemTop(elem) + elem->height() <= top;
|
||||
});
|
||||
return std::min(int(i - begin(_items)), int(_items.size() - 1));
|
||||
}
|
||||
|
||||
not_null<Element*> ListWidget::findItemByY(int y) const {
|
||||
return _items[findItemIndexByY(y)];
|
||||
}
|
||||
|
||||
Element *ListWidget::strictFindItemByY(int y) const {
|
||||
|
|
|
@ -75,6 +75,8 @@ public:
|
|||
virtual void listContentRefreshed() = 0;
|
||||
virtual ClickHandlerPtr listDateLink(not_null<Element*> view) = 0;
|
||||
virtual bool listElementHideReply(not_null<const Element*> view) = 0;
|
||||
virtual bool listIsGoodForAroundPosition(
|
||||
not_null<const Element*> view) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
@ -322,11 +324,12 @@ private:
|
|||
|
||||
void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false);
|
||||
|
||||
not_null<Element*> findItemByY(int y) const;
|
||||
Element *strictFindItemByY(int y) const;
|
||||
int findNearestItem(Data::MessagePosition position) const;
|
||||
[[nodiscard]] int findItemIndexByY(int y) const;
|
||||
[[nodiscard]] not_null<Element*> findItemByY(int y) const;
|
||||
[[nodiscard]] Element *strictFindItemByY(int y) const;
|
||||
[[nodiscard]] int findNearestItem(Data::MessagePosition position) const;
|
||||
void viewReplaced(not_null<const Element*> was, Element *now);
|
||||
HistoryItemsList collectVisibleItems() const;
|
||||
[[nodiscard]] HistoryItemsList collectVisibleItems() const;
|
||||
|
||||
void checkMoveToOtherViewer();
|
||||
void updateVisibleTopItem();
|
||||
|
|
|
@ -135,6 +135,10 @@ RepliesWidget::RepliesWidget(
|
|||
) | rpl::start_with_next([=] {
|
||||
confirmDeleteSelected();
|
||||
}, _topBar->lifetime());
|
||||
_topBar->forwardSelectionRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
confirmForwardSelected();
|
||||
}, _topBar->lifetime());
|
||||
_topBar->clearSelectionRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
clearSelected();
|
||||
|
@ -1380,6 +1384,11 @@ bool RepliesWidget::listElementHideReply(not_null<const Element*> view) {
|
|||
return (view->data()->replyToId() == _rootId);
|
||||
}
|
||||
|
||||
bool RepliesWidget::listIsGoodForAroundPosition(
|
||||
not_null<const Element*> view) {
|
||||
return IsServerMsgId(view->data()->id);
|
||||
}
|
||||
|
||||
void RepliesWidget::confirmSendNowSelected() {
|
||||
auto items = _inner->getSelectedItems();
|
||||
if (items.empty()) {
|
||||
|
@ -1409,6 +1418,19 @@ void RepliesWidget::confirmDeleteSelected() {
|
|||
});
|
||||
}
|
||||
|
||||
void RepliesWidget::confirmForwardSelected() {
|
||||
auto items = _inner->getSelectedItems();
|
||||
if (items.empty()) {
|
||||
return;
|
||||
}
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
Window::ShowForwardMessagesBox(controller(), std::move(items), [=] {
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->clearSelected();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void RepliesWidget::clearSelected() {
|
||||
_inner->cancelSelection();
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@ public:
|
|||
void listContentRefreshed() override;
|
||||
ClickHandlerPtr listDateLink(not_null<Element*> view) override;
|
||||
bool listElementHideReply(not_null<const Element*> view) override;
|
||||
bool listIsGoodForAroundPosition(not_null<const Element*> view) override;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
@ -158,6 +159,7 @@ private:
|
|||
|
||||
void confirmSendNowSelected();
|
||||
void confirmDeleteSelected();
|
||||
void confirmForwardSelected();
|
||||
void clearSelected();
|
||||
|
||||
void send();
|
||||
|
|
|
@ -1150,6 +1150,11 @@ bool ScheduledWidget::listElementHideReply(not_null<const Element*> view) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ScheduledWidget::listIsGoodForAroundPosition(
|
||||
not_null<const Element*> view) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScheduledWidget::confirmSendNowSelected() {
|
||||
auto items = _inner->getSelectedItems();
|
||||
if (items.empty()) {
|
||||
|
|
|
@ -111,6 +111,7 @@ public:
|
|||
void listContentRefreshed() override;
|
||||
ClickHandlerPtr listDateLink(not_null<Element*> view) override;
|
||||
bool listElementHideReply(not_null<const Element*> view) override;
|
||||
bool listIsGoodForAroundPosition(not_null<const Element *> view) override;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
|
|
@ -709,8 +709,9 @@ void MainWidget::cancelUploadLayer(not_null<HistoryItem*> item) {
|
|||
auto &data = session().data();
|
||||
if (const auto item = data.message(itemId)) {
|
||||
if (!item->isEditingMedia()) {
|
||||
const auto history = item->history();
|
||||
item->destroy();
|
||||
item->history()->requestChatListMessage();
|
||||
history->requestChatListMessage();
|
||||
} else {
|
||||
item->returnSavedMedia();
|
||||
session().uploader().cancel(item->fullId());
|
||||
|
|
Loading…
Add table
Reference in a new issue