2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-08-31 06:26:18 +00:00

Support pinned locally in filters.

This commit is contained in:
John Preston
2020-03-17 17:04:30 +04:00
parent 483d4e5a4e
commit e27a8fe058
30 changed files with 409 additions and 155 deletions

View File

@@ -55,19 +55,34 @@ Main::Session &Entry::session() const {
return _owner->session();
}
void Entry::cachePinnedIndex(int index) {
if (_pinnedIndex != index) {
const auto wasPinned = isPinnedDialog();
_pinnedIndex = index;
if (session().supportMode()) {
// Force reorder in support mode.
_sortKeyInChatList = 0;
}
updateChatListSortPosition();
updateChatListEntry();
if (wasPinned != isPinnedDialog()) {
changedChatListPinHook();
void Entry::pinnedIndexChanged(int was, int now) {
if (session().supportMode()) {
// Force reorder in support mode.
_sortKeyInChatList = 0;
}
updateChatListSortPosition();
updateChatListEntry();
if ((was != 0) != (now != 0)) {
changedChatListPinHook();
}
}
void Entry::cachePinnedIndex(FilterId filterId, int index) {
const auto i = _pinnedIndex.find(filterId);
const auto was = (i != end(_pinnedIndex)) ? i->second : 0;
if (index == was) {
return;
}
if (!index) {
_pinnedIndex.erase(i);
pinnedIndexChanged(was, index);
} else {
if (!was) {
_pinnedIndex.emplace(filterId, index);
} else {
i->second = index;
}
pinnedIndexChanged(was, index);
}
}
@@ -97,9 +112,7 @@ void Entry::updateChatListSortPosition() {
const auto fixedIndex = fixedOnTopIndex();
_sortKeyInChatList = fixedIndex
? FixedOnTopDialogPos(fixedIndex)
: isPinnedDialog()
? PinnedDialogPos(_pinnedIndex)
: _sortKeyByDate;
: computeSortPosition(0);
if (needUpdateInChatList()) {
setChatListExistence(true);
} else {
@@ -107,6 +120,23 @@ void Entry::updateChatListSortPosition() {
}
}
int Entry::lookupPinnedIndex(FilterId filterId) const {
if (filterId) {
const auto i = _pinnedIndex.find(filterId);
return (i != end(_pinnedIndex)) ? i->second : 0;
} else if (!_pinnedIndex.empty()) {
return _pinnedIndex.front().first
? 0
: _pinnedIndex.front().second;
}
return 0;
}
uint64 Entry::computeSortPosition(FilterId filterId) const {
const auto index = lookupPinnedIndex(filterId);
return index ? PinnedDialogPos(index) : _sortKeyByDate;
}
void Entry::updateChatListExistence() {
setChatListExistence(shouldBeInChatList());
}
@@ -205,6 +235,9 @@ void Entry::removeFromChatList(
return;
}
_chatListLinks.erase(i);
if (isPinnedDialog(filterId)) {
owner().setChatPinned(_key, filterId, false);
}
list->removeEntry(_key);
}

View File

@@ -32,10 +32,9 @@ struct RowsByLetter {
};
enum class SortMode {
Complex = 0x00,
Date = 0x01,
Name = 0x02,
Add = 0x04,
Date = 0x00,
Name = 0x01,
Add = 0x02,
};
struct PositionChange {
@@ -117,19 +116,18 @@ public:
QChar letter,
not_null<Row*> row);
void updateChatListEntry() const;
bool isPinnedDialog() const {
return _pinnedIndex > 0;
[[nodiscard]] bool isPinnedDialog(FilterId filterId) const {
return lookupPinnedIndex(filterId) != 0;
}
void cachePinnedIndex(int index);
void cachePinnedIndex(FilterId filterId, int index);
bool isProxyPromoted() const {
return _isProxyPromoted;
}
void cacheProxyPromoted(bool promoted);
uint64 sortKeyInChatList() const {
return _sortKeyInChatList;
}
uint64 sortKeyByDate() const {
return _sortKeyByDate;
[[nodiscard]] uint64 sortKeyInChatList(FilterId filterId) const {
return filterId
? computeSortPosition(filterId)
: _sortKeyInChatList;
}
void updateChatListSortPosition();
void setChatListTimeId(TimeId date);
@@ -194,8 +192,12 @@ protected:
});
}
[[nodiscard]] int lookupPinnedIndex(FilterId filterId) const;
private:
virtual void changedChatListPinHook();
void pinnedIndexChanged(int was, int now);
[[nodiscard]] uint64 computeSortPosition(FilterId filterId) const;
void setChatListExistence(bool exists);
RowsByLetter *chatListLinks(FilterId filterId);
@@ -208,7 +210,7 @@ private:
base::flat_map<FilterId, RowsByLetter> _chatListLinks;
uint64 _sortKeyInChatList = 0;
uint64 _sortKeyByDate = 0;
int _pinnedIndex = 0;
base::flat_map<FilterId, int> _pinnedIndex;
bool _isProxyPromoted = false;
TimeId _timeId = 0;

View File

@@ -13,10 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Dialogs {
IndexedList::IndexedList(SortMode sortMode)
IndexedList::IndexedList(SortMode sortMode, FilterId filterId)
: _sortMode(sortMode)
, _list(sortMode)
, _empty(sortMode) {
, _filterId(filterId)
, _list(sortMode, filterId)
, _empty(sortMode, filterId) {
}
RowsByLetter IndexedList::addToEnd(Key key) {
@@ -28,7 +29,7 @@ RowsByLetter IndexedList::addToEnd(Key key) {
for (const auto ch : key.entry()->chatListFirstLetters()) {
auto j = _index.find(ch);
if (j == _index.cend()) {
j = _index.emplace(ch, _sortMode).first;
j = _index.emplace(ch, _sortMode, _filterId).first;
}
result.letters.emplace(ch, j->second.addToEnd(key));
}
@@ -44,7 +45,7 @@ Row *IndexedList::addByName(Key key) {
for (const auto ch : key.entry()->chatListFirstLetters()) {
auto j = _index.find(ch);
if (j == _index.cend()) {
j = _index.emplace(ch, _sortMode).first;
j = _index.emplace(ch, _sortMode, _filterId).first;
}
j->second.addByName(key);
}
@@ -79,7 +80,8 @@ void IndexedList::movePinned(Row *row, int deltaSign) {
Assert(swapPinnedIndexWith != cbegin());
--swapPinnedIndexWith;
}
Auth().data().reorderTwoPinnedChats(
row->key().entry()->owner().reorderTwoPinnedChats(
_filterId,
row->key(),
(*swapPinnedIndexWith)->key());
}
@@ -87,7 +89,7 @@ void IndexedList::movePinned(Row *row, int deltaSign) {
void IndexedList::peerNameChanged(
not_null<PeerData*> peer,
const base::flat_set<QChar> &oldLetters) {
Expects(_sortMode != SortMode::Date && _sortMode != SortMode::Complex);
Expects(_sortMode != SortMode::Date);
if (const auto history = peer->owner().historyLoaded(peer)) {
if (_sortMode == SortMode::Name) {
@@ -102,7 +104,7 @@ void IndexedList::peerNameChanged(
FilterId filterId,
not_null<PeerData*> peer,
const base::flat_set<QChar> &oldLetters) {
Expects(_sortMode == SortMode::Date || _sortMode == SortMode::Complex);
Expects(_sortMode == SortMode::Date);
if (const auto history = peer->owner().historyLoaded(peer)) {
adjustNames(filterId, history, oldLetters);
@@ -139,7 +141,7 @@ void IndexedList::adjustByName(
for (auto ch : toAdd) {
auto j = _index.find(ch);
if (j == _index.cend()) {
j = _index.emplace(ch, _sortMode).first;
j = _index.emplace(ch, _sortMode, _filterId).first;
}
j->second.addByName(key);
}
@@ -165,7 +167,7 @@ void IndexedList::adjustNames(
}
}
for (auto ch : toRemove) {
if (_sortMode == SortMode::Date || _sortMode == SortMode::Complex) {
if (_sortMode == SortMode::Date) {
history->removeChatListEntryByLetter(filterId, ch);
}
if (auto it = _index.find(ch); it != _index.cend()) {
@@ -175,10 +177,10 @@ void IndexedList::adjustNames(
for (auto ch : toAdd) {
auto j = _index.find(ch);
if (j == _index.cend()) {
j = _index.emplace(ch, _sortMode).first;
j = _index.emplace(ch, _sortMode, _filterId).first;
}
auto row = j->second.addToEnd(key);
if (_sortMode == SortMode::Date || _sortMode == SortMode::Complex) {
if (_sortMode == SortMode::Date) {
history->addChatListEntryByLetter(filterId, ch, row);
}
}

View File

@@ -16,7 +16,7 @@ namespace Dialogs {
class IndexedList {
public:
IndexedList(SortMode sortMode);
IndexedList(SortMode sortMode, FilterId filterId = 0);
RowsByLetter addToEnd(Key key);
Row *addByName(Key key);
@@ -81,6 +81,7 @@ private:
const base::flat_set<QChar> &oldChars);
SortMode _sortMode = SortMode();
FilterId _filterId = 0;
List _list, _empty;
base::flat_map<QChar, List> _index;

View File

@@ -66,12 +66,14 @@ int FixedOnTopDialogsCount(not_null<Dialogs::IndexedList*> list) {
return result;
}
int PinnedDialogsCount(not_null<Dialogs::IndexedList*> list) {
int PinnedDialogsCount(
FilterId filterId,
not_null<Dialogs::IndexedList*> list) {
auto result = 0;
for (const auto row : *list) {
if (row->entry()->fixedOnTopIndex()) {
continue;
} else if (!row->entry()->isPinnedDialog()) {
} else if (!row->entry()->isPinnedDialog(filterId)) {
break;
}
++result;
@@ -1075,7 +1077,7 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
});
}
if (anim::Disabled()
&& (!_pressed || !_pressed->entry()->isPinnedDialog())) {
&& (!_pressed || !_pressed->entry()->isPinnedDialog(_filterId))) {
mousePressReleased(e->globalPos(), e->button());
}
}
@@ -1091,7 +1093,9 @@ void InnerWidget::checkReorderPinnedStart(QPoint localPosition) {
if (updateReorderIndexGetCount() < 2) {
_dragging = nullptr;
} else {
const auto &order = session().data().pinnedChatsOrder(_openedFolder);
const auto &order = session().data().pinnedChatsOrder(
_openedFolder,
_filterId);
_pinnedOnDragStart = base::flat_set<Key>{
order.begin(),
order.end()
@@ -1103,14 +1107,14 @@ void InnerWidget::checkReorderPinnedStart(QPoint localPosition) {
}
int InnerWidget::countPinnedIndex(Row *ofRow) {
if (!ofRow || !ofRow->entry()->isPinnedDialog()) {
if (!ofRow || !ofRow->entry()->isPinnedDialog(_filterId)) {
return -1;
}
auto result = 0;
for (const auto row : *shownDialogs()) {
if (row->entry()->fixedOnTopIndex()) {
continue;
} else if (!row->entry()->isPinnedDialog()) {
} else if (!row->entry()->isPinnedDialog(_filterId)) {
break;
} else if (row == ofRow) {
return result;
@@ -1121,7 +1125,9 @@ int InnerWidget::countPinnedIndex(Row *ofRow) {
}
void InnerWidget::savePinnedOrder() {
const auto &newOrder = session().data().pinnedChatsOrder(_openedFolder);
const auto &newOrder = session().data().pinnedChatsOrder(
_openedFolder,
_filterId);
if (newOrder.size() != _pinnedOnDragStart.size()) {
return; // Something has changed in the set of pinned chats.
}
@@ -1130,7 +1136,11 @@ void InnerWidget::savePinnedOrder() {
return; // Something has changed in the set of pinned chats.
}
}
session().api().savePinnedOrder(_openedFolder);
if (_filterId) {
// #TODO pinned reorder data and to server
} else {
session().api().savePinnedOrder(_openedFolder);
}
}
void InnerWidget::finishReorderPinned() {
@@ -1162,7 +1172,7 @@ int InnerWidget::updateReorderIndexGetCount() {
return 0;
}
const auto count = Dialogs::PinnedDialogsCount(shownDialogs());
const auto count = Dialogs::PinnedDialogsCount(_filterId, shownDialogs());
Assert(index < count);
if (count < 2) {
stopReorderPinned();
@@ -1789,6 +1799,7 @@ void InnerWidget::contextMenuEvent(QContextMenuEvent *e) {
Window::FillPeerMenu(
_controller,
history->peer,
_filterId,
[&](const QString &text, Fn<void()> callback) {
return _menu->addAction(text, std::move(callback));
},
@@ -2541,6 +2552,7 @@ void InnerWidget::switchToFilter(FilterId filterId) {
&Data::ChatFilter::id)) {
filterId = 0;
}
stopReorderPinned();
_filterId = filterId;
refreshWithCollapsedRows(true);
_collapsedSelected = 0;
@@ -2983,7 +2995,7 @@ void InnerWidget::setupShortcuts() {
for (const auto [command, index] : pinned) {
request->check(command) && request->handle([=, index = index] {
const auto list = session().data().chatsList()->indexed();
const auto count = Dialogs::PinnedDialogsCount(list);
const auto count = Dialogs::PinnedDialogsCount(_filterId, list);
if (index >= count) {
return false;
}

View File

@@ -218,6 +218,7 @@ void paintRow(
not_null<const BasicRow*> row,
not_null<Entry*> entry,
Dialogs::Key chat,
FilterId filterId,
PeerData *from,
const HiddenSenderInfo *hiddenSenderInfo,
HistoryItem *item,
@@ -322,7 +323,7 @@ void paintRow(
}
auto availableWidth = namewidth;
if (entry->isPinnedDialog() && !entry->fixedOnTopIndex()) {
if (entry->isPinnedDialog(filterId) && (filterId || !entry->fixedOnTopIndex())) {
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
availableWidth -= icon.width() + st::dialogsUnreadPadding;
@@ -349,7 +350,7 @@ void paintRow(
}
} else if (!item) {
auto availableWidth = namewidth;
if (entry->isPinnedDialog() && !entry->fixedOnTopIndex()) {
if (entry->isPinnedDialog(filterId) && (filterId || !entry->fixedOnTopIndex())) {
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
availableWidth -= icon.width() + st::dialogsUnreadPadding;
@@ -366,7 +367,7 @@ void paintRow(
}
paintItemCallback(nameleft, namewidth);
} else if (entry->isPinnedDialog() && !entry->fixedOnTopIndex()) {
} else if (entry->isPinnedDialog(filterId) && (filterId || !entry->fixedOnTopIndex())) {
auto availableWidth = namewidth;
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
@@ -613,7 +614,7 @@ void paintUnreadCount(
void RowPainter::paint(
Painter &p,
not_null<const Row*> row,
int filterId,
FilterId filterId,
int fullWidth,
bool active,
bool selected,
@@ -669,9 +670,8 @@ void RowPainter::paint(
const auto displayPinnedIcon = !displayUnreadCounter
&& !displayMentionBadge
&& !displayUnreadMark
&& !filterId
&& entry->isPinnedDialog()
&& !entry->fixedOnTopIndex();
&& entry->isPinnedDialog(filterId)
&& (filterId || !entry->fixedOnTopIndex());
const auto from = history
? (history->peer->migrateTo()
@@ -749,6 +749,7 @@ void RowPainter::paint(
row,
entry,
row->key(),
filterId,
from,
nullptr,
item,
@@ -872,6 +873,7 @@ void RowPainter::paint(
row,
history,
history,
FilterId(),
from,
hiddenSenderInfo,
item,

View File

@@ -29,7 +29,7 @@ public:
static void paint(
Painter &p,
not_null<const Row*> row,
int filterId,
FilterId filterId,
int fullWidth,
bool active,
bool selected,

View File

@@ -14,7 +14,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Dialogs {
List::List(SortMode sortMode) : _sortMode(sortMode) {
List::List(SortMode sortMode, FilterId filterId)
: _sortMode(sortMode)
, _filterId(filterId) {
}
List::const_iterator List::cfind(Row *value) const {
@@ -32,7 +34,7 @@ not_null<Row*> List::addToEnd(Key key) {
std::make_unique<Row>(key, _rows.size())
).first->second.get();
_rows.emplace_back(result);
if (_sortMode == SortMode::Date || _sortMode == SortMode::Complex) {
if (_sortMode == SortMode::Date) {
adjustByDate(result);
}
return result;
@@ -82,20 +84,20 @@ void List::adjustByName(not_null<Row*> row) {
}
void List::adjustByDate(not_null<Row*> row) {
Expects(_sortMode == SortMode::Date || _sortMode == SortMode::Complex);
Expects(_sortMode == SortMode::Date);
const auto key = row->sortKey(_sortMode);
const auto key = row->sortKey(_filterId);
const auto index = row->pos();
const auto i = _rows.begin() + index;
const auto before = std::find_if(i + 1, _rows.end(), [&](Row *row) {
return (row->sortKey(_sortMode) <= key);
return (row->sortKey(_filterId) <= key);
});
if (before != i + 1) {
rotate(i, i + 1, before);
} else {
const auto from = std::make_reverse_iterator(i);
const auto after = std::find_if(from, _rows.rend(), [&](Row *row) {
return (row->sortKey(_sortMode) >= key);
return (row->sortKey(_filterId) >= key);
}).base();
if (after != i) {
rotate(after, i, i + 1);

View File

@@ -16,7 +16,7 @@ enum class SortMode;
class List final {
public:
List(SortMode sortMode);
List(SortMode sortMode, FilterId filterId = 0);
List(const List &other) = delete;
List &operator=(const List &other) = delete;
List(List &&other) = default;
@@ -78,6 +78,7 @@ private:
std::vector<not_null<Row*>>::iterator last);
SortMode _sortMode = SortMode();
FilterId _filterId = 0;
std::vector<not_null<Row*>> _rows;
std::map<Key, std::unique_ptr<Row>> _rowByKey;

View File

@@ -14,8 +14,8 @@ namespace Dialogs {
MainList::MainList(FilterId filterId, rpl::producer<int> pinnedLimit)
: _filterId(filterId)
, _all(filterId ? SortMode::Date : SortMode::Complex)
, _pinned(1) {
, _all(SortMode::Date, filterId)
, _pinned(filterId, 1) {
_unreadState.known = true;
std::move(

View File

@@ -9,11 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "dialogs/dialogs_key.h"
#include "dialogs/dialogs_entry.h"
#include "history/history.h"
#include "data/data_session.h"
namespace Dialogs {
PinnedList::PinnedList(int limit) : _limit(limit) {
PinnedList::PinnedList(FilterId filterId, int limit)
: _filterId(filterId)
, _limit(limit) {
Expects(limit > 0);
}
@@ -41,7 +44,7 @@ int PinnedList::addPinnedGetPosition(const Key &key) {
applyLimit(_limit - 1);
const auto position = int(_data.size());
_data.push_back(key);
key.entry()->cachePinnedIndex(position + 1);
key.entry()->cachePinnedIndex(_filterId, position + 1);
return position;
}
@@ -54,19 +57,34 @@ void PinnedList::setPinned(const Key &key, bool pinned) {
const auto begin = _data.begin();
std::rotate(begin, begin + position, begin + position + 1);
for (auto i = 0; i != position + 1; ++i) {
_data[i].entry()->cachePinnedIndex(i + 1);
_data[i].entry()->cachePinnedIndex(_filterId, i + 1);
}
}
} else if (const auto it = ranges::find(_data, key); it != end(_data)) {
const auto index = int(it - begin(_data));
_data.erase(it);
key.entry()->cachePinnedIndex(0);
key.entry()->cachePinnedIndex(_filterId, 0);
for (auto i = index, count = int(size(_data)); i != count; ++i) {
_data[i].entry()->cachePinnedIndex(i + 1);
_data[i].entry()->cachePinnedIndex(_filterId, i + 1);
}
}
}
void PinnedList::applyFilterPinned(
FilterId filterId,
not_null<History*> history,
int index) {
Expects(index > 0);
history->cachePinnedIndex(filterId, index);
const auto key = Key{ history };
if (ranges::find(_data, key) == end(_data)) {
_data.push_back(key);
// #TODO pinned
}
}
void PinnedList::applyLimit(int limit) {
Expects(limit >= 0);
@@ -83,18 +101,32 @@ void PinnedList::applyList(
not_null<Data::Session*> owner,
const QVector<MTPDialogPeer> &list) {
clear();
for (const auto &peer : ranges::view::reverse(list)) {
for (const auto &peer : list) {
peer.match([&](const MTPDdialogPeer &data) {
if (const auto peerId = peerFromMTP(data.vpeer())) {
setPinned(owner->history(peerId), true);
addPinned(owner->history(peerId));
}
}, [&](const MTPDdialogPeerFolder &data) {
const auto folderId = data.vfolder_id().v;
setPinned(owner->folder(folderId), true);
addPinned(owner->folder(data.vfolder_id().v));
});
}
}
void PinnedList::applyList(const std::vector<not_null<History*>> &list) {
Expects(_filterId != 0);
clear();
const auto count = int(list.size());
_data.reserve(count);
for (auto i = 0; i != count; ++i) {
const auto history = list[i];
if (history->inChatList()) {
_data.emplace_back(history);
history->cachePinnedIndex(_filterId, i + 1);
}
}
}
void PinnedList::reorder(const Key &key1, const Key &key2) {
const auto index1 = ranges::find(_data, key1) - begin(_data);
const auto index2 = ranges::find(_data, key2) - begin(_data);
@@ -102,8 +134,8 @@ void PinnedList::reorder(const Key &key1, const Key &key2) {
Assert(index2 >= 0 && index2 < _data.size());
Assert(index1 != index2);
std::swap(_data[index1], _data[index2]);
key1.entry()->cachePinnedIndex(index2 + 1);
key2.entry()->cachePinnedIndex(index1 + 1);
key1.entry()->cachePinnedIndex(_filterId, index2 + 1);
key2.entry()->cachePinnedIndex(_filterId, index1 + 1);
}
} // namespace Dialogs

View File

@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class History;
namespace Data {
class Session;
} // namespace Data
@@ -17,7 +19,7 @@ class Key;
class PinnedList final {
public:
explicit PinnedList(int limit);
PinnedList(FilterId filterId, int limit);
void setLimit(int limit);
@@ -28,11 +30,17 @@ public:
// if (pinned) places on the first place in the list.
void setPinned(const Key &key, bool pinned);
void applyFilterPinned(
FilterId filterId,
not_null<History*> history,
int index);
void clear();
void applyList(
not_null<Data::Session*> owner,
const QVector<MTPDialogPeer> &list);
void applyList(const std::vector<not_null<History*>> &list);
void reorder(const Key &key1, const Key &key2);
const std::vector<Key> &order() const {
@@ -43,6 +51,7 @@ private:
int addPinnedGetPosition(const Key &key);
void applyLimit(int limit);
FilterId _filterId = 0;
int _limit = 0;
std::vector<Key> _data;

View File

@@ -220,10 +220,8 @@ Row::Row(Key key, int pos) : _id(key), _pos(pos) {
}
}
uint64 Row::sortKey(SortMode mode) const {
return (mode == SortMode::Complex)
? _id.entry()->sortKeyInChatList()
: _id.entry()->sortKeyByDate();
uint64 Row::sortKey(FilterId filterId) const {
return _id.entry()->sortKeyInChatList(filterId);
}
void Row::validateListEntryCache() const {

View File

@@ -90,7 +90,7 @@ public:
int pos() const {
return _pos;
}
uint64 sortKey(SortMode mode) const;
uint64 sortKey(FilterId filterId) const;
void validateListEntryCache() const;
const Ui::Text::String &listEntryCache() const {