mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-20 22:41:11 +00:00
Support basic feed display in chats list.
This commit is contained in:
parent
9d2239291d
commit
782e70b171
@ -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
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
166
Telegram/SourceFiles/dialogs/dialogs_entry.cpp
Normal file
166
Telegram/SourceFiles/dialogs/dialogs_entry.cpp
Normal 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
|
131
Telegram/SourceFiles/dialogs/dialogs_entry.h
Normal file
131
Telegram/SourceFiles/dialogs/dialogs_entry.h
Normal 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
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -186,17 +186,24 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, 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 ®ion, 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 ®ion, 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 ®ion, 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user