Support basic feed display in chats list.

This commit is contained in:
John Preston 2018-01-05 18:57:18 +03:00
parent 9d2239291d
commit 782e70b171
28 changed files with 1092 additions and 527 deletions

View File

@ -7,17 +7,126 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_feed.h"
#include "dialogs/dialogs_key.h"
namespace Data {
Feed::Feed(FeedId id) : _id(id) {
FeedPosition::FeedPosition(const MTPFeedPosition &position)
: date(position.c_feedPosition().vdate.v)
, peerId(peerFromMTP(position.c_feedPosition().vpeer))
, msgId(position.c_feedPosition().vid.v) {
}
FeedPosition::FeedPosition(not_null<HistoryItem*> item)
: date(toServerTime(item->date.toTime_t()).v)
, peerId(item->history()->peer->id)
, msgId(item->id) {
}
bool FeedPosition::operator<(const FeedPosition &other) const {
if (date < other.date) {
return true;
} else if (other.date < date) {
return false;
}
const auto peer = peerToBareInt(peerId);
const auto otherPeer = peerToBareInt(other.peerId);
if (peer < otherPeer) {
return true;
} else if (otherPeer < peer) {
return false;
}
return (msgId < other.msgId);
}
Feed::Feed(FeedId id)
: Entry(this)
, _id(id) {
}
void Feed::registerOne(not_null<ChannelData*> channel) {
_channels.emplace(channel);
const auto history = App::history(channel);
if (!base::contains(_channels, history)) {
_channels.push_back(history);
if (history->lastMsg) {
updateLastMessage(history->lastMsg);
}
}
}
void Feed::unregisterOne(not_null<ChannelData*> channel) {
_channels.remove(channel);
const auto history = App::history(channel);
_channels.erase(ranges::remove(_channels, history), end(_channels));
if (_lastMessage->history() == history) {
messageRemoved(_lastMessage);
}
}
void Feed::updateLastMessage(not_null<HistoryItem*> item) {
if (justSetLastMessage(item)) {
setChatsListDate(_lastMessage->date);
}
}
void Feed::loadUserpic() {
constexpr auto kPaintUserpicsCount = 4;
auto load = kPaintUserpicsCount;
for (const auto channel : _channels) {
channel->peer->loadUserpic();
if (!--load) {
break;
}
}
}
void Feed::paintUserpic(
Painter &p,
int x,
int y,
int size) const {
const auto small = (size - st::lineWidth) / 2;
const auto delta = size - small;
auto index = 0;
for (const auto channel : _channels) {
channel->peer->paintUserpic(p, x, y, small);
switch (++index) {
case 1:
case 3: x += delta; break;
case 2: x -= delta; y += delta; break;
}
}
}
bool Feed::justSetLastMessage(not_null<HistoryItem*> item) {
if (_lastMessage && FeedPosition(item) <= FeedPosition(_lastMessage)) {
return false;
}
_lastMessage = item;
return true;
}
void Feed::messageRemoved(not_null<HistoryItem*> item) {
if (_lastMessage == item) {
recountLastMessage();
}
}
void Feed::historyCleared(not_null<History*> history) {
if (_lastMessage->history() == history) {
recountLastMessage();
}
}
void Feed::recountLastMessage() {
_lastMessage = nullptr;
for (const auto history : _channels) {
if (const auto last = history->lastMsg) {
justSetLastMessage(last);
}
}
if (_lastMessage) {
setChatsListDate(_lastMessage->date);
}
}
void Feed::setUnreadCounts(int unreadCount, int unreadMutedCount) {
@ -25,14 +134,8 @@ void Feed::setUnreadCounts(int unreadCount, int unreadMutedCount) {
_unreadMutedCount = unreadMutedCount;
}
void Feed::cachePinnedIndex(int index) {
_pinnedIndex = index;
}
uint64 Feed::sortKeyInChatList() const {
return 0ULL;/* isPinnedDialog()
? pinnedDialogPos(_pinnedIndex)
: dialogPosFromDate(chatListDate());*/
void Feed::setUnreadPosition(const FeedPosition &position) {
_unreadPosition = position;
}
} // namespace Data

View File

@ -7,11 +7,47 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "dialogs/dialogs_entry.h"
class ChannelData;
namespace Data {
class Feed {
struct FeedPosition {
FeedPosition() = default;
FeedPosition(const MTPFeedPosition &position);
FeedPosition(not_null<HistoryItem*> item);
explicit operator bool() const {
return (msgId != 0);
}
bool operator<(const FeedPosition &other) const;
inline bool operator>(const FeedPosition &other) const {
return other < *this;
}
inline bool operator<=(const FeedPosition &other) const {
return !(other < *this);
}
inline bool operator>=(const FeedPosition &other) const {
return !(*this < other);
}
inline bool operator==(const FeedPosition &other) const {
return (date == other.date)
&& (peerId == other.peerId)
&& (msgId == other.msgId);
}
inline bool operator!=(const FeedPosition &other) const {
return !(*this == other);
}
TimeId date = 0;
PeerId peerId = 0;
MsgId msgId = 0;
};
class Feed : public Dialogs::Entry {
public:
Feed(FeedId id);
@ -21,20 +57,44 @@ public:
void registerOne(not_null<ChannelData*> channel);
void unregisterOne(not_null<ChannelData*> channel);
void setUnreadCounts(int unreadCount, int unreadMutedCount);
void updateLastMessage(not_null<HistoryItem*> item);
void messageRemoved(not_null<HistoryItem*> item);
void historyCleared(not_null<History*> history);
bool isPinnedDialog() const {
return _pinnedIndex > 0;
void setUnreadCounts(int unreadCount, int unreadMutedCount);
void setUnreadPosition(const FeedPosition &position);
bool toImportant() const override {
return false; // TODO feeds workmode
}
void cachePinnedIndex(int index);
uint64 sortKeyInChatList() const;
int chatListUnreadCount() const override {
return _unreadCount;
}
bool chatListMutedBadge() const override {
return _unreadCount <= _unreadMutedCount;
}
HistoryItem *chatsListItem() const override {
return _lastMessage;
}
void loadUserpic() override;
void paintUserpic(
Painter &p,
int x,
int y,
int size) const override;
private:
void recountLastMessage();
bool justSetLastMessage(not_null<HistoryItem*> item);
FeedId _id = 0;
base::flat_set<not_null<ChannelData*>> _channels;
std::vector<not_null<History*>> _channels;
HistoryItem *_lastMessage = nullptr;
FeedPosition _unreadPosition;
int _unreadCount = 0;
int _unreadMutedCount = 0;
int _pinnedIndex = 0;
bool _complete = false;
};

View File

@ -176,8 +176,7 @@ TimeId SortByOnlineValue(not_null<UserData*> user, TimeId now) {
}
const auto online = user->onlineTill;
const auto fromDate = [](const QDate &date) {
const auto shift = (unixtime() - myunixtime());
return static_cast<TimeId>(QDateTime(date).toTime_t()) + shift;
return toServerTime(QDateTime(date).toTime_t()).v;
};
if (online <= 0) {
switch (online) {

View File

@ -235,8 +235,8 @@ void Session::reorderTwoPinnedDialogs(
Assert(index2 >= 0 && index2 < order.size());
Assert(index1 != index2);
std::swap(_pinnedDialogs[index1], _pinnedDialogs[index2]);
key1.cachePinnedIndex(index2 + 1);
key2.cachePinnedIndex(index1 + 1);
key1.entry()->cachePinnedIndex(index2 + 1);
key2.entry()->cachePinnedIndex(index1 + 1);
}
void Session::setIsPinned(const Dialogs::Key &key, bool pinned) {
@ -249,26 +249,31 @@ void Session::setIsPinned(const Dialogs::Key &key, bool pinned) {
Assert(alreadyIndex < count);
for (auto index = alreadyIndex + 1; index != count; ++index) {
_pinnedDialogs[index - 1] = std::move(_pinnedDialogs[index]);
_pinnedDialogs[index - 1].cachePinnedIndex(index);
_pinnedDialogs[index - 1].entry()->cachePinnedIndex(index);
}
_pinnedDialogs.back() = std::move(saved);
_pinnedDialogs.back().cachePinnedIndex(count);
_pinnedDialogs.back().entry()->cachePinnedIndex(count);
} else {
_pinnedDialogs.push_back(key);
if (_pinnedDialogs.size() > Global::PinnedDialogsCountMax()) {
_pinnedDialogs.front().cachePinnedIndex(0);
_pinnedDialogs.front().entry()->cachePinnedIndex(0);
_pinnedDialogs.pop_front();
auto index = 0;
for (const auto &pinned : _pinnedDialogs) {
pinned.cachePinnedIndex(++index);
pinned.entry()->cachePinnedIndex(++index);
}
} else {
key.cachePinnedIndex(_pinnedDialogs.size());
key.entry()->cachePinnedIndex(_pinnedDialogs.size());
}
}
} else if (!pinned && already != _pinnedDialogs.end()) {
key.entry()->cachePinnedIndex(0);
_pinnedDialogs.erase(already);
auto index = 0;
for (const auto &pinned : _pinnedDialogs) {
pinned.entry()->cachePinnedIndex(++index);
}
}
}

View File

@ -1,28 +0,0 @@
/*
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
*/
#pragma once
#include "base/flat_map.h"
namespace Dialogs {
class Row;
using RowsByLetter = base::flat_map<QChar, Row*>;
enum class SortMode {
Date = 0x00,
Name = 0x01,
Add = 0x02,
};
enum class Mode {
All = 0x00,
Important = 0x01,
};
} // namespace Dialogs

View File

@ -0,0 +1,166 @@
/*
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
*/
#include "dialogs/dialogs_entry.h"
#include "dialogs/dialogs_key.h"
#include "dialogs/dialogs_indexed_list.h"
#include "mainwidget.h"
#include "styles/style_dialogs.h"
namespace Dialogs {
namespace {
auto DialogsPosToTopShift = 0;
uint64 DialogPosFromDate(const QDateTime &date) {
if (date.isNull()) {
return 0;
}
return (uint64(date.toTime_t()) << 32) | (++DialogsPosToTopShift);
}
uint64 PinnedDialogPos(int pinnedIndex) {
return 0xFFFFFFFF00000000ULL + pinnedIndex;
}
} // namespace
bool MessageIsLess(not_null<HistoryItem*> a, not_null<HistoryItem*> b) {
if (a->date < b->date) {
return true;
} else if (b->date < a->date) {
return false;
}
const auto apeer = a->history()->peer->bareId();
const auto bpeer = b->history()->peer->bareId();
if (apeer < bpeer) {
return true;
} else if (bpeer < apeer) {
return false;
}
return a->id < b->id;
}
Entry::Entry(const Key &key)
: lastItemTextCache(st::dialogsTextWidthMin)
, _key(key) {
}
void Entry::cachePinnedIndex(int index) {
if (_pinnedIndex != index) {
const auto wasPinned = isPinnedDialog();
_pinnedIndex = index;
updateChatListSortPosition();
updateChatListEntry();
if (wasPinned != isPinnedDialog()) {
changedChatListPinHook();
}
}
}
void Entry::updateChatListSortPosition() {
_sortKeyInChatList = isPinnedDialog()
? PinnedDialogPos(_pinnedIndex)
: DialogPosFromDate(adjustChatListDate());
if (auto m = App::main()) {
if (needUpdateInChatList()) {
if (_sortKeyInChatList) {
m->createDialog(_key);
updateChatListEntry();
} else {
removeDialog();
}
}
}
}
void Entry::removeDialog() {
if (const auto main = App::main()) {
main->removeDialog(_key);
}
}
PositionChange Entry::adjustByPosInChatList(
Mode list,
not_null<IndexedList*> indexed) {
const auto lnk = mainChatListLink(list);
const auto movedFrom = lnk->pos();
indexed->adjustByPos(chatListLinks(list));
const auto movedTo = lnk->pos();
return { movedFrom, movedTo };
}
void Entry::setChatsListDate(const QDateTime &date) {
if (!_lastMessageDate.isNull() && _lastMessageDate >= date) {
if (!needUpdateInChatList() || !inChatList(Dialogs::Mode::All)) {
return;
}
}
_lastMessageDate = date;
updateChatListSortPosition();
}
int Entry::posInChatList(Dialogs::Mode list) const {
return mainChatListLink(list)->pos();
}
not_null<Row*> Entry::addToChatList(
Mode list,
not_null<IndexedList*> indexed) {
if (!inChatList(list)) {
chatListLinks(list) = indexed->addToEnd(_key);
changedInChatListHook(list, true);
}
return mainChatListLink(list);
}
void Entry::removeFromChatList(
Dialogs::Mode list,
not_null<Dialogs::IndexedList*> indexed) {
if (inChatList(list)) {
indexed->del(_key);
chatListLinks(list).clear();
changedInChatListHook(list, false);
}
}
void Entry::removeChatListEntryByLetter(Mode list, QChar letter) {
Expects(letter != 0);
if (inChatList(list)) {
chatListLinks(list).remove(letter);
}
}
void Entry::addChatListEntryByLetter(
Mode list,
QChar letter,
not_null<Row*> row) {
Expects(letter != 0);
if (inChatList(list)) {
chatListLinks(list).emplace(letter, row);
}
}
void Entry::updateChatListEntry() const {
if (const auto main = App::main()) {
if (inChatList(Mode::All)) {
main->dlgUpdated(
Mode::All,
mainChatListLink(Mode::All));
if (inChatList(Mode::Important)) {
main->dlgUpdated(
Mode::Important,
mainChatListLink(Mode::Important));
}
}
}
}
} // namespace Dialogs

View File

@ -0,0 +1,131 @@
/*
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
*/
#pragma once
#include "base/flat_map.h"
#include "dialogs/dialogs_key.h"
namespace Dialogs {
class Row;
class IndexedList;
using RowsByLetter = base::flat_map<QChar, not_null<Row*>>;
enum class SortMode {
Date = 0x00,
Name = 0x01,
Add = 0x02,
};
enum class Mode {
All = 0x00,
Important = 0x01,
};
struct PositionChange {
int movedFrom;
int movedTo;
};
bool MessageIsLess(not_null<HistoryItem*> a, not_null<HistoryItem*> b);
class Entry {
public:
Entry(const Key &key);
PositionChange adjustByPosInChatList(
Mode list,
not_null<IndexedList*> indexed);
bool inChatList(Mode list) const {
return !chatListLinks(list).empty();
}
int posInChatList(Mode list) const;
not_null<Row*> addToChatList(Mode list, not_null<IndexedList*> indexed);
void removeFromChatList(Mode list, not_null<IndexedList*> indexed);
void removeChatListEntryByLetter(Mode list, QChar letter);
void addChatListEntryByLetter(
Mode list,
QChar letter,
not_null<Row*> row);
void updateChatListEntry() const;
bool isPinnedDialog() const {
return _pinnedIndex > 0;
}
void cachePinnedIndex(int index);
uint64 sortKeyInChatList() const {
return _sortKeyInChatList;
}
void updateChatListSortPosition();
void setChatsListDate(const QDateTime &date);
virtual bool toImportant() const = 0;
virtual bool needUpdateInChatList() const {
return true;
}
virtual int chatListUnreadCount() const = 0;
virtual bool chatListMutedBadge() const = 0;
virtual HistoryItem *chatsListItem() const = 0;
virtual void loadUserpic() = 0;
virtual void paintUserpic(
Painter &p,
int x,
int y,
int size) const = 0;
void paintUserpicLeft(
Painter &p,
int x,
int y,
int w,
int size) const {
paintUserpic(p, rtl() ? (w - x - size) : x, y, size);
}
QDateTime chatsListDate() const {
return _lastMessageDate;
}
virtual ~Entry() = default;
mutable const HistoryItem *textCachedFor = nullptr; // cache
mutable Text lastItemTextCache;
private:
virtual QDateTime adjustChatListDate() const {
return chatsListDate();
}
virtual void removeDialog();
virtual void changedInChatListHook(Dialogs::Mode list, bool added) {
}
virtual void changedChatListPinHook() {
}
RowsByLetter &chatListLinks(Mode list) {
return _chatListLinks[static_cast<int>(list)];
}
const RowsByLetter &chatListLinks(Mode list) const {
return _chatListLinks[static_cast<int>(list)];
}
Row *mainChatListLink(Mode list) const {
auto it = chatListLinks(list).find(0);
Assert(it != chatListLinks(list).cend());
return it->second;
}
Dialogs::Key _key;
RowsByLetter _chatListLinks[2];
uint64 _sortKeyInChatList = 0;
int _pinnedIndex = 0;
QDateTime _lastMessageDate;
};
} // namespace Dialogs

View File

@ -152,14 +152,12 @@ void IndexedList::adjustByName(
void IndexedList::adjustNames(
Mode list,
Key key,
not_null<History*> history,
const PeerData::NameFirstChars &oldChars) {
const auto key = Dialogs::Key(history);
auto mainRow = _list.getRow(key);
if (!mainRow) return;
// #TODO feeds dialogs
auto history = mainRow->history();
PeerData::NameFirstChars toRemove = oldChars, toAdd;
for (auto ch : key.nameFirstChars()) {
auto j = toRemove.find(ch);

View File

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "dialogs/dialogs_common.h"
#include "dialogs/dialogs_entry.h"
#include "dialogs/dialogs_list.h"
class History;
@ -80,7 +80,7 @@ private:
const PeerData::NameFirstChars &oldChars);
void adjustNames(
Mode list,
Key key,
not_null<History*> history,
const PeerData::NameFirstChars &oldChars);
SortMode _sortMode;

View File

@ -186,17 +186,24 @@ void DialogsInner::paintRegion(Painter &p, const QRegion &region, bool paintingO
p.translate(0, st::dialogsImportantBarHeight);
}
auto otherStart = rows->size() * st::dialogsRowHeight;
auto active = App::main()->activePeer();
// #TODO feeds dialogs
auto selected = _menuPeer
? _menuPeer
// #TODO feeds show
const auto active = [&] {
if (const auto peer = App::main()->activePeer()) {
if (const auto history = App::historyLoaded(peer)) {
return Dialogs::Key(history);
}
}
return Dialogs::Key();
}();
const auto selected = _menuKey
? _menuKey
: (isPressed()
? (_pressed
? _pressed->history()->peer
: nullptr)
? _pressed->key()
: Dialogs::Key())
: (_selected
? _selected->history()->peer
: nullptr));
? _selected->key()
: Dialogs::Key()));
if (otherStart) {
auto reorderingPinned = (_aboveIndex >= 0 && !_pinnedRows.empty());
auto &list = rows->all();
@ -302,17 +309,31 @@ void DialogsInner::paintRegion(Painter &p, const QRegion &region, bool paintingO
auto to = ceilclamp(r.y() + r.height() - skip, st::dialogsRowHeight, 0, _filterResults.size());
p.translate(0, from * st::dialogsRowHeight);
if (from < _filterResults.size()) {
auto activePeer = App::main()->activePeer();
auto activeMsgId = App::main()->activeMsgId();
const auto activePeer = App::main()->activePeer();
const auto activeMsgId = App::main()->activeMsgId();
for (; from < to; ++from) {
auto row = _filterResults[from];
// #TODO feeds dialogs
auto peer = row->history()->peer;
auto active = !activeMsgId && ((peer == activePeer)
|| (peer->migrateTo()
&& peer->migrateTo() == activePeer));
auto selected = _menuPeer ? (peer == _menuPeer) : (from == (isPressed() ? _filteredPressed : _filteredSelected));
Dialogs::Layout::RowPainter::paint(p, _filterResults[from], fullWidth, active, selected, paintingOther, ms);
const auto row = _filterResults[from];
const auto key = row->key();
const auto history = key.history();
const auto peer = history ? history->peer : nullptr;
const auto active = !activeMsgId
&& peer
&& activePeer
&& ((peer == activePeer)
|| (peer->migrateTo() == activePeer));
const auto selected = _menuKey
? (key == _menuKey)
: (from == (isPressed()
? _filteredPressed
: _filteredSelected));
Dialogs::Layout::RowPainter::paint(
p,
_filterResults[from],
fullWidth,
active,
selected,
paintingOther,
ms);
p.translate(0, st::dialogsRowHeight);
}
}
@ -335,10 +356,15 @@ void DialogsInner::paintRegion(Painter &p, const QRegion &region, bool paintingO
auto activePeer = App::main()->activePeer();
auto activeMsgId = App::main()->activeMsgId();
for (; from < to; ++from) {
auto &result = _peerSearchResults[from];
auto peer = result->peer;
auto active = ((peer == activePeer) || (peer->migrateTo() && peer->migrateTo() == activePeer)) && !activeMsgId;
auto selected = false ? (peer == _menuPeer) : (from == (isPressed() ? _peerSearchPressed : _peerSearchSelected));
const auto &result = _peerSearchResults[from];
const auto peer = result->peer;
const auto active = !activeMsgId
&& activePeer
&& ((peer == activePeer)
|| (peer->migrateTo() == activePeer));
const auto selected = (from == (isPressed()
? _peerSearchPressed
: _peerSearchSelected));
paintPeerSearchResult(p, result.get(), fullWidth, active, selected, paintingOther, ms);
p.translate(0, st::dialogsRowHeight);
}
@ -377,11 +403,17 @@ void DialogsInner::paintRegion(Painter &p, const QRegion &region, bool paintingO
auto activePeer = App::main()->activePeer();
auto activeMsgId = App::main()->activeMsgId();
for (; from < to; ++from) {
auto &result = _searchResults[from];
auto item = result->item();
auto peer = item->history()->peer;
auto active = (peer == activePeer && item->id == activeMsgId) || (peer->migrateTo() && peer->migrateTo() == activePeer && item->id == -activeMsgId);
auto selected = false ? (peer == _menuPeer) : (from == (isPressed() ? _searchedPressed : _searchedSelected));
const auto &result = _searchResults[from];
const auto item = result->item();
const auto peer = item->history()->peer;
const auto active = (peer == activePeer
&& item->id == activeMsgId)
|| (peer->migrateTo()
&& peer->migrateTo() == activePeer
&& item->id == -activeMsgId);
const auto selected = (from == (isPressed()
? _searchedPressed
: _searchedSelected));
Dialogs::Layout::RowPainter::paint(p, result.get(), fullWidth, active, selected, paintingOther, ms);
p.translate(0, st::dialogsRowHeight);
}
@ -394,8 +426,8 @@ void DialogsInner::paintDialog(
Painter &p,
not_null<Dialogs::Row*> row,
int fullWidth,
PeerData *active,
PeerData *selected,
Dialogs::Key active,
Dialogs::Key selected,
bool onlyBackground,
TimeMs ms) {
auto pos = row->pos();
@ -404,11 +436,8 @@ void DialogsInner::paintDialog(
yadd = qRound(_pinnedRows[pos].yadd.current());
}
if (xadd || yadd) p.translate(xadd, yadd);
// #TODO feeds dialogs
auto isActive = (row->history()->peer == active)
|| (row->history()->peer->migrateTo()
&& row->history()->peer->migrateTo() == active);
auto isSelected = (row->history()->peer == selected);
const auto isActive = (row->key() == active);
const auto isSelected = (row->key() == selected);
Dialogs::Layout::RowPainter::paint(
p,
row,
@ -697,8 +726,7 @@ void DialogsInner::mousePressEvent(QMouseEvent *e) {
auto row = _pressed;
row->addRipple(e->pos() - QPoint(0, dialogsOffset() + _pressed->pos() * st::dialogsRowHeight), QSize(getFullWidth(), st::dialogsRowHeight), [this, row] {
if (!_a_pinnedShifting.animating()) {
// #TODO feeds dialogs
row->history()->updateChatListEntry();
row->entry()->updateChatListEntry();
}
});
_dragStart = e->pos();
@ -1072,41 +1100,57 @@ void DialogsInner::onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRo
}
}
void DialogsInner::createDialog(not_null<History*> history) {
if (history->peer->loadedStatus != PeerData::LoadedStatus::FullLoaded) {
LOG(("API Error: DialogsInner::createDialog() called for a non loaded peer!"));
return;
void DialogsInner::createDialog(Dialogs::Key key) {
if (const auto history = key.history()) {
if (history->peer->loadedStatus
!= PeerData::LoadedStatus::FullLoaded) {
LOG(("API Error: "
"DialogsInner::createDialog() called for a non loaded peer!"
));
return;
}
}
bool creating = !history->inChatList(Dialogs::Mode::All);
const auto entry = key.entry();
auto creating = !entry->inChatList(Dialogs::Mode::All);
if (creating) {
auto mainRow = history->addToChatList(Dialogs::Mode::All, _dialogs.get());
_contactsNoDialogs->del(history, mainRow);
const auto mainRow = entry->addToChatList(
Dialogs::Mode::All,
_dialogs.get());
_contactsNoDialogs->del(key, mainRow);
}
if (_dialogsImportant && !history->inChatList(Dialogs::Mode::Important) && !history->mute()) {
if (_dialogsImportant
&& !entry->inChatList(Dialogs::Mode::Important)
&& entry->toImportant()) {
if (Global::DialogsMode() == Dialogs::Mode::Important) {
creating = true;
}
history->addToChatList(Dialogs::Mode::Important, _dialogsImportant.get());
entry->addToChatList(
Dialogs::Mode::Important,
_dialogsImportant.get());
}
auto changed = history->adjustByPosInChatList(Dialogs::Mode::All, _dialogs.get());
auto changed = entry->adjustByPosInChatList(
Dialogs::Mode::All,
_dialogs.get());
if (_dialogsImportant) {
if (history->mute()) {
if (!entry->toImportant()) {
if (Global::DialogsMode() == Dialogs::Mode::Important) {
return;
}
} else {
auto importantChanged = history->adjustByPosInChatList(Dialogs::Mode::Important, _dialogsImportant.get());
const auto importantChanged = entry->adjustByPosInChatList(
Dialogs::Mode::Important,
_dialogsImportant.get());
if (Global::DialogsMode() == Dialogs::Mode::Important) {
changed = importantChanged;
}
}
}
int from = dialogsOffset() + changed.movedFrom * st::dialogsRowHeight;
int to = dialogsOffset() + changed.movedTo * st::dialogsRowHeight;
const auto from = dialogsOffset() + changed.movedFrom * st::dialogsRowHeight;
const auto to = dialogsOffset() + changed.movedTo * st::dialogsRowHeight;
if (!_dragging) {
// Don't jump in chats list scroll position while dragging.
emit dialogMoved(from, to);
@ -1119,30 +1163,35 @@ void DialogsInner::createDialog(not_null<History*> history) {
}
}
void DialogsInner::removeDialog(not_null<History*> history) {
// #TODO feeds dialogs
if (history->peer == _menuPeer && _menu) {
void DialogsInner::removeDialog(Dialogs::Key key) {
if (key == _menuKey && _menu) {
InvokeQueued(this, [this] { _menu = nullptr; });
}
if (_selected && _selected->history() == history) {
if (_selected && _selected->key() == key) {
_selected = nullptr;
}
if (_pressed && _pressed->history() == history) {
if (_pressed && _pressed->key() == key) {
setPressed(nullptr);
}
history->removeFromChatList(Dialogs::Mode::All, _dialogs.get());
const auto entry = key.entry();
entry->removeFromChatList(
Dialogs::Mode::All,
_dialogs.get());
if (_dialogsImportant) {
history->removeFromChatList(Dialogs::Mode::Important, _dialogsImportant.get());
entry->removeFromChatList(
Dialogs::Mode::Important,
_dialogsImportant.get());
}
Auth().notifications().clearFromHistory(history);
if (_contacts->contains(history)) {
if (!_contactsNoDialogs->contains(history)) {
_contactsNoDialogs->addByName(history);
if (const auto history = key.history()) {
Auth().notifications().clearFromHistory(history);
Local::removeSavedPeer(history->peer);
}
if (_contacts->contains(key)) {
if (!_contactsNoDialogs->contains(key)) {
_contactsNoDialogs->addByName(key);
}
}
Local::removeSavedPeer(history->peer);
emit App::main()->dialogsUpdated();
refresh();
@ -1257,29 +1306,28 @@ void DialogsInner::enterEventHook(QEvent *e) {
updateSelected();
}
void DialogsInner::updateSelectedRow(PeerData *peer) {
// #TODO feeds dialogs
void DialogsInner::updateSelectedRow(Dialogs::Key key) {
if (_state == DefaultState) {
if (peer) {
if (auto h = App::historyLoaded(peer->id)) {
if (h->inChatList(Global::DialogsMode())) {
auto position = h->posInChatList(Global::DialogsMode());
auto top = dialogsOffset();
if (position >= 0 && position < _pinnedRows.size()) {
top += qRound(_pinnedRows[position].yadd.current());
}
update(0, top + position * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight);
}
if (key) {
const auto entry = key.entry();
if (!entry->inChatList(Global::DialogsMode())) {
return;
}
auto position = entry->posInChatList(Global::DialogsMode());
auto top = dialogsOffset();
if (position >= 0 && position < _pinnedRows.size()) {
top += qRound(_pinnedRows[position].yadd.current());
}
update(0, top + position * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight);
} else if (_selected) {
update(0, dialogsOffset() + _selected->pos() * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight);
} else if (_importantSwitchSelected) {
update(0, 0, getFullWidth(), st::dialogsImportantBarHeight);
}
} else if (_state == FilteredState || _state == SearchedState) {
if (peer) {
if (key) {
for (auto i = 0, l = _filterResults.size(); i != l; ++i) {
if (_filterResults[i]->history()->peer == peer) {
if (_filterResults[i]->key() == key) {
update(0, filteredOffset() + i * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight);
break;
}
@ -1325,37 +1373,46 @@ void DialogsInner::contextMenuEvent(QContextMenuEvent *e) {
updateSelected();
}
// #TODO feeds context menu
auto history = [&]() -> History* {
const auto key = [&]() -> Dialogs::Key {
if (_state == DefaultState) {
if (_selected) {
return _selected->history();
return _selected->key();
}
} else if (_state == FilteredState || _state == SearchedState) {
if (_filteredSelected >= 0 && _filteredSelected < _filterResults.size()) {
return _filterResults[_filteredSelected]->history();
return _filterResults[_filteredSelected]->key();
}
}
return nullptr;
return Dialogs::Key();
}();
if (!history) return;
if (!key) return;
_menuPeer = history->peer;
_menuKey = key;
if (_pressButton != Qt::LeftButton) {
mousePressReleased(_pressButton);
}
_menu = base::make_unique_q<Ui::PopupMenu>(nullptr);
Window::FillPeerMenu(
_controller,
_menuPeer,
[this](const QString &text, base::lambda<void()> callback) {
return _menu->addAction(text, std::move(callback));
},
Window::PeerMenuSource::ChatsList);
if (const auto history = key.history()) {
Window::FillPeerMenu(
_controller,
history->peer,
[&](const QString &text, base::lambda<void()> callback) {
return _menu->addAction(text, std::move(callback));
},
Window::PeerMenuSource::ChatsList);
} else if (const auto feed = key.feed()) {
Window::FillFeedMenu(
_controller,
feed,
[&](const QString &text, base::lambda<void()> callback) {
return _menu->addAction(text, std::move(callback));
},
Window::PeerMenuSource::ChatsList);
}
connect(_menu.get(), &QObject::destroyed, [this] {
if (_menuPeer) {
updateSelectedRow(base::take(_menuPeer));
if (_menuKey) {
updateSelectedRow(base::take(_menuKey));
}
auto localPos = mapFromGlobal(QCursor::pos());
if (rect().contains(localPos)) {
@ -1428,10 +1485,12 @@ void DialogsInner::onFilterUpdate(QString newFilter, bool force) {
}
}
}
_filterResults.reserve((toFilter ? toFilter->size() : 0) + (toFilterContacts ? toFilterContacts->size() : 0));
_filterResults.reserve((toFilter ? toFilter->size() : 0)
+ (toFilterContacts ? toFilterContacts->size() : 0));
if (toFilter) {
for_const (auto row, *toFilter) {
// #TODO feeds dialogs
if (!row->history()) continue;
// #TODO feeds name
const auto &nameWords = row->history()->peer->nameWords();
auto nb = nameWords.cbegin(), ne = nameWords.cend(), ni = nb;
for (fi = fb; fi != fe; ++fi) {
@ -1452,7 +1511,8 @@ void DialogsInner::onFilterUpdate(QString newFilter, bool force) {
}
if (toFilterContacts) {
for_const (auto row, *toFilterContacts) {
// #TODO feeds dialogs
if (!row->history()) continue;
// #TODO feeds name
const auto &nameWords = row->history()->peer->nameWords();
auto nb = nameWords.cbegin(), ne = nameWords.cend(), ni = nb;
for (fi = fb; fi != fe; ++fi) {
@ -1524,14 +1584,19 @@ void DialogsInner::clearSearchResults(bool clearPeerSearchResults) {
}
PeerData *DialogsInner::updateFromParentDrag(QPoint globalPos) {
// #TODO feeds dialogs
_mouseSelection = true;
updateSelected(mapFromGlobal(globalPos));
const auto getPeerFromRow = [](Dialogs::Row *row) -> PeerData* {
if (const auto history = row ? row->history() : nullptr) {
return history->peer;
}
return nullptr;
};
if (_state == DefaultState) {
if (_selected) return _selected->history()->peer;
return getPeerFromRow(_selected);
} else if (_state == FilteredState || _state == SearchedState) {
if (_filteredSelected >= 0 && _filteredSelected < _filterResults.size()) {
return _filterResults[_filteredSelected]->history()->peer;
return getPeerFromRow(_filterResults[_filteredSelected]);
} else if (_peerSearchSelected >= 0 && _peerSearchSelected < _peerSearchResults.size()) {
return _peerSearchResults[_peerSearchSelected]->peer;
} else if (_searchedSelected >= 0 && _searchedSelected < _searchResults.size()) {
@ -1618,8 +1683,8 @@ void DialogsInner::applyDialog(const MTPDdialog &dialog) {
dialog.vnotify_settings,
history);
if (!history->isPinnedDialog() && !history->lastMsgDate.isNull()) {
addSavedPeersAfter(history->lastMsgDate);
if (!history->isPinnedDialog() && !history->chatsListDate().isNull()) {
addSavedPeersAfter(history->chatsListDate());
}
_contactsNoDialogs->del(history);
if (peer->migrateFrom()) {
@ -1654,9 +1719,12 @@ void DialogsInner::applyFeedDialog(const MTPDdialogFeed &dialog) {
feed->setUnreadCounts(
dialog.vunread_count.v,
dialog.vunread_muted_count.v);
if (!feed->isPinnedDialog() && !feed->chatsListDate().isNull()) {
addSavedPeersAfter(feed->chatsListDate());
}
if (dialog.has_read_max_position()) {
// #TODO feeds
// feed->set
const auto position = Data::FeedPosition(dialog.vread_max_position);
feed->setUnreadPosition(position);
}
}
@ -2082,8 +2150,7 @@ void DialogsInner::loadPeerPhotos() {
if (((*i)->pos() * st::dialogsRowHeight) >= yTo) {
break;
}
// #TODO feeds dialogs
(*i)->history()->peer->loadUserpic();
(*i)->entry()->loadUserpic();
}
yFrom = 0;
} else {
@ -2098,8 +2165,7 @@ void DialogsInner::loadPeerPhotos() {
if (to > _filterResults.size()) to = _filterResults.size();
for (; from < to; ++from) {
// #TODO feeds dialogs
_filterResults[from]->history()->peer->loadUserpic();
_filterResults[from]->entry()->loadUserpic();
}
}

View File

@ -45,10 +45,10 @@ public:
void selectSkip(int32 direction);
void selectSkipPage(int32 pixels, int32 direction);
void createDialog(not_null<History*> history);
void createDialog(Dialogs::Key key);
void removeDialog(Dialogs::Key key);
void dlgUpdated(Dialogs::Mode list, not_null<Dialogs::Row*> row);
void dlgUpdated(not_null<History*> history, MsgId msgId);
void removeDialog(not_null<History*> history);
void dragLeft();
@ -207,8 +207,8 @@ private:
Painter &p,
not_null<Dialogs::Row*> row,
int fullWidth,
PeerData *active,
PeerData *selected,
Dialogs::Key active,
Dialogs::Key selected,
bool onlyBackground,
TimeMs ms);
void paintPeerSearchResult(
@ -233,7 +233,7 @@ private:
void clearSelection();
void clearSearchResults(bool clearPeerSearchResults = true);
void updateSelectedRow(PeerData *peer = 0);
void updateSelectedRow(Dialogs::Key key = Dialogs::Key());
Dialogs::IndexedList *shownDialogs() const {
return (Global::DialogsMode() == Dialogs::Mode::Important) ? _dialogsImportant.get() : _dialogs.get();
@ -323,7 +323,7 @@ private:
UserData *_searchFromUser = nullptr;
Text _searchFromUserText;
Text _searchInSavedText;
PeerData *_menuPeer = nullptr;
Dialogs::Key _menuKey;
base::lambda<void()> _loadMoreCallback;

View File

@ -29,24 +29,13 @@ const PeerData::NameFirstChars &Key::nameFirstChars() const {
return empty;
}
uint64 Key::sortKey() const {
if (const auto h = history()) {
return h->sortKeyInChatList();
} else if (const auto f = feed()) {
return f->sortKeyInChatList();
} else {
Unexpected("Key value in Key::sortKey");
}
}
void Key::cachePinnedIndex(int index) const {
if (const auto h = history()) {
h->cachePinnedIndex(index);
} else if (const auto f = feed()) {
f->cachePinnedIndex(index);
} else {
Unexpected("Key value in Key::setPinnedIndex");
not_null<Entry*> Key::entry() const {
if (const auto p = base::get_if<not_null<History*>>(&_value)) {
return *p;
} else if (const auto p = base::get_if<not_null<Data::Feed*>>(&_value)) {
return *p;
}
Unexpected("Dialogs entry() call on empty Key.");
}
History *Key::history() const {

View File

@ -9,12 +9,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/value_ordering.h"
class History;
namespace Data {
class Feed;
} // namespace Data
namespace Dialogs {
class Entry;
class Key {
public:
Key() = default;
@ -30,13 +34,12 @@ public:
explicit operator bool() const {
return !!_value;
}
not_null<Entry*> entry() const;
History *history() const;
Data::Feed *feed() const;
const QString &name() const;
const PeerData::NameFirstChars &nameFirstChars() const;
uint64 sortKey() const;
void cachePinnedIndex(int index) const;
inline bool operator<(const Key &other) const {
return _value < other._value;

View File

@ -59,10 +59,11 @@ template <typename PaintItemCallback, typename PaintCounterCallback>
void paintRow(
Painter &p,
const RippleRow *row,
not_null<Entry*> entry,
History *history,
not_null<PeerData*> from,
PeerData *from,
HistoryItem *item,
Data::Draft *draft,
const Data::Draft *draft,
QDateTime date,
int fullWidth,
base::flags<Flag> flags,
@ -94,13 +95,20 @@ void paintRow(
st::dialogsPadding.y(),
fullWidth,
st::dialogsPhotoSize);
} else {
} else if (from) {
from->paintUserpicLeft(
p,
st::dialogsPadding.x(),
st::dialogsPadding.y(),
fullWidth,
st::dialogsPhotoSize);
} else {
entry->paintUserpicLeft(
p,
st::dialogsPadding.x(),
st::dialogsPadding.y(),
fullWidth,
st::dialogsPhotoSize);
}
auto nameleft = st::dialogsPadding.x()
@ -120,11 +128,12 @@ void paintRow(
namewidth,
st::msgNameFont->height);
if (auto chatTypeIcon = ChatTypeIcon(from, active, selected)) {
chatTypeIcon->paint(p, rectForName.topLeft(), fullWidth);
rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip);
if (from) {
if (auto chatTypeIcon = ChatTypeIcon(from, active, selected)) {
chatTypeIcon->paint(p, rectForName.topLeft(), fullWidth);
rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip);
}
}
auto texttop = st::dialogsPadding.y()
+ st::msgNameFont->height
+ st::dialogsSkip;
@ -132,7 +141,7 @@ void paintRow(
paintRowDate(p, date, rectForName, active, selected);
auto availableWidth = namewidth;
if (history->isPinnedDialog()) {
if (entry->isPinnedDialog()) {
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;
@ -140,7 +149,7 @@ void paintRow(
p.setFont(st::dialogsTextFont);
auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService);
if (!history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
if (history && !history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
if (history->cloudDraftTextCache.isEmpty()) {
auto draftWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, lang(lng_from_draft)));
auto draftText = lng_dialogs_text_with_from(lt_from_part, draftWrapped, lt_message, TextUtilities::Clean(draft->textWithTags.text));
@ -153,7 +162,7 @@ void paintRow(
}
} else if (!item) {
auto availableWidth = namewidth;
if (history->isPinnedDialog()) {
if (entry->isPinnedDialog()) {
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;
@ -161,14 +170,14 @@ void paintRow(
auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService);
p.setFont(st::dialogsTextFont);
if (!history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
if (history && !history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
// Empty history
}
} else if (!item->isEmpty()) {
paintRowDate(p, date, rectForName, active, selected);
paintItemCallback(nameleft, namewidth);
} else if (history->isPinnedDialog()) {
} else if (entry->isPinnedDialog()) {
auto availableWidth = namewidth;
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
@ -209,13 +218,22 @@ void paintRow(
text = st::msgNameFont->elided(text, rectForName.width());
}
p.drawTextLeft(rectForName.left(), rectForName.top(), fullWidth, text);
} else {
} else if (from) {
if (!(flags & Flag::SearchResult) && from->isVerified()) {
auto icon = &(active ? st::dialogsVerifiedIconActive : (selected ? st::dialogsVerifiedIconOver : st::dialogsVerifiedIcon));
rectForName.setWidth(rectForName.width() - icon->width());
icon->paint(p, rectForName.topLeft() + QPoint(qMin(from->dialogName().maxWidth(), rectForName.width()), 0), fullWidth);
}
from->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width());
} else {
// TODO feeds name
p.setFont(st::msgNameFont);
auto text = QString("Feed");
auto textWidth = st::msgNameFont->width(text);
if (textWidth > rectForName.width()) {
text = st::msgNameFont->elided(text, rectForName.width());
}
p.drawTextLeft(rectForName.left(), rectForName.top(), fullWidth, text);
}
}
@ -328,69 +346,79 @@ void paintUnreadCount(Painter &p, const QString &text, int x, int y, const Unrea
void RowPainter::paint(
Painter &p,
const Row *row,
not_null<const Row*> row,
int fullWidth,
bool active,
bool selected,
bool onlyBackground,
TimeMs ms) {
// #TODO feeds show
auto history = row->history();
auto item = history->lastMsg;
auto cloudDraft = history->cloudDraft();
if (Data::draftIsNull(cloudDraft)) {
cloudDraft = nullptr;
}
auto displayDate = [item, cloudDraft]() {
const auto entry = row->entry();
const auto history = row->history();
const auto peer = history ? history->peer : nullptr;
const auto unreadCount = entry->chatListUnreadCount();
const auto unreadMuted = entry->chatListMutedBadge();
const auto item = entry->chatsListItem();
const auto cloudDraft = [&]() -> const Data::Draft*{
if (history && (!item || !unreadCount)) {
// Draw item, if there are unread messages.
if (const auto draft = history->cloudDraft()) {
if (!Data::draftIsNull(draft)) {
return draft;
}
}
}
return nullptr;
}();
const auto displayDate = [item, cloudDraft] {
if (item) {
if (cloudDraft) {
return (item->date > cloudDraft->date) ? item->date : cloudDraft->date;
return (item->date > cloudDraft->date)
? item->date
: cloudDraft->date;
}
return item->date;
}
return cloudDraft ? cloudDraft->date : QDateTime();
};
auto unreadCount = history->unreadCount();
if (history->peer->migrateFrom()) {
if (auto migrated = App::historyLoaded(history->peer->migrateFrom()->id)) {
unreadCount += migrated->unreadCount();
}
}
}();
if (item && cloudDraft && unreadCount > 0) {
cloudDraft = nullptr; // Draw item, if draft is older.
}
auto from = history->peer->migrateTo()
? history->peer->migrateTo()
: history->peer;
const auto from = history
? (history->peer->migrateTo()
? history->peer->migrateTo()
: history->peer)
: nullptr;
const auto flags = (active ? Flag::Active : Flag(0))
| (selected ? Flag::Selected : Flag(0))
| (onlyBackground ? Flag::OnlyBackground : Flag(0))
| (history->peer->isSelf() ? Flag::SavedMessages : Flag(0));
auto paintItemCallback = [&](int nameleft, int namewidth) {
| (peer && peer->isSelf() ? Flag::SavedMessages : Flag(0));
const auto paintItemCallback = [&](int nameleft, int namewidth) {
auto availableWidth = namewidth;
auto texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip;
auto hadOneBadge = false;
auto displayUnreadCounter = (unreadCount != 0);
auto displayMentionBadge = history->hasUnreadMentions();
auto displayPinnedIcon = !displayUnreadCounter && history->isPinnedDialog();
if (displayMentionBadge
&& unreadCount == 1
&& item
&& item->isMediaUnread()
&& item->mentionsMe()) {
displayUnreadCounter = false;
}
const auto displayMentionBadge = history
? history->hasUnreadMentions()
: false;
const auto displayUnreadCounter = [&] {
if (displayMentionBadge
&& unreadCount == 1
&& item
&& item->isMediaUnread()
&& item->mentionsMe()) {
return false;
}
return (unreadCount > 0);
}();
const auto displayPinnedIcon = !displayUnreadCounter
&& !displayMentionBadge
&& entry->isPinnedDialog();
if (displayUnreadCounter) {
auto counter = QString::number(unreadCount);
auto mutedCounter = history->mute();
auto unreadRight = fullWidth - st::dialogsPadding.x();
auto unreadTop = texttop + st::dialogsTextFont->ascent - st::dialogsUnreadFont->ascent - (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2;
auto unreadWidth = 0;
UnreadBadgeStyle st;
st.active = active;
st.muted = history->mute();
st.muted = unreadMuted;
paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth);
availableWidth -= unreadWidth + st.padding;
@ -416,19 +444,19 @@ void RowPainter::paint(
paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth);
availableWidth -= unreadWidth + st.padding + (hadOneBadge ? st::dialogsUnreadPadding : 0);
}
auto &color = active
const auto &color = active
? st::dialogsTextFgServiceActive
: (selected
? st::dialogsTextFgServiceOver
: st::dialogsTextFgService);
auto actionWasPainted = history->paintSendAction(
const auto actionWasPainted = history ? history->paintSendAction(
p,
nameleft,
texttop,
availableWidth,
fullWidth,
color,
ms);
ms) : false;
if (!actionWasPainted) {
auto itemRect = QRect(
nameleft,
@ -441,35 +469,35 @@ void RowPainter::paint(
active,
selected,
HistoryItem::DrawInDialog::Normal,
history->textCachedFor,
history->lastItemTextCache);
entry->textCachedFor,
entry->lastItemTextCache);
}
};
auto paintCounterCallback = [&] {
const auto paintCounterCallback = [&] {
if (unreadCount) {
auto counter = QString::number(unreadCount);
if (counter.size() > 4) {
counter = qsl("..") + counter.mid(counter.size() - 3);
}
auto mutedCounter = history->mute();
auto unreadRight = st::dialogsPadding.x() + st::dialogsPhotoSize;
auto unreadTop = st::dialogsPadding.y() + st::dialogsPhotoSize - st::dialogsUnreadHeight;
auto unreadWidth = 0;
UnreadBadgeStyle st;
st.active = active;
st.muted = history->mute();
st.muted = unreadMuted;
paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth);
}
};
paintRow(
p,
row,
entry,
history,
from,
item,
cloudDraft,
displayDate(),
displayDate,
fullWidth,
flags,
ms,
@ -479,7 +507,7 @@ void RowPainter::paint(
void RowPainter::paint(
Painter &p,
const FakeRow *row,
not_null<const FakeRow*> row,
int fullWidth,
bool active,
bool selected,
@ -530,6 +558,7 @@ void RowPainter::paint(
p,
row,
history,
history,
from,
item,
cloudDraft,

View File

@ -23,7 +23,7 @@ class RowPainter {
public:
static void paint(
Painter &p,
const Row *row,
not_null<const Row*> row,
int fullWidth,
bool active,
bool selected,
@ -31,7 +31,7 @@ public:
TimeMs ms);
static void paint(
Painter &p,
const FakeRow *row,
not_null<const FakeRow*> row,
int fullWidth,
bool active,
bool selected,

View File

@ -58,6 +58,9 @@ public:
Data::Feed *feed() const {
return _id.feed();
}
not_null<Entry*> entry() const {
return _id.entry();
}
QString name() const {
return _id.name();
}
@ -65,7 +68,7 @@ public:
return _pos;
}
uint64 sortKey() const {
return _id.sortKey();
return _id.entry()->sortKeyInChatList();
}
// for any attached data, for example View in contacts list

View File

@ -182,11 +182,13 @@ void DialogsWidget::activate() {
_inner->activate();
}
void DialogsWidget::createDialog(not_null<History*> history) {
auto creating = !history->inChatList(Dialogs::Mode::All);
_inner->createDialog(history);
if (creating && history->peer->migrateFrom()) {
if (const auto migrated = App::historyLoaded(history->peer->migrateFrom())) {
void DialogsWidget::createDialog(Dialogs::Key key) {
const auto creating = !key.entry()->inChatList(Dialogs::Mode::All);
_inner->createDialog(key);
const auto history = key.history();
if (creating && history && history->peer->migrateFrom()) {
if (const auto migrated = App::historyLoaded(
history->peer->migrateFrom())) {
if (migrated->inChatList(Dialogs::Mode::All)) {
removeDialog(migrated);
}
@ -747,8 +749,7 @@ void DialogsWidget::dragMoveEvent(QDragMoveEvent *e) {
} else {
_chooseByDragTimer.start(ChoosePeerByDragTimeout);
}
PeerData *p = _inner->updateFromParentDrag(mapToGlobal(e->pos()));
if (p) {
if (_inner->updateFromParentDrag(mapToGlobal(e->pos()))) {
e->setDropAction(Qt::CopyAction);
} else {
e->setDropAction(Qt::IgnoreAction);
@ -1118,8 +1119,8 @@ void DialogsWidget::scrollToPeer(not_null<History*> history, MsgId msgId) {
_inner->scrollToPeer(history, msgId);
}
void DialogsWidget::removeDialog(not_null<History*> history) {
_inner->removeDialog(history);
void DialogsWidget::removeDialog(Dialogs::Key key) {
_inner->removeDialog(key);
onFilterUpdate();
}

View File

@ -55,8 +55,8 @@ public:
void loadDialogs();
void loadPinnedDialogs();
void createDialog(not_null<History*> history);
void removeDialog(not_null<History*> history);
void createDialog(Dialogs::Key key);
void removeDialog(Dialogs::Key key);
void dlgUpdated(Dialogs::Mode list, not_null<Dialogs::Row*> row);
void dlgUpdated(not_null<History*> history, MsgId msgId);

View File

@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_facade.h"
#include "storage/storage_shared_media.h"
#include "data/data_channel_admins.h"
#include "data/data_feed.h"
#include "ui/text_options.h"
#include "core/crash_reports.h"
@ -58,8 +59,8 @@ HistoryItem *createUnsupportedMessage(History *history, MsgId msgId, MTPDmessage
} // namespace
History::History(const PeerId &peerId)
: peer(App::peer(peerId))
, lastItemTextCache(st::dialogsTextWidthMin)
: Entry(this)
, peer(App::peer(peerId))
, cloudDraftTextCache(st::dialogsTextWidthMin)
, _mute(peer->isMuted())
, _sendActionText(st::dialogsTextWidthMin) {
@ -450,9 +451,14 @@ HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) {
}
if (item->date <= inviteDate) {
++itemIndex;
_joinedMessage = HistoryJoined::create(this, inviteDate, inviter, flags);
_joinedMessage = HistoryJoined::create(
this,
inviteDate,
inviter,
flags);
addNewInTheMiddle(_joinedMessage, blockIndex, itemIndex);
if (lastMsgDate.isNull() || inviteDate >= lastMsgDate) {
const auto lastDate = chatsListDate();
if (lastDate.isNull() || inviteDate >= lastDate) {
setLastMessage(_joinedMessage);
if (unread) {
newItemAdded(_joinedMessage);
@ -1855,6 +1861,16 @@ void History::getNextShowFrom(HistoryBlock *block, int i) {
showFrom = nullptr;
}
QDateTime History::adjustChatListDate() const {
const auto result = chatsListDate();
if (const auto draft = cloudDraft()) {
if (!Data::draftIsNull(draft) && draft->date > result) {
return draft->date;
}
}
return result;
}
void History::countScrollState(int top) {
countScrollTopItem(top);
if (scrollTopItem) {
@ -2039,6 +2055,36 @@ void History::recountGroupingAround(not_null<HistoryItem*> item) {
}
}
int History::chatListUnreadCount() const {
const auto result = unreadCount();
if (const auto from = peer->migrateFrom()) {
if (const auto migrated = App::historyLoaded(from)) {
return result + migrated->unreadCount();
}
}
return result;
}
bool History::chatListMutedBadge() const {
return mute();
}
HistoryItem *History::chatsListItem() const {
return lastMsg;
}
void History::loadUserpic() {
peer->loadUserpic();
}
void History::paintUserpic(
Painter &p,
int x,
int y,
int size) const {
peer->paintUserpic(p, x, y, size);
}
auto History::recountGroupingFromTill(not_null<HistoryItem*> item)
-> std::pair<not_null<HistoryItem*>, not_null<HistoryItem*>> {
const auto recountFromItem = [&] {
@ -2208,22 +2254,18 @@ uint32 _dialogsPosToTopShift = 0x80000000UL;
} // namespace
inline uint64 dialogPosFromDate(const QDateTime &date) {
if (date.isNull()) return 0;
return (uint64(date.toTime_t()) << 32) | (++_dialogsPosToTopShift);
}
inline uint64 pinnedDialogPos(int pinnedIndex) {
return 0xFFFFFFFF00000000ULL + pinnedIndex;
}
void History::setLastMessage(HistoryItem *msg) {
if (msg) {
if (!lastMsg) Local::removeSavedPeer(peer);
if (!lastMsg) {
Local::removeSavedPeer(peer);
}
lastMsg = msg;
if (const auto feed = peer->feed()) {
feed->updateLastMessage(msg);
}
setChatsListDate(msg->date);
} else {
lastMsg = 0;
} else if (lastMsg) {
lastMsg = nullptr;
updateChatListEntry();
}
}
@ -2235,43 +2277,10 @@ bool History::needUpdateInChatList() const {
return false;
} else if (isPinnedDialog()) {
return true;
} else if (const auto channel = peer->asChannel()) {
return !channel->feed() && channel->amIn();
}
return !peer->isChannel() || peer->asChannel()->amIn();
}
void History::setChatsListDate(const QDateTime &date) {
if (!lastMsgDate.isNull() && lastMsgDate >= date) {
if (!needUpdateInChatList() || !inChatList(Dialogs::Mode::All)) {
return;
}
}
lastMsgDate = date;
updateChatListSortPosition();
}
void History::updateChatListSortPosition() {
auto chatListDate = [this]() {
if (auto draft = cloudDraft()) {
if (!Data::draftIsNull(draft) && draft->date > lastMsgDate) {
return draft->date;
}
}
return lastMsgDate;
};
_sortKeyInChatList = isPinnedDialog()
? pinnedDialogPos(_pinnedIndex)
: dialogPosFromDate(chatListDate());
if (auto m = App::main()) {
if (needUpdateInChatList()) {
if (_sortKeyInChatList) {
m->createDialog(this);
updateChatListEntry();
} else {
m->deleteConversation(peer, false);
}
}
}
return true;
}
void History::fixLastMessage(bool wasAtBottom) {
@ -2406,6 +2415,10 @@ void History::clear(bool leaveItems) {
setUnreadCount(0);
if (auto channel = peer->asChannel()) {
channel->clearPinnedMessage();
if (const auto feed = channel->feed()) {
// Should be after setLastMessage(nullptr);
feed->historyCleared(this);
}
}
clearLastKeyboard();
}
@ -2481,84 +2494,24 @@ void History::clearOnDestroy() {
clearBlocks(false);
}
History::PositionInChatListChange History::adjustByPosInChatList(Dialogs::Mode list, Dialogs::IndexedList *indexed) {
Assert(indexed != nullptr);
Dialogs::Row *lnk = mainChatListLink(list);
int32 movedFrom = lnk->pos();
indexed->adjustByPos(chatListLinks(list));
int32 movedTo = lnk->pos();
return { movedFrom, movedTo };
}
int History::posInChatList(Dialogs::Mode list) const {
return mainChatListLink(list)->pos();
}
Dialogs::Row *History::addToChatList(Dialogs::Mode list, Dialogs::IndexedList *indexed) {
Assert(indexed != nullptr);
if (!inChatList(list)) {
chatListLinks(list) = indexed->addToEnd(this);
if (list == Dialogs::Mode::All && unreadCount()) {
App::histories().unreadIncrement(unreadCount(), mute());
Notify::unreadCounterUpdated();
}
}
return mainChatListLink(list);
}
void History::removeFromChatList(Dialogs::Mode list, Dialogs::IndexedList *indexed) {
Assert(indexed != nullptr);
if (inChatList(list)) {
indexed->del(this);
chatListLinks(list).clear();
if (list == Dialogs::Mode::All && unreadCount()) {
App::histories().unreadIncrement(-unreadCount(), mute());
Notify::unreadCounterUpdated();
}
void History::removeDialog() {
if (const auto main = App::main()) {
main->deleteConversation(peer, false);
}
}
void History::removeChatListEntryByLetter(Dialogs::Mode list, QChar letter) {
Assert(letter != 0);
if (inChatList(list)) {
chatListLinks(list).remove(letter);
void History::changedInChatListHook(Dialogs::Mode list, bool added) {
if (list == Dialogs::Mode::All && unreadCount()) {
const auto delta = added ? unreadCount() : -unreadCount();
App::histories().unreadIncrement(delta, mute());
Notify::unreadCounterUpdated();
}
}
void History::addChatListEntryByLetter(Dialogs::Mode list, QChar letter, Dialogs::Row *row) {
Assert(letter != 0);
if (inChatList(list)) {
chatListLinks(list).emplace(letter, row);
}
}
void History::updateChatListEntry() const {
if (auto main = App::main()) {
if (inChatList(Dialogs::Mode::All)) {
main->dlgUpdated(
Dialogs::Mode::All,
mainChatListLink(Dialogs::Mode::All));
if (inChatList(Dialogs::Mode::Important)) {
main->dlgUpdated(
Dialogs::Mode::Important,
mainChatListLink(Dialogs::Mode::Important));
}
}
}
}
void History::cachePinnedIndex(int pinnedIndex) {
if (_pinnedIndex != pinnedIndex) {
auto wasPinned = isPinnedDialog();
_pinnedIndex = pinnedIndex;
updateChatListSortPosition();
updateChatListEntry();
if (wasPinned != isPinnedDialog()) {
Notify::peerUpdatedDelayed(
peer,
Notify::PeerUpdate::Flag::PinnedChanged);
}
}
void History::changedChatListPinHook() {
Notify::peerUpdatedDelayed(
peer,
Notify::PeerUpdate::Flag::PinnedChanged);
}
void History::changeMsgId(MsgId oldId, MsgId newId) {

View File

@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_types.h"
#include "data/data_peer.h"
#include "dialogs/dialogs_common.h"
#include "dialogs/dialogs_entry.h"
#include "ui/effects/send_action_animations.h"
#include "base/observer.h"
#include "base/timer.h"
@ -162,7 +162,7 @@ class IndexedList;
} // namespace Dialogs
class ChannelHistory;
class History {
class History : public Dialogs::Entry {
public:
History(const PeerId &peerId);
History(const History &) = delete;
@ -244,32 +244,6 @@ public:
void setLastMessage(HistoryItem *msg);
void fixLastMessage(bool wasAtBottom);
bool needUpdateInChatList() const;
void updateChatListSortPosition();
void setChatsListDate(const QDateTime &date);
uint64 sortKeyInChatList() const {
return _sortKeyInChatList;
}
struct PositionInChatListChange {
int movedFrom;
int movedTo;
};
PositionInChatListChange adjustByPosInChatList(Dialogs::Mode list, Dialogs::IndexedList *indexed);
bool inChatList(Dialogs::Mode list) const {
return !chatListLinks(list).empty();
}
int posInChatList(Dialogs::Mode list) const;
Dialogs::Row *addToChatList(Dialogs::Mode list, Dialogs::IndexedList *indexed);
void removeFromChatList(Dialogs::Mode list, Dialogs::IndexedList *indexed);
void removeChatListEntryByLetter(Dialogs::Mode list, QChar letter);
void addChatListEntryByLetter(Dialogs::Mode list, QChar letter, Dialogs::Row *row);
void updateChatListEntry() const;
bool isPinnedDialog() const {
return (_pinnedIndex > 0);
}
void cachePinnedIndex(int newPinnedIndex);
MsgId minMsgId() const;
MsgId maxMsgId() const;
MsgId msgIdForRead() const;
@ -362,18 +336,17 @@ public:
bool newLoaded = true;
HistoryItem *lastMsg = nullptr;
HistoryItem *lastSentMsg = nullptr;
QDateTime lastMsgDate;
typedef QList<HistoryItem*> NotifyQueue;
NotifyQueue notifies;
Data::Draft *localDraft() {
Data::Draft *localDraft() const {
return _localDraft.get();
}
Data::Draft *cloudDraft() {
Data::Draft *cloudDraft() const {
return _cloudDraft.get();
}
Data::Draft *editDraft() {
Data::Draft *editDraft() const {
return _editDraft.get();
}
void setLocalDraft(std::unique_ptr<Data::Draft> &&draft);
@ -397,6 +370,20 @@ public:
void setForwardDraft(MessageIdsList &&items);
void recountGroupingAround(not_null<HistoryItem*> item);
bool needUpdateInChatList() const override;
bool toImportant() const override {
return !mute();
}
int chatListUnreadCount() const override;
bool chatListMutedBadge() const override;
HistoryItem *chatsListItem() const override;
void loadUserpic() override;
void paintUserpic(
Painter &p,
int x,
int y,
int size) const override;
// some fields below are a property of a currently displayed instance of this
// conversation history not a property of the conversation history itself
public:
@ -436,9 +423,6 @@ public:
mtpRequestId sendRequestId = 0;
mutable const HistoryItem *textCachedFor = nullptr; // cache
mutable Text lastItemTextCache;
void changeMsgId(MsgId oldId, MsgId newId);
Text cloudDraftTextCache;
@ -482,6 +466,11 @@ protected:
}
private:
QDateTime adjustChatListDate() const override;
void removeDialog() override;
void changedInChatListHook(Dialogs::Mode list, bool added) override;
void changedChatListPinHook() override;
// After adding a new history slice check the lastMsg and newLoaded.
void checkLastMsg();
@ -520,20 +509,6 @@ private:
base::optional<int> _unreadMentionsCount;
base::flat_set<MsgId> _unreadMentions;
Dialogs::RowsByLetter _chatListLinks[2];
Dialogs::RowsByLetter &chatListLinks(Dialogs::Mode list) {
return _chatListLinks[static_cast<int>(list)];
}
const Dialogs::RowsByLetter &chatListLinks(Dialogs::Mode list) const {
return _chatListLinks[static_cast<int>(list)];
}
Dialogs::Row *mainChatListLink(Dialogs::Mode list) const {
auto it = chatListLinks(list).find(0);
Assert(it != chatListLinks(list).cend());
return it->second;
}
uint64 _sortKeyInChatList = 0; // like ((unixtime) << 32) | (incremented counter)
// A pointer to the block that is currently being built.
// We hold this pointer so we can destroy it while building
// and then create a new one if it is necessary.

View File

@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_controller.h"
#include "core/crash_reports.h"
#include "data/data_session.h"
#include "data/data_feed.h"
namespace {
@ -164,7 +165,7 @@ ReplyKeyboard *HistoryItem::inlineReplyKeyboard() {
void HistoryItem::invalidateChatsListEntry() {
if (App::main()) {
// #TODO feeds dialogs
// #TODO feeds search results
App::main()->dlgUpdated(history(), id);
}
@ -172,6 +173,12 @@ void HistoryItem::invalidateChatsListEntry() {
if (history()->textCachedFor == this) {
history()->textCachedFor = nullptr;
}
if (const auto feed = history()->peer->feed()) {
if (feed->textCachedFor == this) {
feed->textCachedFor = nullptr;
feed->updateChatListEntry();
}
}
}
void HistoryItem::finishEditionToEmpty() {
@ -280,6 +287,7 @@ UserData *HistoryItem::viaBot() const {
}
void HistoryItem::destroy() {
const auto history = this->history();
if (isLogEntry()) {
Assert(detached());
} else {
@ -288,7 +296,7 @@ void HistoryItem::destroy() {
if (IsServerMsgId(id)) {
if (const auto types = sharedMediaTypes()) {
Auth().storage().remove(Storage::SharedMediaRemoveOne(
history()->peer->id,
history->peer->id,
types,
id));
}
@ -296,22 +304,30 @@ void HistoryItem::destroy() {
Auth().api().cancelLocalItem(this);
}
auto wasAtBottom = history()->loadedAtBottom();
_history->removeNotification(this);
const auto wasAtBottom = history->loadedAtBottom();
history->removeNotification(this);
detach();
if (const auto channel = history()->peer->asChannel()) {
if (history->lastMsg == this) {
history->fixLastMessage(wasAtBottom);
}
if (history->lastKeyboardId == id) {
history->clearLastKeyboard();
}
if ((!out() || isPost()) && unread() && history->unreadCount() > 0) {
history->setUnreadCount(history->unreadCount() - 1);
}
if (const auto channel = history->peer->asChannel()) {
if (channel->pinnedMessageId() == id) {
channel->clearPinnedMessage();
}
}
if (history()->lastMsg == this) {
history()->fixLastMessage(wasAtBottom);
}
if (history()->lastKeyboardId == id) {
history()->clearLastKeyboard();
}
if ((!out() || isPost()) && unread() && history()->unreadCount() > 0) {
history()->setUnreadCount(history()->unreadCount() - 1);
if (const auto feed = channel->feed()) {
// Must be after histroy->lastMsg is cleared.
// Otherwise feed last message will be this value again.
feed->messageRemoved(this);
// #TODO feeds unread
}
}
}
Global::RefPendingRepaintItems().remove(this);

View File

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_media_types.h"
#include "history/history_message.h"
#include "history/history_item_components.h"
#include "data/data_feed.h"
#include "auth_session.h"
#include "window/notifications_manager.h"
#include "storage/storage_shared_media.h"
@ -774,8 +775,14 @@ void HistoryService::updateDependentText() {
if (history()->textCachedFor == this) {
history()->textCachedFor = nullptr;
}
if (const auto feed = history()->peer->feed()) {
if (feed->textCachedFor == this) {
feed->textCachedFor = nullptr;
feed->updateChatListEntry();
}
}
if (App::main()) {
// #TODO feeds dialogs
// #TODO feeds search results
App::main()->dlgUpdated(history(), id);
}
App::historyUpdateDependent(this);

View File

@ -1091,8 +1091,8 @@ void MainWidget::deletedContact(UserData *user, const MTPcontacts_Link &result)
App::feedUserLink(MTP_int(peerToUser(user->id)), d.vmy_link, d.vforeign_link);
}
void MainWidget::removeDialog(not_null<History*> history) {
_dialogs->removeDialog(history);
void MainWidget::removeDialog(Dialogs::Key key) {
_dialogs->removeDialog(key);
}
void MainWidget::deleteConversation(PeerData *peer, bool deleteHistory) {
@ -1306,31 +1306,36 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu
if (!v || v->isEmpty()) {
if (peer->isChat() && !peer->asChat()->haveLeft()) {
auto h = App::historyLoaded(peer->id);
if (h) Local::addSavedPeer(peer, h->lastMsgDate);
} else if (peer->isChannel()) {
if (peer->asChannel()->inviter > 0 && peer->asChannel()->amIn()) {
if (auto from = App::userLoaded(peer->asChannel()->inviter)) {
auto h = App::history(peer->id);
h->clear(true);
h->addNewerSlice(QVector<MTPMessage>());
h->asChannelHistory()->insertJoinedMessage(true);
_history->peerMessagesUpdated(h->peer->id);
if (const auto history = App::historyLoaded(peer->id)) {
Local::addSavedPeer(peer, history->chatsListDate());
}
} else if (const auto channel = peer->asChannel()) {
if (channel->inviter > 0 && channel->amIn()) {
if (const auto from = App::userLoaded(channel->inviter)) {
const auto history = App::history(peer->id);
history->clear(true);
history->addNewerSlice(QVector<MTPMessage>());
history->asChannelHistory()->insertJoinedMessage(true);
_history->peerMessagesUpdated(peer->id);
}
}
} else {
deleteConversation(peer, false);
}
} else {
auto h = App::history(peer->id);
if (!h->lastMsg) {
h->addNewMessage((*v)[0], NewMessageLast);
const auto history = App::history(peer->id);
if (!history->lastMsg) {
history->addNewMessage((*v)[0], NewMessageLast);
}
if (!h->lastMsgDate.isNull() && h->loadedAtBottom()) {
if (peer->isChannel() && peer->asChannel()->inviter > 0 && h->lastMsgDate <= peer->asChannel()->inviteDate && peer->asChannel()->amIn()) {
if (auto from = App::userLoaded(peer->asChannel()->inviter)) {
h->asChannelHistory()->insertJoinedMessage(true);
_history->peerMessagesUpdated(h->peer->id);
if (!history->chatsListDate().isNull() && history->loadedAtBottom()) {
if (const auto channel = peer->asChannel()) {
if (channel->inviter > 0
&& history->chatsListDate() <= channel->inviteDate
&& channel->amIn()) {
if (const auto from = App::userLoaded(channel->inviter)) {
history->asChannelHistory()->insertJoinedMessage(true);
_history->peerMessagesUpdated(peer->id);
}
}
}
}
@ -2200,8 +2205,8 @@ bool MainWidget::viewsIncrementFail(const RPCError &error, mtpRequestId req) {
return false;
}
void MainWidget::createDialog(not_null<History*> history) {
_dialogs->createDialog(history);
void MainWidget::createDialog(Dialogs::Key key) {
_dialogs->createDialog(key);
}
void MainWidget::choosePeer(PeerId peerId, MsgId showAtMsgId) {

View File

@ -106,8 +106,8 @@ public:
void activate();
void createDialog(not_null<History*> history);
void removeDialog(not_null<History*> history);
void createDialog(Dialogs::Key key);
void removeDialog(Dialogs::Key key);
void dlgUpdated(Dialogs::Mode list, not_null<Dialogs::Row*> row);
void dlgUpdated(not_null<History*> history, MsgId msgId);

View File

@ -261,8 +261,8 @@ void Controller::showJumpToDate(not_null<PeerData*> peer, QDate requestedDate) {
return history->blocks.front()->items.front()->date.date();
}
}
} else if (!history->lastMsgDate.isNull()) {
return history->lastMsgDate.date();
} else if (!history->chatsListDate().isNull()) {
return history->chatsListDate().date();
}
}
return QDate::currentDate();
@ -272,8 +272,8 @@ void Controller::showJumpToDate(not_null<PeerData*> peer, QDate requestedDate) {
peer = channel;
}
if (auto history = App::historyLoaded(peer)) {
if (!history->lastMsgDate.isNull()) {
return history->lastMsgDate.date();
if (!history->chatsListDate().isNull()) {
return history->chatsListDate().date();
}
}
return QDate::currentDate();

View File

@ -25,18 +25,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_boxes.h"
#include "window/window_controller.h"
#include "data/data_session.h"
#include "data/data_feed.h"
#include "dialogs/dialogs_key.h"
namespace Window {
namespace {
void AddChatMembers(not_null<ChatData*> chat) {
if (chat->count >= Global::ChatSizeMax() && chat->amCreator()) {
Ui::show(Box<ConvertToSupergroupBox>(chat));
} else {
AddParticipantsBoxController::Start(chat);
}
}
class Filler {
public:
Filler(
@ -64,6 +58,25 @@ private:
};
class FeedFiller {
public:
FeedFiller(
not_null<Controller*> controller,
not_null<Data::Feed*> feed,
const PeerMenuCallback &addAction,
PeerMenuSource source);
void fill();
private:
void addPinToggle();
not_null<Controller*> _controller;
not_null<Data::Feed*> _feed;
const PeerMenuCallback &_addAction;
PeerMenuSource _source;
};
History *FindWastedPin() {
const auto &order = Auth().data().pinnedDialogsOrder();
for (const auto pinned : order) {
@ -78,6 +91,58 @@ History *FindWastedPin() {
return nullptr;
}
void AddChatMembers(not_null<ChatData*> chat) {
if (chat->count >= Global::ChatSizeMax() && chat->amCreator()) {
Ui::show(Box<ConvertToSupergroupBox>(chat));
} else {
AddParticipantsBoxController::Start(chat);
}
}
bool PinnedLimitReached(Dialogs::Key key) {
const auto pinnedCount = Auth().data().pinnedDialogsCount();
const auto pinnedMax = Global::PinnedDialogsCountMax();
if (pinnedCount < pinnedMax) {
return false;
}
// Some old chat, that was converted, maybe is still pinned.
if (auto wasted = FindWastedPin()) {
Auth().data().setPinnedDialog(wasted, false);
Auth().data().setPinnedDialog(key, true);
Auth().api().savePinnedOrder();
} else {
auto errorText = lng_error_pinned_max(
lt_count,
pinnedMax);
Ui::show(Box<InformBox>(errorText));
}
return true;
}
void TogglePinnedDialog(Dialogs::Key key) {
const auto isPinned = !key.entry()->isPinnedDialog();
if (isPinned && PinnedLimitReached(key)) {
return;
}
Auth().data().setPinnedDialog(key, isPinned);
auto flags = MTPmessages_ToggleDialogPin::Flags(0);
if (isPinned) {
flags |= MTPmessages_ToggleDialogPin::Flag::f_pinned;
}
MTP::send(MTPmessages_ToggleDialogPin(
MTP_flags(flags),
key.history()
? MTP_inputDialogPeer(key.history()->peer->input)
: MTP_inputDialogPeerFeed(MTP_int(key.feed()->id()))));
if (isPinned) {
if (const auto main = App::main()) {
main->dialogsToUp();
}
}
}
Filler::Filler(
not_null<Controller*> controller,
not_null<PeerData*> peer,
@ -115,40 +180,8 @@ void Filler::addPinToggle() {
? lng_context_unpin_from_top
: lng_context_pin_to_top);
};
auto pinToggle = [peer] {
auto history = App::history(peer);
auto isPinned = !history->isPinnedDialog();
const auto pinnedCount = Auth().data().pinnedDialogsCount();
const auto pinnedMax = Global::PinnedDialogsCountMax();
if (isPinned && pinnedCount >= pinnedMax) {
// Some old chat, that was converted to supergroup, maybe is still pinned.
if (auto wasted = FindWastedPin()) {
Auth().data().setPinnedDialog(wasted, false);
Auth().data().setPinnedDialog(history, true);
Auth().api().savePinnedOrder();
} else {
auto errorText = lng_error_pinned_max(
lt_count,
pinnedMax);
Ui::show(Box<InformBox>(errorText));
}
return;
}
Auth().data().setPinnedDialog(history, isPinned);
auto flags = MTPmessages_ToggleDialogPin::Flags(0);
if (isPinned) {
flags |= MTPmessages_ToggleDialogPin::Flag::f_pinned;
}
MTP::send(
MTPmessages_ToggleDialogPin(
MTP_flags(flags),
MTP_inputDialogPeer(peer->input)));
if (isPinned) {
if (auto main = App::main()) {
main->dialogsToUp();
}
}
auto pinToggle = [=] {
TogglePinnedDialog(App::history(peer));
};
auto pinAction = _addAction(pinText(isPinned), pinToggle);
@ -382,6 +415,37 @@ void Filler::fill() {
}
}
FeedFiller::FeedFiller(
not_null<Controller*> controller,
not_null<Data::Feed*> feed,
const PeerMenuCallback &addAction,
PeerMenuSource source)
: _controller(controller)
, _feed(feed)
, _addAction(addAction)
, _source(source) {
}
void FeedFiller::fill() {
if (_source == PeerMenuSource::ChatsList) {
addPinToggle();
}
}
void FeedFiller::addPinToggle() {
auto feed = _feed;
auto isPinned = feed->isPinnedDialog();
auto pinText = [](bool isPinned) {
return lang(isPinned
? lng_context_unpin_from_top
: lng_context_pin_to_top);
};
auto pinToggle = [=] {
TogglePinnedDialog(feed);
};
_addAction(pinText(isPinned), pinToggle);
}
} // namespace
void PeerMenuDeleteContact(not_null<UserData*> user) {
@ -599,4 +663,14 @@ void FillPeerMenu(
filler.fill();
}
void FillFeedMenu(
not_null<Controller*> controller,
not_null<Data::Feed*> feed,
const PeerMenuCallback &callback,
PeerMenuSource source) {
// TODO feeds context menu
FeedFiller filler(controller, feed, callback, source);
filler.fill();
}
} // namespace Window

View File

@ -11,6 +11,10 @@ namespace Ui {
class RpWidget;
} // namespace Ui
namespace Data {
class Feed;
} // namespace Data
namespace Window {
class Controller;
@ -30,6 +34,11 @@ void FillPeerMenu(
not_null<PeerData*> peer,
const PeerMenuCallback &addAction,
PeerMenuSource source);
void FillFeedMenu(
not_null<Controller*> controller,
not_null<Data::Feed*> feed,
const PeerMenuCallback &addAction,
PeerMenuSource source);
void PeerMenuDeleteContact(not_null<UserData*> user);
void PeerMenuShareContactBox(not_null<UserData*> user);

View File

@ -193,7 +193,8 @@
<(src_loc)/data/data_user_photos.h
<(src_loc)/data/data_web_page.cpp
<(src_loc)/data/data_web_page.h
<(src_loc)/dialogs/dialogs_common.h
<(src_loc)/dialogs/dialogs_entry.cpp
<(src_loc)/dialogs/dialogs_entry.h
<(src_loc)/dialogs/dialogs_indexed_list.cpp
<(src_loc)/dialogs/dialogs_indexed_list.h
<(src_loc)/dialogs/dialogs_inner_widget.cpp