2018-01-04 09:40:58 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
|
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
|
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
|
|
*/
|
2019-04-15 11:54:03 +00:00
|
|
|
#include "data/data_folder.h"
|
2018-01-04 09:40:58 +00:00
|
|
|
|
2018-01-23 16:51:12 +00:00
|
|
|
#include "data/data_session.h"
|
2019-01-04 11:09:48 +00:00
|
|
|
#include "data/data_channel.h"
|
2020-02-21 07:58:50 +00:00
|
|
|
#include "data/data_histories.h"
|
2020-06-12 12:12:34 +00:00
|
|
|
#include "data/data_changes.h"
|
2018-01-05 15:57:18 +00:00
|
|
|
#include "dialogs/dialogs_key.h"
|
2018-01-13 12:45:11 +00:00
|
|
|
#include "history/history.h"
|
2018-01-11 19:33:26 +00:00
|
|
|
#include "history/history_item.h"
|
2018-01-22 09:33:09 +00:00
|
|
|
#include "lang/lang_keys.h"
|
2018-01-23 16:51:12 +00:00
|
|
|
#include "storage/storage_facade.h"
|
2019-04-22 14:22:39 +00:00
|
|
|
#include "core/application.h"
|
2019-07-24 08:46:23 +00:00
|
|
|
#include "main/main_account.h"
|
2019-07-24 11:45:24 +00:00
|
|
|
#include "main/main_session.h"
|
2020-06-17 09:36:25 +00:00
|
|
|
#include "mtproto/mtproto_config.h"
|
2018-01-29 17:13:24 +00:00
|
|
|
#include "apiwrap.h"
|
|
|
|
#include "mainwidget.h"
|
2019-04-23 13:28:44 +00:00
|
|
|
#include "styles/style_dialogs.h"
|
2018-01-05 15:57:18 +00:00
|
|
|
|
2018-01-04 09:40:58 +00:00
|
|
|
namespace Data {
|
2019-04-17 12:08:02 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
constexpr auto kLoadedChatsMinCount = 20;
|
2019-05-08 08:50:39 +00:00
|
|
|
constexpr auto kShowChatNamesCount = 8;
|
2019-04-17 12:08:02 +00:00
|
|
|
|
|
|
|
} // namespace
|
2018-01-04 09:40:58 +00:00
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
Folder::Folder(not_null<Data::Session*> owner, FolderId id)
|
2020-06-12 14:09:04 +00:00
|
|
|
: Entry(owner, Type::Folder)
|
2018-01-22 09:33:09 +00:00
|
|
|
, _id(id)
|
2020-06-12 12:12:34 +00:00
|
|
|
, _chatsList(
|
|
|
|
&owner->session(),
|
|
|
|
FilterId(),
|
2020-06-17 09:36:25 +00:00
|
|
|
owner->session().serverConfig().pinnedDialogsInFolderMax.value())
|
2021-02-19 11:54:27 +00:00
|
|
|
, _name(tr::lng_archived_name(tr::now))
|
|
|
|
, _chatListNameSortKey(owner->nameSortKey(_name)) {
|
2018-01-22 09:33:09 +00:00
|
|
|
indexNameParts();
|
2019-04-23 13:28:44 +00:00
|
|
|
|
2020-06-12 12:12:34 +00:00
|
|
|
session().changes().peerUpdates(
|
|
|
|
PeerUpdate::Flag::Name
|
|
|
|
) | rpl::filter([=](const PeerUpdate &update) {
|
|
|
|
return ranges::contains(_lastHistories, update.peer, &History::peer);
|
|
|
|
}) | rpl::start_with_next([=] {
|
|
|
|
++_chatListViewVersion;
|
|
|
|
updateChatListEntryPostponed();
|
2019-04-23 13:28:44 +00:00
|
|
|
}, _lifetime);
|
2020-03-16 10:20:18 +00:00
|
|
|
|
|
|
|
_chatsList.setAllAreMuted(true);
|
|
|
|
|
|
|
|
_chatsList.unreadStateChanges(
|
|
|
|
) | rpl::filter([=] {
|
|
|
|
return inChatList();
|
|
|
|
}) | rpl::start_with_next([=](const Dialogs::UnreadState &old) {
|
|
|
|
++_chatListViewVersion;
|
|
|
|
notifyUnreadStateChange(old);
|
2020-03-25 13:28:59 +00:00
|
|
|
updateChatListEntryPostponed();
|
2020-03-16 10:20:18 +00:00
|
|
|
}, _lifetime);
|
|
|
|
|
|
|
|
_chatsList.fullSize().changes(
|
|
|
|
) | rpl::start_with_next([=] {
|
2020-03-25 13:28:59 +00:00
|
|
|
updateChatListEntryPostponed();
|
2020-03-16 10:20:18 +00:00
|
|
|
}, _lifetime);
|
2018-01-22 09:33:09 +00:00
|
|
|
}
|
|
|
|
|
2020-03-25 13:28:59 +00:00
|
|
|
void Folder::updateChatListEntryPostponed() {
|
|
|
|
if (_updateChatListEntryPostponed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_updateChatListEntryPostponed = true;
|
|
|
|
Ui::PostponeCall(this, [=] {
|
|
|
|
updateChatListEntry();
|
|
|
|
_updateChatListEntryPostponed = false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
FolderId Folder::id() const {
|
2018-01-23 16:51:12 +00:00
|
|
|
return _id;
|
|
|
|
}
|
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
void Folder::indexNameParts() {
|
2019-05-03 10:55:44 +00:00
|
|
|
// We don't want archive to be filtered in the chats list.
|
|
|
|
//_nameWords.clear();
|
|
|
|
//_nameFirstLetters.clear();
|
|
|
|
//auto toIndexList = QStringList();
|
|
|
|
//auto appendToIndex = [&](const QString &value) {
|
|
|
|
// if (!value.isEmpty()) {
|
|
|
|
// toIndexList.push_back(TextUtilities::RemoveAccents(value));
|
|
|
|
// }
|
|
|
|
//};
|
|
|
|
|
|
|
|
//appendToIndex(_name);
|
|
|
|
//const auto appendTranslit = !toIndexList.isEmpty()
|
|
|
|
// && cRussianLetters().match(toIndexList.front()).hasMatch();
|
|
|
|
//if (appendTranslit) {
|
|
|
|
// appendToIndex(translitRusEng(toIndexList.front()));
|
|
|
|
//}
|
|
|
|
//auto toIndex = toIndexList.join(' ');
|
|
|
|
//toIndex += ' ' + rusKeyboardLayoutSwitch(toIndex);
|
|
|
|
|
|
|
|
//const auto namesList = TextUtilities::PrepareSearchWords(toIndex);
|
|
|
|
//for (const auto &name : namesList) {
|
|
|
|
// _nameWords.insert(name);
|
|
|
|
// _nameFirstLetters.insert(name[0]);
|
|
|
|
//}
|
2018-01-04 09:40:58 +00:00
|
|
|
}
|
|
|
|
|
2019-04-16 08:50:59 +00:00
|
|
|
void Folder::registerOne(not_null<History*> history) {
|
2019-04-22 14:22:39 +00:00
|
|
|
if (_chatsList.indexed()->size() == 1) {
|
2019-04-18 11:31:30 +00:00
|
|
|
updateChatListSortPosition();
|
2020-03-16 10:20:18 +00:00
|
|
|
if (!_chatsList.cloudUnreadKnown()) {
|
2020-02-21 07:58:50 +00:00
|
|
|
owner().histories().requestDialogEntry(this);
|
2019-04-24 14:53:12 +00:00
|
|
|
}
|
2019-04-23 12:29:23 +00:00
|
|
|
} else {
|
|
|
|
updateChatListEntry();
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
2019-04-22 15:09:03 +00:00
|
|
|
applyChatListMessage(history->chatListMessage());
|
2019-05-08 08:50:39 +00:00
|
|
|
reorderLastHistories();
|
2018-01-04 09:40:58 +00:00
|
|
|
}
|
|
|
|
|
2019-04-16 08:50:59 +00:00
|
|
|
void Folder::unregisterOne(not_null<History*> history) {
|
2019-04-16 14:05:56 +00:00
|
|
|
if (_chatsList.empty()) {
|
2019-04-16 08:50:59 +00:00
|
|
|
updateChatListExistence();
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
2019-04-22 15:09:03 +00:00
|
|
|
if (_chatListMessage && _chatListMessage->history() == history) {
|
|
|
|
computeChatListMessage();
|
|
|
|
}
|
2019-05-08 08:50:39 +00:00
|
|
|
reorderLastHistories();
|
2019-04-22 15:09:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Folder::oneListMessageChanged(HistoryItem *from, HistoryItem *to) {
|
|
|
|
if (!applyChatListMessage(to) && _chatListMessage == from) {
|
|
|
|
computeChatListMessage();
|
|
|
|
}
|
2019-04-23 12:29:23 +00:00
|
|
|
if (from || to) {
|
2019-05-08 08:50:39 +00:00
|
|
|
reorderLastHistories();
|
2019-04-23 12:29:23 +00:00
|
|
|
}
|
2019-04-22 15:09:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Folder::applyChatListMessage(HistoryItem *item) {
|
|
|
|
if (!item) {
|
|
|
|
return false;
|
|
|
|
} else if (_chatListMessage
|
|
|
|
&& _chatListMessage->date() >= item->date()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
_chatListMessage = item;
|
|
|
|
updateChatListEntry();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Folder::computeChatListMessage() {
|
2021-03-13 12:12:08 +00:00
|
|
|
auto &&items = ranges::views::all(
|
2019-04-22 15:09:03 +00:00
|
|
|
*_chatsList.indexed()
|
2021-03-13 12:12:08 +00:00
|
|
|
) | ranges::views::filter([](not_null<Dialogs::Row*> row) {
|
2019-04-22 15:09:03 +00:00
|
|
|
return row->entry()->chatListMessage() != nullptr;
|
|
|
|
});
|
|
|
|
const auto chatListDate = [](not_null<Dialogs::Row*> row) {
|
|
|
|
return row->entry()->chatListMessage()->date();
|
|
|
|
};
|
|
|
|
const auto top = ranges::max_element(
|
|
|
|
items,
|
|
|
|
ranges::less(),
|
|
|
|
chatListDate);
|
|
|
|
if (top == items.end()) {
|
|
|
|
_chatListMessage = nullptr;
|
|
|
|
} else {
|
|
|
|
_chatListMessage = (*top)->entry()->chatListMessage();
|
|
|
|
}
|
|
|
|
updateChatListEntry();
|
2019-04-16 14:05:56 +00:00
|
|
|
}
|
|
|
|
|
2019-05-08 08:50:39 +00:00
|
|
|
void Folder::reorderLastHistories() {
|
2019-04-23 12:29:23 +00:00
|
|
|
// We want first kShowChatNamesCount histories, by last message date.
|
2019-05-08 08:50:39 +00:00
|
|
|
const auto pred = [](not_null<History*> a, not_null<History*> b) {
|
2019-04-23 12:29:23 +00:00
|
|
|
const auto aItem = a->chatListMessage();
|
|
|
|
const auto bItem = b->chatListMessage();
|
|
|
|
const auto aDate = aItem ? aItem->date() : TimeId(0);
|
|
|
|
const auto bDate = bItem ? bItem->date() : TimeId(0);
|
|
|
|
return aDate > bDate;
|
|
|
|
};
|
2019-05-08 08:50:39 +00:00
|
|
|
_lastHistories.erase(_lastHistories.begin(), _lastHistories.end());
|
|
|
|
_lastHistories.reserve(kShowChatNamesCount + 1);
|
2021-03-13 12:12:08 +00:00
|
|
|
auto &&histories = ranges::views::all(
|
2019-05-08 08:50:39 +00:00
|
|
|
*_chatsList.indexed()
|
2021-03-13 12:12:08 +00:00
|
|
|
) | ranges::views::transform([](not_null<Dialogs::Row*> row) {
|
2019-05-08 08:50:39 +00:00
|
|
|
return row->history();
|
2021-03-13 12:12:08 +00:00
|
|
|
}) | ranges::views::filter([](History *history) {
|
2019-05-08 08:50:39 +00:00
|
|
|
return (history != nullptr);
|
2021-03-13 12:12:08 +00:00
|
|
|
}) | ranges::views::transform([](History *history) {
|
2019-05-08 08:50:39 +00:00
|
|
|
return not_null<History*>(history);
|
|
|
|
});
|
|
|
|
for (const auto history : histories) {
|
|
|
|
const auto i = ranges::upper_bound(_lastHistories, history, pred);
|
|
|
|
if (size(_lastHistories) < kShowChatNamesCount
|
|
|
|
|| i != end(_lastHistories)) {
|
|
|
|
_lastHistories.insert(i, history);
|
2019-04-23 12:29:23 +00:00
|
|
|
}
|
2019-05-08 08:50:39 +00:00
|
|
|
if (size(_lastHistories) > kShowChatNamesCount) {
|
|
|
|
_lastHistories.pop_back();
|
2019-04-23 12:29:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
++_chatListViewVersion;
|
|
|
|
updateChatListEntry();
|
|
|
|
}
|
|
|
|
|
2019-04-22 14:22:39 +00:00
|
|
|
not_null<Dialogs::MainList*> Folder::chatsList() {
|
|
|
|
return &_chatsList;
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
void Folder::loadUserpic() {
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
void Folder::paintUserpic(
|
2018-01-05 15:57:18 +00:00
|
|
|
Painter &p,
|
2020-05-28 14:32:10 +00:00
|
|
|
std::shared_ptr<Data::CloudImageView> &view,
|
2018-01-05 15:57:18 +00:00
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int size) const {
|
2019-06-17 13:26:08 +00:00
|
|
|
paintUserpic(p, x, y, size, nullptr, nullptr);
|
2019-05-16 08:39:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Folder::paintUserpic(
|
|
|
|
Painter &p,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int size,
|
|
|
|
const style::color &bg,
|
|
|
|
const style::color &fg) const {
|
2019-06-17 13:26:08 +00:00
|
|
|
paintUserpic(p, x, y, size, &bg, &fg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Folder::paintUserpic(
|
|
|
|
Painter &p,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int size,
|
|
|
|
const style::color *overrideBg,
|
|
|
|
const style::color *overrideFg) const {
|
2019-04-16 08:50:59 +00:00
|
|
|
p.setPen(Qt::NoPen);
|
2019-06-17 13:26:08 +00:00
|
|
|
p.setBrush(overrideBg ? *overrideBg : st::historyPeerArchiveUserpicBg);
|
2019-04-16 08:50:59 +00:00
|
|
|
{
|
|
|
|
PainterHighQualityEnabler hq(p);
|
2019-04-23 13:28:44 +00:00
|
|
|
p.drawEllipse(x, y, size, size);
|
|
|
|
}
|
|
|
|
if (size == st::dialogsPhotoSize) {
|
2019-06-17 13:26:08 +00:00
|
|
|
const auto rect = QRect{ x, y, size, size };
|
|
|
|
if (overrideFg) {
|
|
|
|
st::dialogsArchiveUserpic.paintInCenter(
|
|
|
|
p,
|
|
|
|
rect,
|
|
|
|
(*overrideFg)->c);
|
|
|
|
} else {
|
|
|
|
st::dialogsArchiveUserpic.paintInCenter(p, rect);
|
|
|
|
}
|
2019-04-23 13:28:44 +00:00
|
|
|
} else {
|
|
|
|
p.save();
|
|
|
|
const auto ratio = size / float64(st::dialogsPhotoSize);
|
|
|
|
p.translate(x + size / 2., y + size / 2.);
|
|
|
|
p.scale(ratio, ratio);
|
|
|
|
const auto skip = st::dialogsPhotoSize;
|
2019-06-17 13:26:08 +00:00
|
|
|
const auto rect = QRect{ -skip, -skip, 2 * skip, 2 * skip };
|
|
|
|
if (overrideFg) {
|
|
|
|
st::dialogsArchiveUserpic.paintInCenter(
|
|
|
|
p,
|
|
|
|
rect,
|
|
|
|
(*overrideFg)->c);
|
|
|
|
} else {
|
|
|
|
st::dialogsArchiveUserpic.paintInCenter(p, rect);
|
|
|
|
}
|
2019-04-23 13:28:44 +00:00
|
|
|
p.restore();
|
2019-04-16 08:50:59 +00:00
|
|
|
}
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
|
2019-05-08 08:50:39 +00:00
|
|
|
const std::vector<not_null<History*>> &Folder::lastHistories() const {
|
|
|
|
return _lastHistories;
|
2019-04-23 12:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32 Folder::chatListViewVersion() const {
|
|
|
|
return _chatListViewVersion;
|
|
|
|
}
|
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
void Folder::requestChatListMessage() {
|
2019-01-15 11:57:45 +00:00
|
|
|
if (!chatListMessageKnown()) {
|
2020-02-21 07:58:50 +00:00
|
|
|
owner().histories().requestDialogEntry(this);
|
2019-01-15 11:57:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-18 09:00:38 +00:00
|
|
|
TimeId Folder::adjustedChatListTimeId() const {
|
2019-04-22 15:09:03 +00:00
|
|
|
return _chatListMessage ? _chatListMessage->date() : chatListTimeId();
|
2018-02-03 19:52:35 +00:00
|
|
|
}
|
2019-04-16 08:50:59 +00:00
|
|
|
|
|
|
|
void Folder::applyDialog(const MTPDdialogFolder &data) {
|
2020-03-16 10:20:18 +00:00
|
|
|
_chatsList.updateCloudUnread(data);
|
2019-07-05 13:38:38 +00:00
|
|
|
if (const auto peerId = peerFromMTP(data.vpeer())) {
|
2019-04-17 13:22:37 +00:00
|
|
|
const auto history = owner().history(peerId);
|
2019-04-16 08:50:59 +00:00
|
|
|
const auto fullId = FullMsgId(
|
|
|
|
peerToChannel(peerId),
|
2019-07-05 13:38:38 +00:00
|
|
|
data.vtop_message().v);
|
2019-04-25 12:45:15 +00:00
|
|
|
history->setFolder(this, owner().message(fullId));
|
2019-04-17 13:22:37 +00:00
|
|
|
} else {
|
|
|
|
_chatsList.clear();
|
|
|
|
updateChatListExistence();
|
2019-04-16 08:50:59 +00:00
|
|
|
}
|
2019-04-22 14:22:39 +00:00
|
|
|
if (_chatsList.indexed()->size() < kLoadedChatsMinCount) {
|
|
|
|
session().api().requestDialogs(this);
|
2019-04-19 08:47:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Folder::applyPinnedUpdate(const MTPDupdateDialogPinned &data) {
|
2019-07-05 13:38:38 +00:00
|
|
|
const auto folderId = data.vfolder_id().value_or_empty();
|
2019-04-19 08:47:49 +00:00
|
|
|
if (folderId != 0) {
|
|
|
|
LOG(("API Error: Nested folders detected."));
|
2019-04-17 12:08:02 +00:00
|
|
|
}
|
2020-03-17 13:04:30 +00:00
|
|
|
owner().setChatPinned(this, FilterId(), data.is_pinned());
|
2019-04-16 08:50:59 +00:00
|
|
|
}
|
2018-01-31 17:10:29 +00:00
|
|
|
|
2019-04-18 11:31:30 +00:00
|
|
|
int Folder::fixedOnTopIndex() const {
|
|
|
|
return kArchiveFixOnTopIndex;
|
2018-05-11 14:03:53 +00:00
|
|
|
}
|
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
bool Folder::shouldBeInChatList() const {
|
2019-04-16 14:05:56 +00:00
|
|
|
return !_chatsList.empty();
|
2018-01-23 16:51:12 +00:00
|
|
|
}
|
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
int Folder::chatListUnreadCount() const {
|
2019-04-24 15:28:01 +00:00
|
|
|
const auto state = chatListUnreadState();
|
|
|
|
return state.marks
|
2020-06-18 18:04:16 +00:00
|
|
|
+ (Core::App().settings().countUnreadMessages()
|
2019-04-24 15:28:01 +00:00
|
|
|
? state.messages
|
|
|
|
: state.chats);
|
2018-01-23 16:51:12 +00:00
|
|
|
}
|
|
|
|
|
2020-03-16 10:20:18 +00:00
|
|
|
Dialogs::UnreadState Folder::chatListUnreadState() const {
|
|
|
|
return _chatsList.unreadState();
|
|
|
|
}
|
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
bool Folder::chatListUnreadMark() const {
|
2021-02-25 13:03:51 +00:00
|
|
|
return false;
|
2018-06-26 18:03:45 +00:00
|
|
|
}
|
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
bool Folder::chatListMutedBadge() const {
|
2019-04-22 14:22:39 +00:00
|
|
|
return true;
|
2018-01-23 16:51:12 +00:00
|
|
|
}
|
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
HistoryItem *Folder::chatListMessage() const {
|
2019-04-22 15:09:03 +00:00
|
|
|
return _chatListMessage;
|
2019-01-15 11:57:45 +00:00
|
|
|
}
|
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
bool Folder::chatListMessageKnown() const {
|
2019-04-22 15:09:03 +00:00
|
|
|
return true;
|
2018-01-23 16:51:12 +00:00
|
|
|
}
|
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
const QString &Folder::chatListName() const {
|
2018-01-23 16:51:12 +00:00
|
|
|
return _name;
|
|
|
|
}
|
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
const base::flat_set<QString> &Folder::chatListNameWords() const {
|
2018-01-23 16:51:12 +00:00
|
|
|
return _nameWords;
|
|
|
|
}
|
|
|
|
|
2019-04-15 11:54:03 +00:00
|
|
|
const base::flat_set<QChar> &Folder::chatListFirstLetters() const {
|
2018-01-23 16:51:12 +00:00
|
|
|
return _nameFirstLetters;
|
|
|
|
}
|
|
|
|
|
2021-02-19 11:54:27 +00:00
|
|
|
const QString &Folder::chatListNameSortKey() const {
|
|
|
|
return _chatListNameSortKey;
|
|
|
|
}
|
|
|
|
|
2018-01-04 09:40:58 +00:00
|
|
|
} // namespace Data
|